Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 26 additions & 7 deletions LoopStructural/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,21 @@
loggers = {}
@dataclass
class LoopStructuralConfig:
"""
Configuration for LoopStructural
"""Configuration for LoopStructural package.

This dataclass holds configuration parameters for the LoopStructural
geological modelling package.

Parameters
----------
nelements : int, optional
The default number of elements to use in interpolation, by default 10_000

Examples
--------
>>> config = LoopStructuralConfig(nelements=50000)
>>> config.nelements
50000
"""

nelements: int = 10_000
Expand All @@ -42,15 +55,21 @@ class LoopStructuralConfig:


def setLogging(level="info", handler=None):
"""
Set the logging parameters for log file or custom handler
"""Set the logging parameters for log file or custom handler.

Parameters
----------
level : str
'info', 'warning', 'error', 'debug'
level : str, optional
Logging level to set, by default "info"
Valid options: 'info', 'warning', 'error', 'debug'
handler : logging.Handler, optional
A logging handler to use instead of the default StreamHandler
A logging handler to use instead of the default StreamHandler, by default None

Examples
--------
>>> import LoopStructural
>>> LoopStructural.setLogging('debug')
>>> LoopStructural.setLogging('info', logging.FileHandler('loop.log'))
"""
import LoopStructural

Expand Down
87 changes: 87 additions & 0 deletions LoopStructural/datatypes/_bounding_box.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,10 +82,24 @@ def __init__(

@property
def global_origin(self):
"""Get the global origin of the bounding box.

Returns
-------
np.ndarray
The global origin coordinates
"""
return self._global_origin

@global_origin.setter
def global_origin(self, global_origin):
"""Set the global origin of the bounding box.

Parameters
----------
global_origin : array_like
The global origin coordinates
"""
if self.dimensions != len(global_origin):
logger.warning(
f"Global origin has {len(global_origin)} dimensions but bounding box has {self.dimensions}"
Expand All @@ -94,20 +108,53 @@ def global_origin(self, global_origin):

@property
def global_maximum(self):
"""Get the global maximum coordinates of the bounding box.

Returns
-------
np.ndarray
The global maximum coordinates (local maximum + global origin)
"""
return self.maximum + self.global_origin

@property
def valid(self):
"""Check if the bounding box has valid origin and maximum values.

Returns
-------
bool
True if both origin and maximum are set, False otherwise
"""
return self._origin is not None and self._maximum is not None

@property
def origin(self) -> np.ndarray:
"""Get the origin coordinates of the bounding box.

Returns
-------
np.ndarray
Origin coordinates

Raises
------
LoopValueError
If the origin is not set
"""
if self._origin is None:
raise LoopValueError("Origin is not set")
return self._origin

@origin.setter
def origin(self, origin: np.ndarray):
"""Set the origin coordinates of the bounding box.

Parameters
----------
origin : np.ndarray
Origin coordinates
"""
if self.dimensions != len(origin):
logger.warning(
f"Origin has {len(origin)} dimensions but bounding box has {self.dimensions}"
Expand All @@ -116,24 +163,64 @@ def origin(self, origin: np.ndarray):

@property
def maximum(self) -> np.ndarray:
"""Get the maximum coordinates of the bounding box.

Returns
-------
np.ndarray
Maximum coordinates

Raises
------
LoopValueError
If the maximum is not set
"""
if self._maximum is None:
raise LoopValueError("Maximum is not set")
return self._maximum

@maximum.setter
def maximum(self, maximum: np.ndarray):
"""Set the maximum coordinates of the bounding box.

Parameters
----------
maximum : np.ndarray
Maximum coordinates
"""
self._maximum = maximum

@property
def nelements(self):
"""Get the total number of elements in the bounding box.

Returns
-------
int
Total number of elements (product of nsteps)
"""
return self.nsteps.prod()

@property
def volume(self):
"""Calculate the volume of the bounding box.

Returns
-------
float
Volume of the bounding box
"""
return np.prod(self.maximum - self.origin)

@property
def bb(self):
"""Get a numpy array containing origin and maximum coordinates.

Returns
-------
np.ndarray
Array with shape (2, n_dimensions) containing [origin, maximum]
"""
return np.array([self.origin, self.maximum])

@nelements.setter
Expand Down
53 changes: 53 additions & 0 deletions LoopStructural/datatypes/_structured_grid.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,26 @@

@dataclass
class StructuredGrid:
"""A structured grid for storing 3D geological data.

This class represents a regular 3D grid with properties and cell properties
that can be used for geological modelling and visualisation.

Parameters
----------
origin : np.ndarray, optional
Origin point of the grid, by default [0, 0, 0]
step_vector : np.ndarray, optional
Step size in each direction, by default [1, 1, 1]
nsteps : np.ndarray, optional
Number of steps in each direction, by default [10, 10, 10]
cell_properties : Dict[str, np.ndarray], optional
Properties defined at cell centres, by default empty dict
properties : Dict[str, np.ndarray], optional
Properties defined at grid nodes, by default empty dict
name : str, optional
Name of the grid, by default "default_grid"
"""
origin: np.ndarray = field(default_factory=lambda: np.array([0, 0, 0]))
step_vector: np.ndarray = field(default_factory=lambda: np.array([1, 1, 1]))
nsteps: np.ndarray = field(default_factory=lambda: np.array([10, 10, 10]))
Expand All @@ -16,6 +36,13 @@ class StructuredGrid:
name: str = "default_grid"

def to_dict(self):
"""Convert the structured grid to a dictionary representation.

Returns
-------
dict
Dictionary containing all grid properties and metadata
"""
return {
"origin": self.origin,
"maximum": self.maximum,
Expand All @@ -28,9 +55,28 @@ def to_dict(self):

@property
def maximum(self):
"""Calculate the maximum coordinates of the grid.

Returns
-------
np.ndarray
Maximum coordinates (origin + nsteps * step_vector)
"""
return self.origin + self.nsteps * self.step_vector

def vtk(self):
"""Convert the structured grid to a PyVista RectilinearGrid.

Returns
-------
pv.RectilinearGrid
PyVista grid object with all properties attached

Raises
------
ImportError
If PyVista is not installed
"""
try:
import pyvista as pv
except ImportError:
Expand Down Expand Up @@ -65,6 +111,13 @@ def plot(self, pyvista_kwargs={}):

@property
def cell_centres(self):
"""Calculate the coordinates of cell centres.

Returns
-------
tuple of np.ndarray
X, Y, Z coordinates of all cell centres
"""
x = np.linspace(
self.origin[0] + self.step_vector[0] * 0.5,
self.maximum[0] + self.step_vector[0] * 0.5,
Expand Down
6 changes: 4 additions & 2 deletions LoopStructural/interpolators/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
"""
Interpolators and interpolation supports
"""Interpolators and interpolation supports for LoopStructural.

This module provides various interpolation methods and support structures
for geological modelling, including finite difference, piecewise linear,
and radial basis function interpolators.
"""


Expand Down
Loading