Source code for cratermaker.components.surface.icosphere

import math
from pathlib import Path
from typing import Any

from numpy.typing import NDArray

from cratermaker.components.surface import Surface
from cratermaker.components.target import Target
from cratermaker.utils.general_utils import format_large_units, parameter


[docs] @Surface.register("icosphere") class IcosphereSurface(Surface): """ Create a uniform grid configuration using an icosphere. This is the most accurate and efficient way to create a uniform grid, but is limited to a few resolutions. Parameters ---------- gridlevel : float, default 8 The subdivision level of the icosphere. target : Target, optional The target body or name of a known target body for the impact simulation. reset : bool, optional Flag to indicate whether to reset the surface. Default is True. regrid : bool, optional Flag to indicate whether to regrid the surface. Default is False. simdir : str | Path |simdir| Returns ------- IcosphereSurface An instance of the IcosphereSurface class initialized with the given pixel size. Notes ----- The number of faces of the icosphere is given by the formula: .. math:: n_{face} = 10 * 4^{gridlevel} + 2 The number of nodes is given by the formula: .. math:: n_{node} = 20 * 4^{gridlevel} """ def __init__( self, gridlevel: int = 8, target: Target | str | None = None, reset: bool = True, regrid: bool = False, simdir: str | Path | None = None, **kwargs: Any, ): super().__init__(target=target, simdir=simdir, **kwargs) self.gridlevel = gridlevel self._load_from_files(reset=reset, regrid=regrid, **kwargs) def __str__(self) -> str: str_repr = super().__str__() pix_mean = format_large_units(self.pix_mean, quantity="length") pix_std = format_large_units(self.pix_std, quantity="length") str_repr += f"Grid Level: {self.gridlevel}\n" str_repr += f"Effective pixel size: {pix_mean} +/- {pix_std}\n" return str_repr @property def _hashvars(self): """ The variables used to generate the hash. """ return super()._hashvars + [self.gridlevel] def _generate_face_distribution(self, **kwargs: Any) -> NDArray: """ Creates the points that define the mesh centers. Returns ------- (3,n) ndarray of np.float64 Array of points on a unit sphere. """ from trimesh.creation import icosphere print(f"Generating a mesh with icosphere level {self.gridlevel}.") mesh = icosphere(self.gridlevel) points = mesh.vertices.T return points @parameter def gridlevel(self) -> int: """The subdivision level of the icosphere.""" return self._gridlevel @gridlevel.setter def gridlevel(self, value: int) -> None: if value < 0: raise ValueError("The value of gridlevel must be a non-negative integer.") self._gridlevel = int(value) @property def pix(self) -> float: """The approximate face size for a cell of the mesh in meters.""" if self._pix is None: nfaces = 10 * 4**self.gridlevel + 2 self._pix = (4 * math.pi * self.radius**2 / nfaces) ** 0.5 return self._pix