Source code for gempy.core.data.geo_model

import pprint
import warnings
from dataclasses import dataclass, field
from typing import Sequence, Optional

import numpy as np

import gempy_engine.core.data.engine_grid
from gempy_engine.core.data import Solutions
from gempy_engine.core.data.geophysics_input import GeophysicsInput
from gempy_engine.core.data.raw_arrays_solution import RawArraysSolution
from gempy_engine.core.data import InterpolationOptions
from gempy_engine.core.data.input_data_descriptor import InputDataDescriptor
from gempy_engine.core.data.interpolation_input import InterpolationInput
from .orientations import OrientationsTable
from .structural_frame import StructuralFrame
from gempy_engine.core.data.transforms import Transform, GlobalAnisotropy
from .grid import Grid

"""
TODO:
    - [ ] StructuralFrame will all input points chunked on Elements. Here I will need a property to put all
    together to feed to InterpolationInput

"""


@dataclass
class GeoModelMeta:
    """
    Container for metadata associated with a GeoModel.

    Attributes:
        name (str): Name of the geological model.
        creation_date (str): Date of creation of the model.
        last_modification_date (str): Last modification date of the model.
        owner (str): Owner of the geological model.
    """

    name: str
    creation_date: str
    last_modification_date: str
    owner: str


[docs] @dataclass(init=False) class GeoModel: """ Class representing a geological model. """ meta: GeoModelMeta #: Meta-information about the geological model, like its name, creation and modification dates, and owner. structural_frame: StructuralFrame #: The structural information of the geological model. grid: Grid #: The general grid used in the geological model. # region GemPy engine data types _interpolation_options: InterpolationOptions #: The interpolation options provided by the user. geophysics_input: GeophysicsInput = None #: The geophysics input of the geological model. transform: Transform = None #: The transformation used in the geological model for input points. interpolation_grid: gempy_engine.core.data.engine_grid.EngineGrid = None #: Optional grid used for interpolation. Can be seen as a cache field. _interpolationInput: InterpolationInput = None #: Input data for interpolation. Fed by the structural frame and can be seen as a cache field. _input_data_descriptor: InputDataDescriptor = None #: Descriptor of the input data. Fed by the structural frame and can be seen as a cache field. # endregion _solutions: gempy_engine.core.data.solutions.Solutions = field(init=False, default=None) #: The computed solutions of the geological model. legacy_model: "gpl.Project" = None #: Legacy model (if available). Allows for backward compatibility.
[docs] def __init__(self, name: str, structural_frame: StructuralFrame, grid: Grid, interpolation_options: InterpolationOptions): # TODO: Fill the arguments properly self.meta = GeoModelMeta( name=name, creation_date=None, last_modification_date=None, owner=None ) self.structural_frame = structural_frame # ? This could be Optional self.grid = grid self._interpolation_options = interpolation_options self.transform = Transform.from_input_points( surface_points=self.surface_points_copy, orientations=self.orientations_copy )
def __repr__(self): # TODO: Improve this return pprint.pformat(self.__dict__) def update_transform(self, auto_anisotropy: GlobalAnisotropy = GlobalAnisotropy.NONE, anisotropy_limit: Optional[np.ndarray] = None): self.transform = Transform.from_input_points( surface_points=self.surface_points_copy, orientations=self.orientations_copy ) self.transform.apply_anisotropy(anisotropy_type=auto_anisotropy, anisotropy_limit=anisotropy_limit) @property def interpolation_options(self) -> InterpolationOptions: n_octree_lvl = self._interpolation_options._number_octree_levels # * we access the private one because we do not care abot the extract mesh octree level octrees_set: bool = n_octree_lvl > 1 resolution_set = bool(self.grid.active_grids_bool[0]) # 0 corresponds # Create a tuple representing the conditions match (octrees_set, resolution_set): case (True, False): self._interpolation_options.block_solutions_type = RawArraysSolution.BlockSolutionType.OCTREE case (True, True): warnings.warn("Both octree levels and resolution are set. The default grid for the `raw_array_solution`" "and plots will be the dense regular grid. To use octrees instead, set resolution to None in the " "regular grid.") self._interpolation_options.block_solutions_type = RawArraysSolution.BlockSolutionType.DENSE_GRID case (False, True): self._interpolation_options.block_solutions_type = RawArraysSolution.BlockSolutionType.DENSE_GRID case (False, False): raise ValueError("The resolution of the grid is not set. Please set the resolution of the grid or " "the number of octree levels in InterpolationOptions.number_octree_levels.") self._interpolation_options._model_name = self.meta.name return self._interpolation_options @interpolation_options.setter def interpolation_options(self, value): self._interpolation_options = value @property def solutions(self) -> Solutions: return self._solutions @solutions.setter def solutions(self, value): self._solutions = value # * Set solutions per group if self._solutions.raw_arrays is not None: for e, group in enumerate(self.structural_frame.structural_groups): group.kriging_solution = RawArraysSolution( # ? Maybe I need to add more fields, but I am not sure yet scalar_field_matrix=self._solutions.raw_arrays.scalar_field_matrix[e], block_matrix=self._solutions.raw_arrays.block_matrix[e], ) # * Set solutions per element for e, element in enumerate(self.structural_frame.structural_elements[:-1]): # * Ignore basement dc_mesh = self._solutions.dc_meshes[e] if self._solutions.dc_meshes is not None else None # TODO: These meshes are in the order of the scalar field element.vertices = (self.transform.apply_inverse(dc_mesh.vertices) if dc_mesh is not None else None) element.edges = (dc_mesh.edges if dc_mesh is not None else None) # * Reordering the elements according to the scalar field for e, order_per_structural_group in enumerate(self._solutions._ordered_elements): elements = self.structural_frame.structural_groups[e].elements reordered_elements = [elements[i] for i in order_per_structural_group] self.structural_frame.structural_groups[e].elements = reordered_elements @property def surface_points_copy(self): """This is a copy! Returns a SurfacePointsTable for all surface points across the structural elements""" surface_points_table = self.structural_frame.surface_points if self.transform is not None: surface_points_table.model_transform = self.transform return surface_points_table @property def surface_points(self): raise AttributeError("This property can only be set, not read. You can access the copy with `surface_points_copy` or" "the original on the individual structural elements.") @surface_points.setter def surface_points(self, value): self.structural_frame.surface_points = value @property def orientations_copy(self) -> OrientationsTable: """This is a copy! Returns a OrientationsTable for all orientations across the structural elements""" orientations_table = self.structural_frame.orientations if self.transform is not None: orientations_table.model_transform = self.transform return orientations_table @property def orientations(self) -> OrientationsTable: raise AttributeError("This property can only be set, not read. You can access the copy with `orientations_copy` or" "the original on the individual structural elements.") @orientations.setter def orientations(self, value): self.structural_frame.orientations = value @property def interpolation_input_copy(self): if self.structural_frame.is_dirty is False: return self._interpolationInput # self.grid.regular_grid.set_regular_grid( # extent=self.grid.regular_grid.extent, # resolution=octree_leaf_resolution # ) self._interpolationInput = InterpolationInput.from_structural_frame( structural_frame=self.structural_frame, grid=self.grid, transform=self.transform ) return self._interpolationInput @property def input_data_descriptor(self) -> InputDataDescriptor: # TODO: This should have the exact same dirty logic as interpolation_input return self.structural_frame.input_data_descriptor def add_surface_points(self, X: Sequence[float], Y: Sequence[float], Z: Sequence[float], surface: Sequence[str], nugget: Optional[Sequence[float]] = None) -> None: raise NotImplementedError("This method is deprecated. Use `gp.add_surface_points` instead")