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.grid
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. transform: Transform = None #: The transformation used in the geological model for input points. interpolation_grid: gempy_engine.core.data.grid.Grid = 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, orientations=self.orientations )
def __repr__(self): # TODO: Improve this return pprint.pformat(self.__dict__) def update_transform(self, auto_anisotropy: GlobalAnisotropy = GlobalAnisotropy.CUBE, anisotropy_limit: Optional[np.ndarray] = None): self.transform = Transform.from_input_points( surface_points=self.surface_points, orientations=self.orientations ) self.transform.apply_anisotropy(anisotropy_type=auto_anisotropy, anisotropy_limit=anisotropy_limit) @property def solutions(self): return self._solutions @solutions.setter def solutions(self, value): self._solutions = value for e, group in enumerate(self.structural_frame.structural_groups): group.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], ) 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) @property def surface_points(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 @surface_points.setter def surface_points(self, value): self.structural_frame.surface_points = value @property def orientations(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 @orientations.setter def orientations(self, value): self.structural_frame.orientations = value @property def interpolation_input(self): if self.structural_frame.is_dirty is False: return self._interpolationInput n_octree_lvl = self.interpolation_options.number_octree_levels compute_octrees: bool = n_octree_lvl > 1 # * Set regular grid to the octree resolution. ? Probably a better way to do this would be to make regular_grid resolution a property if compute_octrees: octree_leaf_resolution = np.array([2 ** n_octree_lvl] * 3) resolution_not_set = self.grid.regular_grid.resolution is not None resolution_is_octree_resolution = np.allclose(self.grid.regular_grid.resolution, octree_leaf_resolution) if resolution_not_set and not resolution_is_octree_resolution: warnings.warn( message="You are using octrees and passing a regular grid. The resolution of the regular grid will be overwritten", category=UserWarning ) 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, octrees=compute_octrees ) 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")