pycsamt.ai.geology.layers#
Correlated stratigraphic interfaces and layered resistivity priors.
Layer interfaces are stored as depth below the grid datum and have shape
(n_interface, nx) in 2-D or (n_interface, ny, nx) in 3-D. Resulting
resistivity arrays follow the geology convention (nz, nx) or
(nz, ny, nx).
Functions
|
Generate a correlated 2-D section or 3-D layered electrical volume. |
Classes
|
Define the electrical distribution of one stratigraphic unit. |
|
Immutable discretized layered electrical geology. |
- class pycsamt.ai.geology.layers.ElectricalLayer(name, resistivity_ohm_m, log10_std=0.0, heterogeneity=None, resistivity_bounds_ohm_m=None)[source]
Bases:
objectDefine the electrical distribution of one stratigraphic unit.
- Parameters:
name (str) – Unique non-empty unit name.
resistivity_ohm_m (float) – Positive median resistivity in ohm metres.
log10_std (float, default=0.0) – Non-negative standard deviation of within-unit log10 resistivity.
heterogeneity (GaussianCorrelation or None, optional) – Spatial correlation model required when
log10_stdis positive.resistivity_bounds_ohm_m (tuple of float or None, optional) – Positive inclusive lower/upper clipping bounds.
Examples
A homogeneous conductive cover:
>>> cover = ElectricalLayer("conductive cover", 10.0) >>> cover.log10_resistivity 1.0
A heterogeneous basement:
>>> basement = ElectricalLayer( ... "basement", ... 1000.0, ... log10_std=0.2, ... heterogeneity=GaussianCorrelation(1000, 200), ... resistivity_bounds_ohm_m=(100, 5000), ... )
- name: str
- resistivity_ohm_m: float
- log10_std: float = 0.0
- heterogeneity: GaussianCorrelation | None = None
- property log10_resistivity: float[source]
Return the median resistivity in log10 ohm metres.
- Returns:
log10(resistivity_ohm_m).- Return type:
Examples
>>> ElectricalLayer("unit", 100).log10_resistivity 2.0
- to_dict()[source]
Return a JSON-serializable unit definition.
- Returns:
Electrical distribution and optional correlation state.
- Return type:
Examples
>>> ElectricalLayer("unit", 100).to_dict()["resistivity_ohm_m"] 100.0
- classmethod from_dict(data)[source]
Restore a validated electrical layer.
- Parameters:
data (mapping) – State returned by
to_dict().- Returns:
Immutable unit definition.
- Return type:
Examples
>>> unit = ElectricalLayer("unit", 50) >>> ElectricalLayer.from_dict(unit.to_dict()) == unit True
- class pycsamt.ai.geology.layers.LayeredGeology(grid, layers, interface_depth_m, layer_index, resistivity_ohm_m, seed, boundary, interface_policy, minimum_thickness_m, adjusted_interface_fraction=0.0, generation_config=<factory>)[source]
Bases:
objectImmutable discretized layered electrical geology.
- Parameters:
grid (GeologyGrid) – Target model grid.
layers (sequence of ElectricalLayer) – Units ordered shallowest to deepest.
interface_depth_m (ndarray) – Interface surfaces shaped
(n_layer - 1, *horizontal_shape).layer_index (ndarray of int) – Zero-based unit index for every model cell, shaped like
grid.resistivity_ohm_m (ndarray) – Positive cell resistivities shaped like
grid.seed (int) – Root generation seed.
boundary (str) – Recorded generation policies.
interface_policy (str) – Recorded generation policies.
minimum_thickness_m (float) – Enforced minimum vertical separation.
adjusted_interface_fraction (float) – Fraction of interface samples changed by projection.
generation_config (mapping, optional) – Finite JSON-compatible requested interface means, relief amplitudes, and correlation models needed to regenerate the surfaces.
Examples
>>> grid = GeologyGrid.regular_2d(nx=8, nz=6, dx_m=100, dz_m=50) >>> layers = [ ... ElectricalLayer("cover", 10), ... ElectricalLayer("basement", 1000), ... ] >>> model = generate_layered_geology(grid, layers, [150], seed=1) >>> model.resistivity_ohm_m.shape (6, 8) >>> model.n_layers 2
- grid: GeologyGrid
- layers: tuple[ElectricalLayer, ...]
- interface_depth_m: ndarray
- layer_index: ndarray
- resistivity_ohm_m: ndarray
- seed: int
- boundary: str
- interface_policy: str
- minimum_thickness_m: float
- adjusted_interface_fraction: float = 0.0
- property n_layers: int[source]
Return the number of stratigraphic units.
- Returns:
Number of electrical layers.
- Return type:
Examples
>>> grid = GeologyGrid.regular_2d(nx=2, nz=2, dx_m=1, dz_m=1) >>> model = generate_layered_geology( ... grid, ... [ElectricalLayer("a", 1), ElectricalLayer("b", 2)], ... [1], ... seed=0, ... ) >>> model.n_layers 2
- property model_hash: str[source]
Return a platform-stable digest of model values and provenance.
- Returns:
SHA-256 digest.
- Return type:
Examples
>>> grid = GeologyGrid.regular_2d(nx=2, nz=2, dx_m=1, dz_m=1) >>> model = generate_layered_geology( ... grid, ... [ElectricalLayer("a", 1), ElectricalLayer("b", 2)], ... [1], ... seed=0, ... ) >>> len(model.model_hash) 64
- layer_mask(layer)[source]
Return a read-only Boolean mask for one unit.
- Parameters:
- Returns:
Mask shaped like the geological grid.
- Return type:
ndarray of bool
- Raises:
KeyError – If a named unit is unavailable.
IndexError – If a numeric index is outside the layer range.
Examples
>>> grid = GeologyGrid.regular_2d(nx=2, nz=2, dx_m=1, dz_m=1) >>> model = generate_layered_geology( ... grid, ... [ElectricalLayer("a", 1), ElectricalLayer("b", 2)], ... [1], ... seed=0, ... ) >>> model.layer_mask("a").shape (2, 2)
- interface(index)[source]
Return one read-only interface-depth surface.
- Parameters:
index (int) – Zero-based interface between layers
indexandindex + 1.- Returns:
Shape
(nx,)in 2-D or(ny, nx)in 3-D.- Return type:
ndarray
Examples
>>> grid = GeologyGrid.regular_2d(nx=3, nz=2, dx_m=1, dz_m=1) >>> model = generate_layered_geology( ... grid, ... [ElectricalLayer("a", 1), ElectricalLayer("b", 2)], ... [1], ... seed=0, ... ) >>> model.interface(0).shape (3,)
- summary()[source]
Return compact JSON-compatible geological diagnostics.
- Returns:
Shape, unit fractions, interface ranges, resistivity range, and interface-adjustment fraction.
- Return type:
Examples
>>> grid = GeologyGrid.regular_2d(nx=2, nz=2, dx_m=1, dz_m=1) >>> model = generate_layered_geology( ... grid, ... [ElectricalLayer("a", 1), ElectricalLayer("b", 2)], ... [1], ... seed=0, ... ) >>> model.summary()["n_layers"] 2
- provenance()[source]
Return generation provenance without cell arrays.
- Returns:
Grid, layer definitions, seed, and interface policies.
- Return type:
Examples
>>> grid = GeologyGrid.regular_2d(nx=2, nz=2, dx_m=1, dz_m=1) >>> model = generate_layered_geology( ... grid, ... [ElectricalLayer("a", 1), ElectricalLayer("b", 2)], ... [1], ... seed=0, ... ) >>> model.provenance()["seed"] 0
- to_npz(path)[source]
Persist the layered model in a pickle-free compressed archive.
- Parameters:
path (str or pathlib.Path) – Destination NPZ path.
- Returns:
Requested destination.
- Return type:
Examples
>>> from tempfile import TemporaryDirectory >>> grid = GeologyGrid.regular_2d(nx=2, nz=2, dx_m=1, dz_m=1) >>> model = generate_layered_geology( ... grid, ... [ElectricalLayer("a", 1), ElectricalLayer("b", 2)], ... [1], ... seed=0, ... ) >>> with TemporaryDirectory() as directory: ... path = model.to_npz(Path(directory) / "layers.npz") ... restored = LayeredGeology.from_npz(path) >>> restored.model_hash == model.model_hash True
- classmethod from_npz(path)[source]
Load and validate a layered model without enabling pickle.
- Parameters:
path (str or pathlib.Path) – Archive written by
to_npz().- Returns:
Immutable restored model.
- Return type:
Examples
>>> from tempfile import TemporaryDirectory >>> model = generate_layered_geology( ... GeologyGrid.regular_2d(nx=2, nz=2, dx_m=1, dz_m=1), ... [ElectricalLayer("a", 1), ElectricalLayer("b", 2)], ... [1], ... seed=0, ... ) >>> with TemporaryDirectory() as directory: ... path = model.to_npz(Path(directory) / "m.npz") ... restored = LayeredGeology.from_npz(path) >>> np.array_equal(restored.layer_index, model.layer_index) True
- pycsamt.ai.geology.layers.generate_layered_geology(grid, layers, mean_interface_depth_m, *, seed, interface_relief_std_m=0.0, interface_correlation=None, minimum_thickness_m=None, interface_policy='project', boundary='reflect')[source]
Generate a correlated 2-D section or 3-D layered electrical volume.
- Parameters:
grid (GeologyGrid) – Regular target grid.
layers (sequence of ElectricalLayer) – Units ordered shallowest to deepest. At least one is required.
mean_interface_depth_m (sequence of float) – One mean depth for every adjacent layer pair.
seed (int) – Explicit root seed. Labeled child seeds independently drive interfaces and within-layer heterogeneity.
interface_relief_std_m (float or sequence, default=0.0) – Interface depth standard deviations in metres.
interface_correlation (GaussianCorrelation, sequence, or None, optional) – Horizontal correlation model(s). Required for every interface with positive relief. A single model is shared across interfaces.
minimum_thickness_m (float or None, optional) – Minimum separation between top boundary, interfaces, and bottom boundary. Default is one vertical cell.
interface_policy ({"project", "raise"}, default="project") – Project invalid/crossing surfaces into the feasible domain or reject the realization. Projection fraction is recorded on the result.
boundary ({"reflect", "periodic"}, default="reflect") – Boundary policy for all correlated fields.
- Returns:
Immutable interfaces, unit indices, resistivity, and provenance.
- Return type:
- Raises:
ValueError – If layer/interface counts disagree, thickness is infeasible, relief lacks correlation, a 3-D correlation lacks
length_y_m, or strict interface validation fails.
Examples
Laterally varying three-layer 2-D model:
>>> grid = GeologyGrid.regular_2d(nx=24, nz=16, dx_m=100, dz_m=50) >>> units = [ ... ElectricalLayer("cover", 20), ... ElectricalLayer("sediments", 100), ... ElectricalLayer("basement", 1000), ... ] >>> model = generate_layered_geology( ... grid, ... units, ... [200, 500], ... seed=5, ... interface_relief_std_m=[30, 50], ... interface_correlation=GaussianCorrelation(600, 100), ... ) >>> model.interface_depth_m.shape (2, 24) >>> set(np.unique(model.layer_index)) <= {0, 1, 2} True