pycsamt.forward.synthetic#

Synthetic 1-D earth model generators.

The LayeredModel dataclass is the central data object shared by all forward solvers and the ML training pipeline. It stores the subsurface resistivity distribution as a vector of layer resistivities and thicknesses, and provides several class-method constructors for generating geologically realistic random models.

Typical usage#

>>> from pycsamt.forward.synthetic import LayeredModel
>>> m = LayeredModel.random(n_layers=5, seed=42)
>>> m.resistivity          # array of Ω·m
>>> m.thickness            # array of metres
>>> x = m.to_vector()      # flat parameter vector for ML
>>> m2 = LayeredModel.from_vector(x, n_layers=5)

Geological priors#

LayeredModel.from_geology(name) provides pre-set parameter ranges for common geological scenarios:

'sedimentary'

Alternating conductive (clay/shale) and resistive (sand/carbonate) layers, depth 0–3 km.

'crystalline'

Resistive upper crust (1000–10 000 Ω·m) grading to conductive lower crust, depth 0–30 km.

'geothermal'

Resistive cap rock over a conductive heat source/fluid zone.

'marine'

Seawater (0.3 Ω·m) over resistive hydrocarbon reservoir, typical for marine CSEM scenarios.

'permafrost'

Very resistive frozen layer (> 1000 Ω·m) over conductive unfrozen sediments.

Module Attributes

GEOLOGY_PRIORS

Backwards-compatible view of the geology catalog.

Classes

LayeredModel(resistivity, thickness[, ...])

1-D layered earth model.

class pycsamt.forward.synthetic.LayeredModel(resistivity, thickness, depth=None, name='')[source]#

Bases: object

1-D layered earth model.

The earth has n_layers layers. The last layer is the halfspace (infinite thickness). Thicknesses apply to layers 0 … n−2.

Parameters:
  • resistivity (array-like, shape (n_layers,)) – Layer resistivities in Ω·m, ordered top → bottom.

  • thickness (array-like, shape (n_layers-1,)) – Layer thicknesses in metres. The halfspace has no thickness.

  • depth (ndarray or None) – Top-of-layer depths [m]; computed from thickness if not given.

  • name (str) – Optional label (e.g. geological scenario name).

Examples

>>> from pycsamt.forward.synthetic import LayeredModel
>>> m = LayeredModel(
...     resistivity=[100, 10, 500],
...     thickness=[300, 800],
... )
>>> m.n_layers
3
>>> m.depth
array([   0.,  300., 1100.])
resistivity: ndarray#
thickness: ndarray#
depth: ndarray = None#
name: str = ''#
property n_layers: int[source]#

Number of layers including the halfspace.

property conductivity: ndarray[source]#

Layer conductivities σ = 1/ρ [S/m].

to_vector(*, log_rho=True)[source]#

Flatten to a 1-D parameter vector suitable for ML targets.

The vector layout is [ρ₀, ρ₁, …, ρ_{n-1}, h₀, h₁, …, h_{n-2}] where ρ values are log₁₀(Ω·m) when log_rho is True.

Parameters:

log_rho (bool) – If True (default), resistivities are stored as log₁₀(ρ). This normalises the dynamic range and improves neural network convergence.

Returns:

v

Return type:

ndarray, shape (2 * n_layers - 1,)

classmethod from_vector(v, n_layers, *, log_rho=True, name='')[source]#

Reconstruct a LayeredModel from a flat parameter vector.

Parameters:
  • v (ndarray, shape (2 * n_layers - 1,)) – Parameter vector as produced by to_vector().

  • n_layers (int) – Number of layers.

  • log_rho (bool) – Whether resistivities in v are in log₁₀ space.

  • name (str)

Return type:

LayeredModel

classmethod random(n_layers=5, *, rho_range=(1.0, 10000.0), depth_max=2000.0, seed=None, name='random')[source]#

Generate a random layered model with log-uniform resistivities.

Layer thicknesses are drawn from a Dirichlet-like distribution that sums to depth_max so that the model spans the full target depth range.

Parameters:
  • n_layers (int) – Number of layers (including halfspace).

  • rho_range ((low, high)) – Resistivity bounds in Ω·m.

  • depth_max (float) – Total depth spanned by the first (n-1) layers [m].

  • seed (int, Generator, or None) – Random seed for reproducibility.

  • name (str)

Return type:

LayeredModel

classmethod blocky(n_layers=4, *, rho_background=100.0, rho_anomaly=5.0, anomaly_layer=1, depth_max=1000.0, equal_thickness=True, seed=None, name='blocky')[source]#

Build a model with a single conductive (or resistive) anomaly embedded in a resistive (or conductive) background.

Parameters:
  • rho_background (float) – Background layer resistivity [Ω·m].

  • rho_anomaly (float) – Anomaly layer resistivity [Ω·m].

  • anomaly_layer (int) – Zero-based index of the anomalous layer.

  • n_layers (int)

  • depth_max (float)

  • equal_thickness (bool)

  • seed (int | Generator | None)

  • name (str)

Return type:

LayeredModel

classmethod smooth(n_layers=10, *, rho_surface=100.0, rho_deep=10.0, depth_max=5000.0, perturbation=0.2, seed=None, name='smooth')[source]#

Build a model with a smooth gradient from surface to depth, plus small random perturbations.

Parameters:
  • rho_surface (float) – Resistivity at the surface [Ω·m].

  • rho_deep (float) – Resistivity at depth_max [Ω·m].

  • perturbation (float) – Fractional standard deviation of log₁₀(ρ) noise added to the gradient. 0 = pure gradient.

  • n_layers (int)

  • depth_max (float)

  • seed (int | Generator | None)

  • name (str)

Return type:

LayeredModel

classmethod from_geology(name, *, seed=None)[source]#

Generate a random model using a predefined geological prior.

Parameters:
  • name (str) – One of: 'sedimentary', 'crystalline', 'geothermal', 'marine', 'permafrost'.

  • seed (int | Generator | None)

Raises:

KeyError – If name is not in GEOLOGY_PRIORS.

Return type:

LayeredModel

plot(ax=None, *, log_scale=True, depth_max=None, label=None, **kwargs)[source]#

Plot the 1-D resistivity–depth profile.

Parameters:
  • ax (Axes or None) – Target axes. Created if not given.

  • log_scale (bool) – Use log₁₀ scale on the resistivity axis.

  • depth_max (float or None) – Maximum depth shown. Defaults to 1.2× the deepest interface.

  • label (str or None) – Line label for legend.

Returns:

ax

Return type:

Axes

pycsamt.forward.synthetic.GEOLOGY_PRIORS: dict[str, dict] = {'basement': {'depth_max_range': (1000, 20000), 'description': 'Deep resistive basement / craton', 'log_rho_range': (3.0, 5.0), 'n_layers': (2, 4)}, 'coastal': {'depth_max_range': (10, 500), 'description': 'Coastal/delta mixing zone: fresh water over saline sediments', 'log_rho_range': (-0.3010299956639812, 2.6989700043360187), 'n_layers': (3, 6)}, 'crystalline': {'depth_max_range': (5000, 30000), 'description': 'Resistive upper to conductive lower crust', 'log_rho_range': (2.0, 4.500003068051694), 'n_layers': (3, 6)}, 'evaporite': {'depth_max_range': (200, 3000), 'description': 'Salt/anhydrite evaporite basin', 'log_rho_range': (3.0, 6.0), 'n_layers': (2, 5)}, 'geothermal': {'depth_max_range': (500, 5000), 'description': 'Resistive cap over conductive geothermal reservoir', 'log_rho_range': (0.3010299956639812, 4.0), 'n_layers': (3, 5)}, 'hydrothermal': {'depth_max_range': (200, 3000), 'description': 'Hydrothermal alteration zone with clay cap', 'log_rho_range': (0.0, 3.0), 'n_layers': (3, 6)}, 'laterite': {'depth_max_range': (10, 200), 'description': 'Deeply weathered tropical profile: laterite / saprolite / bedrock', 'log_rho_range': (1.0, 3.6989700043360187), 'n_layers': (3, 6)}, 'marine': {'depth_max_range': (100, 2000), 'description': 'Seawater over possible HC reservoir (CSEM context)', 'log_rho_range': (-0.5228787452803376, 3.0), 'n_layers': (3, 6)}, 'mineralized': {'depth_max_range': (50, 1000), 'description': 'Conductive ore-bearing zone embedded in resistive host rock', 'log_rho_range': (-2.0, 3.0), 'n_layers': (3, 6)}, 'permafrost': {'depth_max_range': (50, 500), 'description': 'Frozen resistive layer over conductive unfrozen sediments', 'log_rho_range': (1.0, 4.500003068051694), 'n_layers': (3, 5)}, 'porphyry': {'depth_max_range': (200, 2000), 'description': 'Porphyry copper system: clay-alteration cap over mineralised core', 'log_rho_range': (1.0, 4.0), 'n_layers': (3, 6)}, 'sedimentary': {'depth_max_range': (500, 3000), 'description': 'Alternating clay/shale and sand/carbonate layers', 'log_rho_range': (0.47712125471966244, 3.49996186559619), 'n_layers': (3, 7)}, 'volcanic': {'depth_max_range': (200, 5000), 'description': 'Volcanic pile: alternating lavas and pyroclastics', 'log_rho_range': (1.0, 4.0), 'n_layers': (3, 7)}}#

Backwards-compatible view of the geology catalog. Prefer from pycsamt.metadata.geology import CATALOG for new code.