2.25.3.1. pycsamt.ai.inversion#

Supervised, physics-informed (PINN), and hybrid neural-network inverters for 1-D/2-D/3-D EM inversion, plus uncertainty calibration, static-shift/skew reliability weighting for the DUHI hybrid AI-physics pathway, and experiment/run configuration.

pycsamt.ai.inversion#

High-level AI-based EM inversion workflows.

class pycsamt.ai.inversion.InversionConfig(arch='resnet', n_layers=5, solver='mt1d', device=None, include_phase=True, log_thickness=True, augment_noise=0.02, epochs=100, batch_size=256, lr=0.001, weight_decay=1e-05, patience=20, min_delta=1e-05, val_frac=0.1, grad_clip=1.0, seed=None, checkpoint_dir='checkpoints', checkpoint_name='em_inverter', save_best=True, verbose=True)#

Bases: object

Collect settings that define a 1-D AI-based EM inversion run.

InversionConfig is the configuration object for EMInverter1D. It covers four concern areas: network architecture, training hyperparameters, regularisation, and checkpoint management.

The recommended workflow:

  1. Generate a template with write_template().

  2. Edit the values in the generated file.

  3. Load the edited file with from_file().

  4. Optionally call validate() to catch range errors.

  5. Call to_inverter() to instantiate a ready-to-fit EMInverter1D.

  6. Pass to_fit_kwargs() to inv.fit(dataset, **cfg.to_fit_kwargs()).

Parameters:
  • arch ({'resnet', 'cnn1d', 'fcn'}) – Network architecture.

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

  • solver ({'mt1d', 'csamt1d', 'tem1d'}) – Forward solver this inverter targets.

  • device (str or None) – Compute device; None auto-detects (CUDA > MPS > CPU).

  • include_phase (bool) – Include impedance phase in the input feature vector.

  • log_thickness (bool) – Apply log10 to thickness targets during training.

  • augment_noise (float) – On-the-fly per-epoch noise augmentation level.

  • epochs (int) – Maximum training epochs.

  • batch_size (int) – Mini-batch size.

  • lr (float) – Initial Adam learning rate.

  • weight_decay (float) – Adam L2 regularisation coefficient.

  • patience (int) – Early-stopping patience (epochs without improvement).

  • min_delta (float) – Minimum validation-loss decrease to count as an improvement.

  • val_frac (float) – Fraction of data used for validation.

  • grad_clip (float or None) – Gradient-norm clipping threshold; None disables clipping.

  • seed (int or None) – Random seed for train/val split.

  • checkpoint_dir (str or None) – Directory for checkpoint files; None disables auto-saving.

  • checkpoint_name (str) – Base file name for checkpoints (without extension).

  • save_best (bool) – Auto-save the best checkpoint after training.

  • verbose (bool) – Print training progress.

Examples

Default configuration (ResNet, 5 layers, MT1D):

>>> cfg = InversionConfig()
>>> cfg.arch
'resnet'

Deep ResNet for a crystalline-crust survey:

>>> cfg = InversionConfig(
...     arch="resnet",
...     n_layers=6,
...     solver="mt1d",
...     epochs=300,
...     lr=5e-4,
...     seed=0,
... )

Round-trip template:

>>> path = InversionConfig.write_template("inv_config.yml")
>>> cfg = InversionConfig.from_file(path)
>>> cfg.solver
'mt1d'

Snapshot a fitted inverter:

>>> cfg = InversionConfig.from_inverter(inv)
>>> cfg.write_template("run_snapshot.py")
arch: str = 'resnet'#
n_layers: int = 5#
solver: str = 'mt1d'#
device: str | None = None#
include_phase: bool = True#
log_thickness: bool = True#
augment_noise: float = 0.02#
epochs: int = 100#
batch_size: int = 256#
lr: float = 0.001#
weight_decay: float = 1e-05#
patience: int = 20#
min_delta: float = 1e-05#
val_frac: float = 0.1#
grad_clip: float | None = 1.0#
seed: int | None = None#
checkpoint_dir: str | None = 'checkpoints'#
checkpoint_name: str = 'em_inverter'#
save_best: bool = True#
verbose: bool = True#
validate()#

Check parameter ranges and raise ValueError on errors.

Raises:

ValueError – Descriptive message pointing to the offending parameter.

Return type:

None

to_inverter()#

Instantiate a EMInverter1D.

Returns an untrained inverter configured according to the architecture and feature settings stored in this config. Call inv.fit(dataset, **cfg.to_fit_kwargs()) to train it.

Return type:

EMInverter1D

Examples

>>> cfg = InversionConfig(arch="cnn1d", n_layers=4, epochs=50)
>>> inv = cfg.to_inverter()
>>> type(inv).__name__
'EMInverter1D'
to_fit_kwargs()#

Assemble keyword arguments for EMInverter1D.fit().

The returned dict is ready to be unpacked directly:

inv = cfg.to_inverter()
inv.fit(dataset, **cfg.to_fit_kwargs())
Returns:

Keys: epochs, batch_size, lr, patience, val_frac, grad_clip, seed, verbose.

Return type:

dict

Notes

weight_decay and min_delta are EMTrainer parameters not currently exposed through EMInverter1D.fit. They are stored in InversionConfig for documentation and round-trip reproducibility but are not included in the returned dict.

checkpoint_path()#

Return the full checkpoint file path, or None if disabled.

Return type:

pathlib.Path or None

classmethod from_inverter(inv)#

Snapshot a fitted (or unfitted) inverter’s architecture settings.

Creates an InversionConfig whose architecture and feature fields match those of inv. Training hyperparameters are reset to their defaults because the inverter does not record them after training.

Use this to generate a reproducible record of a training run:

cfg = InversionConfig.from_inverter(inv)
cfg.write_template("run_snapshot.py")
Parameters:

inv (EMInverter1D) – Source inverter (fitted or unfitted).

Return type:

InversionConfig

to_template(path='inversion_config.py', *, fmt=None)#

Write this configuration to an annotated source-of-truth file.

Parameters:
  • path (path-like, default "inversion_config.py") – Destination file. The suffix selects the output format (.py, .json, .yml).

  • fmt ({"py", "json", "yml", "yaml"}, optional) – Explicit format override.

Return type:

pathlib.Path

classmethod write_template(path='inversion_config.py', *, fmt=None)#

Generate a documented source-of-truth configuration file.

Creates a file with default parameter values and an inline comment for every parameter. Edit the file, then load with from_file().

Parameters:
  • path (path-like, default "inversion_config.py") – Destination file.

  • fmt ({"py", "json", "yml", "yaml"}, optional) – Explicit format override.

Return type:

pathlib.Path

Examples

>>> from pycsamt.ai.inversion.config import InversionConfig
>>> path = InversionConfig.write_template("my_inv.yml")
>>> path.suffix
'.yml'
classmethod from_file(path, *, strict=True)#

Load a configuration from a source-of-truth file.

Parameters:
  • path (path-like) – Python, JSON, YML, or YAML file generated by write_template() or following the same structure.

  • strict (bool, default True) – If True, unknown keys raise ValueError. If False, unknown keys are silently ignored.

Return type:

InversionConfig

Examples

>>> InversionConfig.write_template("inv_config.json")
PosixPath('inv_config.json')
>>> cfg = InversionConfig.from_file("inv_config.json")
>>> cfg.arch
'resnet'
classmethod read(path, *, strict=True)#

Alias — matches the convention used by ModEmConfig and OccamConfig.

Parameters:
Return type:

InversionConfig

summary()#

Return a human-readable multi-line summary of the configuration.

Return type:

str

class pycsamt.ai.inversion.RunConfig(forward=<factory>, inversion=<factory>, name='', description='')#

Bases: object

Bundle a ForwardConfig and an InversionConfig into one source-of-truth experiment file.

Parameters:
  • forward (ForwardConfig) – Dataset generation and solver settings.

  • inversion (InversionConfig) – Network architecture and training settings.

  • name (str) – Short experiment identifier written into the file header.

  • description (str) – Optional free-text note describing the experiment.

Notes

validate() checks internal consistency between the two sub-configs:

  • forward.solver == inversion.solver

  • forward.include_phase == inversion.include_phase

  • Fixed layer count (n_layers_min == n_layers_max) must match inversion.n_layers.

Examples

Default run (MT1D, ResNet, 5 layers):

>>> run = RunConfig()
>>> run.forward.solver
'mt1d'
>>> run.inversion.arch
'resnet'

Custom experiment:

>>> run = RunConfig(
...     forward=ForwardConfig(solver="mt1d", n_samples=20_000, seed=1),
...     inversion=InversionConfig(arch="resnet", n_layers=5, epochs=200),
...     name="mt1d_resnet_20k",
... )
>>> run.validate()

Write a template, edit it, reload:

>>> path = RunConfig.write_template("experiment_01.yml")
>>> run = RunConfig.from_file(path)
forward: ForwardConfig#
inversion: InversionConfig#
name: str = ''#
description: str = ''#
validate()#

Validate both sub-configs and their mutual consistency.

Raises:

ValueError – Descriptive message pointing to the offending parameter or the cross-config inconsistency.

Return type:

None

to_dataset_kwargs()#

Return kwargs for generate_dataset().

Delegates to ForwardConfig.to_dataset_kwargs().

Return type:

dict[str, Any]

to_inverter()#

Return a configured, untrained EMInverter1D.

Delegates to InversionConfig.to_inverter().

to_fit_kwargs()#

Return kwargs for fit().

Delegates to InversionConfig.to_fit_kwargs().

Return type:

dict[str, Any]

checkpoint_path()#

Return the checkpoint file path, or None if disabled.

Delegates to InversionConfig.checkpoint_path().

Return type:

Path | None

to_template(path='run_config.py', *, fmt=None)#

Write this run configuration to an annotated source-of-truth file.

Parameters:
  • path (path-like, default "run_config.py") – Destination. The suffix selects the format (.py, .json, .yml).

  • fmt ({"py", "json", "yml", "yaml"}, optional) – Explicit format override.

Return type:

pathlib.Path

classmethod write_template(path='run_config.py', *, fmt=None, name='', description='')#

Generate a documented source-of-truth run configuration file.

Writes a single file covering both dataset generation and network training with default parameter values and an inline comment for every parameter.

Parameters:
  • path (path-like, default "run_config.py") – Destination.

  • fmt ({"py", "json", "yml", "yaml"}, optional) – Explicit format override.

  • name (str) – Experiment name written into the file header.

  • description (str) – Free-text description written into the file header.

Return type:

pathlib.Path

Examples

>>> from pycsamt.ai.inversion.run_config import RunConfig
>>> path = RunConfig.write_template("experiment_01.yml")
>>> path.suffix
'.yml'
classmethod from_file(path, *, strict=True)#

Load a run configuration from a source-of-truth file.

Parameters:
  • path (path-like) – Python, JSON, YML, or YAML file generated by write_template() or following the same structure.

  • strict (bool, default True) – If True, unknown parameter keys raise ValueError. If False, unknown keys are silently ignored.

Return type:

RunConfig

Examples

>>> RunConfig.write_template("run.json")
PosixPath('run.json')
>>> run = RunConfig.from_file("run.json")
>>> run.forward.solver
'mt1d'
>>> run.inversion.arch
'resnet'
classmethod read(path, *, strict=True)#

Alias — matches the convention used by ModEmConfig, OccamConfig, ForwardConfig.

Parameters:
Return type:

RunConfig

summary()#

Return a human-readable multi-line summary of the full run config.

Return type:

str

class pycsamt.ai.inversion.PINNInverter1D(sites, *, solver='mt1d', n_layers=10, depth_max=2000.0, smoothness_weight=0.01, lr=0.01, device=None, comp='xy', recursive=True, on_dup='replace', verbose=0)#

Bases: BasePINNInverter

Physics-informed 1-D EM inversion.

Fits a layered Earth model to observed MT/CSAMT apparent resistivity and phase by gradient descent. No labelled training data is required.

Parameters:
  • sites (Any) – Path, EDIFile, EDICollection, Site, Sites, APISurvey, or iterable. Station data are extracted at construction time.

  • solver ({'mt1d', 'csamt1d'}) – EM physics solver (TEM not supported).

  • n_layers (int, default 10) – Number of earth layers including the halfspace.

  • depth_max (float, default 2000.0) – Approximate investigation depth in metres, used to set equal initial layer thicknesses.

  • smoothness_weight (float, default 0.01) – Regularisation weight \(\lambda\) on the first-difference of log-resistivity.

  • lr (float, default 1e-2) – Adam learning rate.

  • device (str or None) – Torch device. Auto-detects CUDA/CPU if None.

  • comp ({'xy', 'yx', 'xx', 'yy'}, default 'xy') – Impedance tensor component to use.

  • recursive (bool, default True) – Passed to ensure_sites.

  • on_dup (str, default 'replace') – Passed to ensure_sites.

  • verbose (int, default 0) – Verbosity level for site loading.

fit(epochs=500, *, verbose=True, log_every=100)#

Run the physics-informed optimisation.

Parameters:
  • epochs (int, default 500) – Number of Adam iterations per station.

  • verbose (bool, default True) – Print progress per station.

  • log_every (int, default 100) – Print epoch detail every this many steps.

Return type:

self

predict()#

Return fitted layered models for all stations.

Returns:

models – One per station, same order as stations.

Return type:

list of LayeredModel

residuals()#

Compute observed vs predicted data for all sites.

Returns:

Columns: station, freq, rho_obs, rho_pred, phase_obs, phase_pred.

Return type:

pandas.DataFrame

loss_curves()#

Return the Adam loss history for all stations.

Returns:

Columns: station, epoch, loss.

Return type:

pandas.DataFrame

property stations: list[str]#

Station names in order.

property n_sites: int#

Number of loaded stations.

class pycsamt.ai.inversion.PINNInverter2D(sites, *, n_layers=10, depth_max=2000.0, n_freqs=32, mode='te', smoothness_weight=0.01, lateral_weight=0.005, epochs=300, lr=0.01, comp_te='xy', comp_tm='yx', device=None, recursive=True, on_dup='replace', verbose=0)#

Bases: BasePINNInverter

Physics-informed 2-D MT inversion.

Optimises a pseudo-2D resistivity section by minimising the data misfit with the 1-D MT forward plus lateral and vertical smoothness penalties.

Parameters:
  • sites (Any) – Path, EDIFile, EDICollection, Site, Sites, APISurvey, or iterable.

  • n_layers (int, default 10) – Number of model layers per station.

  • depth_max (float, default 2000.0) – Target maximum depth in metres.

  • n_freqs (int, default 32) – Frequency-grid points for the common grid.

  • mode ({'te', 'tm', 'both'}, default 'te') – Which observed polarisation to use. 'both' averages TE and TM data misfits.

  • smoothness_weight (float, default 0.01) – Vertical smoothness weight \(\lambda_z\).

  • lateral_weight (float, default 0.005) – Lateral smoothness weight \(\lambda_x\).

  • epochs (int, default 300) – Adam iterations.

  • lr (float, default 1e-2) – Adam learning rate.

  • comp_te (str, default 'xy') – Impedance tensor component for TE mode.

  • comp_tm (str, default 'yx') – Impedance tensor component for TM mode.

  • device (str or None) – Torch compute device (auto-detects if None).

  • recursive (bool, default True)

  • on_dup (str, default 'replace')

  • verbose (int, default 0)

fit(*, verbose=True, log_every=50)#

Run the joint 2-D physics-informed inversion.

Parameters:
  • verbose (bool, default True)

  • log_every (int, default 50)

Return type:

self

resistivity_section(*, as_log10=True)#

Return the 2-D resistivity section.

Parameters:

as_log10 (bool, default True) – If True return log10(rho); else linear rho.

Return type:

ndarray (n_layers, n_stations)

thickness_section()#

Return layer thicknesses in metres.

Return type:

ndarray (n_layers-1, n_stations)

convergence_curve()#

Return Adam loss history.

Returns:

Columns: epoch, loss.

Return type:

pandas.DataFrame

residuals()#

Observed vs predicted data fit.

Returns:

Columns: station, freq, rho_obs, rho_pred, phase_obs, phase_pred.

Return type:

pandas.DataFrame

property stations: list[str]#

Station names in profile order.

property n_sites: int#

Number of loaded stations.

class pycsamt.ai.inversion.PINNInverter3D(sites, *, n_layers=10, depth_max=2000.0, n_freqs=32, mode='te', smoothness_weight=0.01, graph_weight=0.005, radius=5000.0, adjacency=None, station_coords=None, station_spacing=500.0, epochs=300, lr=0.01, comp_te='xy', comp_tm='yx', device=None, recursive=True, on_dup='replace', verbose=0)#

Bases: BasePINNInverter

Physics-informed quasi-3D MT inversion.

Optimises a per-station 1-D column model for all stations simultaneously using the 1-D MT forward with graph-Laplacian spatial smoothness coupling neighbour stations through their shared adjacency.

Parameters:
  • sites (Any) – Path, EDIFile, EDICollection, Site, Sites, APISurvey, or iterable.

  • n_layers (int, default 10) – Number of model layers per station.

  • depth_max (float, default 2000.0) – Target maximum depth in metres.

  • n_freqs (int, default 32) – Frequency-grid points for the common grid.

  • mode ({'te', 'tm', 'both'}, default 'te') – Which polarisation to fit.

  • smoothness_weight (float, default 0.01) – Vertical smoothness weight.

  • graph_weight (float, default 0.005) – Graph-Laplacian spatial smoothness weight.

  • radius (float, default 5000.0) – Edge radius in metres used to build the station adjacency when adjacency is None.

  • adjacency (ndarray (S, S) or None) – Pre-computed normalised adjacency matrix. When None it is built from station positions with build_adjacency().

  • station_coords (ndarray (S, 2) or None) – Explicit (x, y) station positions [m]. Auto-extracted from site metadata if None.

  • station_spacing (float, default 500.0) – Uniform grid spacing [m] used when no geographic coordinates are available.

  • epochs (int, default 300) – Adam iterations.

  • lr (float, default 1e-2) – Adam learning rate.

  • comp_te (str, default 'xy')

  • comp_tm (str, default 'yx')

  • device (str or None)

  • recursive (bool, default True)

  • on_dup (str, default 'replace')

  • verbose (int, default 0)

fit(*, verbose=True, log_every=50)#

Run the joint 3-D physics-informed inversion.

Return type:

self

Parameters:
resistivity_volume(*, as_log10=True)#

Return the quasi-3D resistivity volume.

Returns:

Column i is the 1-D model at station i in the same order as stations.

Return type:

ndarray (n_layers, n_stations)

Parameters:

as_log10 (bool)

thickness_volume()#

Return layer thicknesses in metres.

Return type:

ndarray (n_layers-1, n_stations)

station_coords()#

Return station (x, y) positions [m].

Return type:

ndarray

adjacency()#

Return the station adjacency matrix.

Return type:

ndarray

convergence_curve()#

Return Adam loss history.

Returns:

Columns: epoch, loss.

Return type:

pandas.DataFrame

residuals()#

Observed vs predicted data fit.

Returns:

Columns: station, freq, rho_obs, rho_pred, phase_obs, phase_pred.

Return type:

pandas.DataFrame

property stations: list[str]#

Station names in order.

property n_sites: int#

Number of loaded stations.

class pycsamt.ai.inversion.HybridInverter1D(sites, ai_inverter, *, solver='mt1d', max_iter=200, smoothness_weight=0.005, lr=0.005, device=None, comp='xy', n_freqs=32, recursive=True, on_dup='replace', verbose=0)#

Bases: BaseHybridInverter

Two-stage hybrid AI + physics 1-D inversion.

Parameters:
  • sites (Any) – Path, EDIFile, EDICollection, Site, Sites, APISurvey, or iterable.

  • ai_inverter (EMInverter1D or str or Path) – Pre-trained supervised inverter (fitted EMInverter1D) or path to a saved .npz checkpoint.

  • solver ({'mt1d', 'csamt1d'}, default 'mt1d') – EM physics used in the refinement step.

  • max_iter (int, default 200) – Adam iterations for the physics refinement.

  • smoothness_weight (float, default 0.005) – Regularisation weight on log-resistivity first differences.

  • lr (float, default 5e-3) – Adam learning rate for Stage 2.

  • device (str or None) – Torch device. Auto-detects CUDA/CPU if None.

  • comp ({'xy', 'yx', 'xx', 'yy'}, default 'xy') – Impedance component for observed data.

  • n_freqs (int, default 32) – Frequency-grid size for EMInverter1D input.

  • recursive (bool, default True)

  • on_dup (str, default 'replace')

  • verbose (int, default 0)

fit(*, verbose=True, log_every=50)#

Run both inversion stages.

Parameters:
  • verbose (bool, default True) – Print per-station progress.

  • log_every (int, default 50) – Epoch-detail print frequency for Stage 2.

Return type:

self

predict()#

Return Stage-2 refined layered models.

Return type:

list of LayeredModel

stage1_models()#

Return Stage-1 (AI-only) layered models.

These are the AI starting points before physics refinement.

Return type:

list of LayeredModel

convergence_curves()#

Return Stage-2 Adam loss history.

Returns:

Columns: station, epoch, loss.

Return type:

pandas.DataFrame

residuals(stage=2)#

Observed vs predicted data fit.

Parameters:

stage ({1, 2}, default 2) – Which stage’s models to evaluate.

Returns:

Columns: station, freq, rho_obs, rho_pred, phase_obs, phase_pred.

Return type:

pandas.DataFrame

property stations: list[str]#

Station names in order.

property n_sites: int#

Number of loaded stations.

class pycsamt.ai.inversion.HybridInverter2D(sites, ai_inverter, *, n_layers=None, depth_max=2000.0, n_freqs=32, mode='te', smoothness_weight=0.005, lateral_weight=0.003, epochs=150, lr=0.005, comp_te='xy', comp_tm='yx', device=None, recursive=True, on_dup='replace', verbose=0)#

Bases: BaseHybridInverter

Two-stage hybrid AI + physics 2-D inversion.

Parameters:
  • sites (Any) – Path, EDIFile, EDICollection, Site, Sites, APISurvey, or iterable.

  • ai_inverter (EMInverter2D or str or Path) – Pre-trained EMInverter2D (fitted) or path to a .npz checkpoint.

  • n_layers (int or None) – Layers per station in Stage 2. Defaults to ai_inverter.n_depth.

  • depth_max (float, default 2000.0) – Total depth for uniform thickness init when Stage 1 does not provide thicknesses.

  • n_freqs (int, default 32) – Frequency-grid size for the panel and the shared optimisation grid.

  • mode ({'te', 'tm', 'both'}, default 'te') – Data polarisation used in Stage 2.

  • smoothness_weight (float, default 0.005) – Vertical smoothness weight.

  • lateral_weight (float, default 0.003) – Lateral smoothness weight.

  • epochs (int, default 150) – Adam iterations for Stage 2.

  • lr (float, default 5e-3) – Adam learning rate for Stage 2.

  • comp_te (str, default 'xy')

  • comp_tm (str, default 'yx')

  • device (str or None)

  • recursive (bool, default True)

  • on_dup (str, default 'replace')

  • verbose (int, default 0)

fit(*, verbose=True, log_every=50)#

Run both inversion stages.

Parameters:
  • verbose (bool, default True)

  • log_every (int, default 50)

Return type:

self

resistivity_section(*, as_log10=True)#

Return the Stage-2 2-D resistivity section.

Parameters:

as_log10 (bool, default True)

Return type:

ndarray (n_layers, n_stations)

thickness_section()#

Return Stage-2 layer thicknesses in metres.

Return type:

ndarray (n_layers-1, n_stations)

stage1_section(*, as_log10=True)#

Return the Stage-1 AI 2-D section.

Parameters:

as_log10 (bool, default True)

Return type:

ndarray (n_layers, n_stations)

convergence_curve()#

Return Stage-2 Adam loss history.

Returns:

Columns: epoch, loss.

Return type:

pandas.DataFrame

residuals(stage=2)#

Observed vs predicted data fit.

Parameters:

stage ({1, 2}, default 2) – Which stage’s models to evaluate.

Returns:

Columns: station, freq, rho_obs, rho_pred, phase_obs, phase_pred.

Return type:

pandas.DataFrame

property stations: list[str]#

Station names in profile order.

property n_sites: int#

Number of loaded stations.

pycsamt.ai.inversion.map_ai_grid_to_occam(grid, model, mesh, x_coordinates=None, z_coordinates=None)#

Map a regular AI grid to Occam parameter order.

AI values are bilinearly interpolated to physical Occam cell centres. Values within each model-parameter rectangle are then averaged with finite-element cell area as weight:

\[\bar m_j = \frac{\sum_{k \in G_j} m_k A_k} {\sum_{k \in G_j} A_k},\]

where \(G_j\) is parameter group \(j\) and \(A_k\) is the area of mesh cell \(k\). Air layers are excluded, and the returned vector follows the layer-major order used by Occam startup and iteration files.

Parameters:
  • grid (array-like of float, shape (n_depth, n_horizontal)) – Finite AI values on a regular or explicitly coordinated grid. Values are typically log10 resistivity or predictive standard deviation.

  • model (OccamModel) – Populated Occam model definition. Horizontal parameter codes give the number of mesh cells spanned, and n_merge gives the number of earth rows spanned vertically.

  • mesh (OccamMesh) – Populated Occam finite-element mesh supplying physical cell widths, layer thicknesses, node positions, and air-layer count.

  • x_coordinates (array-like of float, optional) – Strictly increasing horizontal coordinates of AI grid columns, in the same coordinate system as mesh.x_nodes. The length must equal grid.shape[1]. If omitted, uniformly spaced cell centres spanning the complete Occam horizontal domain are used.

  • z_coordinates (array-like of float, optional) – Strictly increasing AI depth coordinates, positive downward from the earth surface. The length must equal grid.shape[0]. If omitted, uniformly spaced cell centres spanning the Occam earth domain are used.

Returns:

Area-weighted values in Occam layer-major parameter order.

Return type:

numpy.ndarray of float, shape (model.n_params,)

Raises:
  • TypeError – Raised when model or mesh lacks the required Occam geometry interface.

  • ValueError – Raised for invalid grids or coordinates, non-positive mesh dimensions, model groups inconsistent with the mesh, or a returned parameter count inconsistent with model.n_params.

Notes

Values outside the supplied AI coordinate range use nearest-edge extrapolation through numpy.interp(). Publication workflows should normally supply coordinates spanning the complete inversion domain so extrapolation is unnecessary.

The mapping averages log10 resistivity when grid is in log10 units. It therefore preserves the parameterization optimized by Occam rather than arithmetic resistivity.

When grid contains predictive standard deviation, the same area-weighted spatial averaging is applied. This treats uncertainty as a spatial field and does not assume independent AI pixels; it is therefore not a standard-error reduction by the number of cells.

See also

pycsamt.ai.inversion.duhi2d.DUHIInverter2D

Uses this mapper for AI means and standard deviations.

pycsamt.models.occam2d.OccamModel

Defines the parameter grouping traversed here.

pycsamt.models.occam2d.OccamMesh

Defines the physical cell areas used as weights.

Examples

Map a grid after reading an Occam project:

>>> from pycsamt.ai.inversion.mapping2d import (
...     map_ai_grid_to_occam,
... )
>>> from pycsamt.models.occam2d import OccamMesh, OccamModel
>>> mesh = OccamMesh.read("occam_run/Occam2DMesh")
>>> model = OccamModel.read("occam_run/Occam2DModel")
>>> parameters = map_ai_grid_to_occam(
...     ai_grid,
...     model,
...     mesh,
...     x_coordinates=ai_x,
...     z_coordinates=ai_z,
... )
>>> parameters.shape == (model.n_params,)
True
pycsamt.ai.inversion.dimensionality_reliability(beta_deg, *, beta_scale_deg=5.0, minimum=0.0)#

Convert phase-tensor skew into compatibility with 2-D physics.

Parameters:
  • beta_deg (array-like of float) – Phase-tensor skew angles in degrees. Non-finite entries receive the configured minimum reliability.

  • beta_scale_deg (float, default=5.0) – Positive skew scale \(\beta_0\). Reliability equals exp(-1) at this absolute skew.

  • minimum (float, default=0.0) – Lower bound in the closed interval [0, 1].

Returns:

Reliability values with the same shape as beta_deg.

Return type:

numpy.ndarray of float

Raises:

ValueError – If the input is empty or scalar controls are invalid.

Examples

>>> dimensionality_reliability([0.0, 5.0]).round(6).tolist()
[1.0, 0.367879]
pycsamt.ai.inversion.combine_observation_reliability(measurement, dimensionality, *, minimum=0.0)#

Return bounded measurement-by-dimensionality reliability.

Parameters:
  • measurement (array-like of float) – Reliability factors in [0, 1]. Inputs must be broadcastable to one common shape.

  • dimensionality (array-like of float) – Reliability factors in [0, 1]. Inputs must be broadcastable to one common shape.

  • minimum (float, default=0.0) – Lower bound applied after multiplication.

Returns:

Broadcast product clipped to [minimum, 1].

Return type:

numpy.ndarray of float

Raises:

ValueError – If inputs are empty, non-finite, outside [0, 1], or cannot be broadcast together.

Examples

>>> combine_observation_reliability(
...     [0.8, 0.5], [0.5, 0.2]
... ).tolist()
[0.4, 0.1]
class pycsamt.ai.inversion.HybridInverter3D(sites, ai_inverter, *, n_layers=None, depth_max=2000.0, n_freqs=32, mode='te', smoothness_weight=0.005, graph_weight=0.003, radius=5000.0, adjacency=None, station_coords=None, station_spacing=500.0, epochs=150, lr=0.005, comp_te='xy', comp_tm='yx', device=None, recursive=True, on_dup='replace', verbose=0)#

Bases: BaseHybridInverter

Two-stage hybrid AI + physics quasi-3D inversion.

Parameters:
  • sites (Any) – Path, EDIFile, EDICollection, Site, Sites, APISurvey, or iterable.

  • ai_inverter (GCNInverter3D or str or Path) – Pre-trained (fitted) GCNInverter3D or path to a .npz checkpoint.

  • n_layers (int or None) – Layers per station for Stage 2. Defaults to ai_inverter.n_layers.

  • depth_max (float, default 2000.0) – Target depth for uniform thickness init.

  • n_freqs (int, default 32) – Frequency-grid size for Stage 2 optimisation.

  • mode ({'te', 'tm', 'both'}, default 'te') – Polarisation used in Stage 2.

  • smoothness_weight (float, default 0.005) – Vertical smoothness weight for Stage 2.

  • graph_weight (float, default 0.003) – Graph spatial smoothness weight.

  • radius (float, default 5000.0) – Edge radius [m] for adjacency construction.

  • adjacency (ndarray (S, S) or None) – Pre-computed adjacency. Built from station positions if None.

  • station_coords (ndarray (S, 2) or None) – Explicit (x, y) positions [m].

  • station_spacing (float, default 500.0) – Fallback uniform grid spacing [m].

  • epochs (int, default 150) – Adam iterations for Stage 2.

  • lr (float, default 5e-3) – Adam learning rate for Stage 2.

  • comp_te (str, default 'xy')

  • comp_tm (str, default 'yx')

  • device (str or None)

  • recursive (bool, default True)

  • on_dup (str, default 'replace')

  • verbose (int, default 0)

fit(*, verbose=True, log_every=50)#

Run both inversion stages.

Return type:

self

Parameters:
resistivity_volume(*, as_log10=True)#

Return the Stage-2 quasi-3D volume.

Return type:

ndarray (n_layers, n_stations)

Parameters:

as_log10 (bool)

thickness_volume()#

Return Stage-2 layer thicknesses [m].

Return type:

ndarray (n_layers-1, n_stations)

stage1_volume(*, as_log10=True)#

Return the Stage-1 GCN quasi-3D volume.

Return type:

ndarray (n_layers, n_stations)

Parameters:

as_log10 (bool)

convergence_curve()#

Return Stage-2 Adam loss history.

Returns:

Columns: epoch, loss.

Return type:

pandas.DataFrame

residuals(stage=2)#

Observed vs predicted data fit.

Parameters:

stage ({1, 2}, default 2)

Returns:

Columns: station, stage, freq, rho_obs, rho_pred, phase_obs, phase_pred.

Return type:

pandas.DataFrame

station_coords()#

Return station (x, y) positions [m].

Return type:

ndarray

adjacency()#

Return the station adjacency matrix.

Return type:

ndarray

property stations: list[str]#

Station names in order.

property n_sites: int#

Number of loaded stations.

class pycsamt.ai.inversion.SiteObs1D(name, freq, rho_obs, phase_obs)#

Bases: object

Observed 1-D MT/CSAMT data for one station.

Parameters:
  • name (str) – Station identifier.

  • freq (ndarray (n_f,)) – Frequencies in Hz, sorted high to low.

  • rho_obs (ndarray (n_f,)) – Apparent resistivity in Ohm.m.

  • phase_obs (ndarray (n_f,)) – Impedance phase in degrees.

name: str#
freq: ndarray#
rho_obs: ndarray#
phase_obs: ndarray#
class pycsamt.ai.inversion.SiteObs2D(name, freq, rho_te, phase_te, rho_tm, phase_tm)#

Bases: object

Observed 2-D MT profile data for one station.

Parameters:
  • name (str) – Station identifier.

  • freq (ndarray (n_f,)) – Frequencies in Hz, sorted high to low.

  • rho_te (ndarray (n_f,)) – TE apparent resistivity in Ohm.m (Zxy convention).

  • phase_te (ndarray (n_f,)) – TE phase in degrees.

  • rho_tm (ndarray (n_f,)) – TM apparent resistivity in Ohm.m (Zyx convention).

  • phase_tm (ndarray (n_f,)) – TM phase magnitude in degrees (absolute value stored).

name: str#
freq: ndarray#
rho_te: ndarray#
phase_te: ndarray#
rho_tm: ndarray#
phase_tm: ndarray#
pycsamt.ai.inversion.sites_to_obs_1d(sites, *, comp='xy', recursive=True, on_dup='replace', strict=False, verbose=0)#

Convert any site input to observed-data containers.

Parameters:
  • sites (Any) – Path, glob, EDIFile, EDICollection, Site, Sites, APISurvey, or iterable of any of the above.

  • comp ({'xy', 'yx', 'xx', 'yy'}, default 'xy') – Impedance tensor component to extract.

  • recursive (bool) – Forwarded to ensure_sites.

  • on_dup (str) – Forwarded to ensure_sites.

  • strict (bool) – Forwarded to ensure_sites.

  • verbose (int) – Forwarded to ensure_sites.

Returns:

One entry per valid station.

Return type:

list of SiteObs1D

Raises:

ValueError – If comp is invalid or no valid site data survives extraction.

pycsamt.ai.inversion.sites_to_obs_2d(sites, *, comp_te='xy', comp_tm='yx', recursive=True, on_dup='replace', strict=False, verbose=0)#

Convert any site input to 2-D observation containers.

Parameters:
  • sites (Any) – Path, glob, EDIFile, EDICollection, Site, Sites, APISurvey, or iterable of any of the above.

  • comp_te (str, default 'xy') – Tensor component treated as TE mode.

  • comp_tm (str, default 'yx') – Tensor component treated as TM mode.

  • recursive (bool) – Forwarded to ensure_sites.

  • on_dup (str) – Forwarded to ensure_sites.

  • strict (bool) – Forwarded to ensure_sites.

  • verbose (int) – Forwarded to ensure_sites.

Returns:

One entry per valid station, same order as input.

Return type:

list of SiteObs2D

Raises:

ValueError – If no valid station data is found.

pycsamt.ai.inversion.sites_to_features_1d(sites, *, comp='xy', n_freqs=32, freq_min=None, freq_max=None, recursive=True, on_dup='replace', strict=False, verbose=0)#

Convert any site input to an ML feature matrix.

The feature layout matches to_array() so the result is directly compatible with a fitted EMInverter1D:

X = [log10(rho_f1), ..., log10(rho_fn), phase_f1, ..., phase_fn]
Parameters:
  • sites (Any) – Same accepted types as sites_to_obs_1d().

  • comp (str, default 'xy') – Tensor component.

  • n_freqs (int, default 32) – Frequencies in the common interpolation grid.

  • freq_min (float or None) – Override the auto-detected frequency range.

  • freq_max (float or None) – Override the auto-detected frequency range.

  • recursive (bool)

  • on_dup (str)

  • strict (bool)

  • verbose (int)

Returns:

  • X (ndarray, shape (n_stations, 2*n_freqs)) – Block-format feature matrix (rho block first, then phase block).

  • freqs (ndarray, shape (n_freqs,)) – Common frequency grid in Hz.

  • names (list of str) – Station names in the same row order as X.

Return type:

tuple[ndarray, ndarray, list[str]]

pycsamt.ai.inversion.sites_to_panel_2d(sites, *, n_freqs=32, n_components=4, comp_te='xy', comp_tm='yx', freq_min=None, freq_max=None, recursive=True, on_dup='replace', strict=False, verbose=0)#

Build an EMInverter2D input panel from sites.

Returns:

  • X_panel (ndarray) – Shape (1, n_components, n_freqs, n_stations). Channel layout (for n_components=4): [log10(rho_te), phase_te, log10(rho_tm), phase_tm]. For n_components=2: TE channels only.

  • freqs (ndarray (n_freqs,)) – Common frequency grid in Hz (high to low).

  • names (list of str) – Station names in the same column order as X_panel.

Parameters:
Return type:

tuple[ndarray, ndarray, list[str]]

pycsamt.ai.inversion.sites_to_coords_3d(sites, *, station_spacing=500.0, recursive=True, on_dup='replace', verbose=0)#

Extract station (x, y) positions in metres.

Tries site.coords (lat, lon); falls back to a uniform grid when coordinates are missing or all zero.

Parameters:
  • sites (Any) – Same accepted types as sites_to_obs_1d().

  • station_spacing (float, default 500.0) – Spacing in metres used when coordinates are unavailable (uniform-grid fallback).

  • recursive (bool) – Forwarded to ensure_sites.

  • on_dup (str) – Forwarded to ensure_sites.

  • verbose (int) – Forwarded to ensure_sites.

Returns:

xy – Station (x, y) positions in metres.

Return type:

ndarray (n_stations, 2)

pycsamt.ai.inversion.obs_to_features_1d(obs, n_freqs=32, *, freq_min=None, freq_max=None)#

Build a feature matrix from a list of obs objects.

Works with both SiteObs1D (uses rho_obs, phase_obs) and SiteObs2D (uses rho_te, phase_te). Skips ensure_sites, so it is safe to call with pre-extracted obs lists.

Parameters:
Returns:

  • X (ndarray (n_stations, 2*n_freqs))

  • freqs (ndarray (n_freqs,))

  • names (list of str)

Return type:

tuple[ndarray, ndarray, list[str]]

pycsamt.ai.inversion.inv1d

End-to-end 1-D EM inversion workflow.

pycsamt.ai.inversion.inv2d

EMInverter2D — high-level U-Net–based 2-D MT inversion pipeline.

pycsamt.ai.inversion.inv3d

GCNInverter3D — graph-convolutional 3-D MT spatial inversion.

pycsamt.ai.inversion.ensemble

EnsembleInverter — deep ensemble for uncertainty-aware EM inversion.

pycsamt.ai.inversion.joint

JointInverter — multi-modal / multi-physics joint inversion.

pycsamt.ai.inversion.calibration

Calibrated uncertainty quantification for EM neural-network inverters.

pycsamt.ai.inversion.pinn1d

Physics-informed 1-D MT/CSAMT inversion.

pycsamt.ai.inversion.pinn2d

Physics-informed AI 2-D MT inversion.

pycsamt.ai.inversion.pinn3d

Physics-informed AI 3-D MT inversion.

pycsamt.ai.inversion.hybrid1d

Hybrid AI + physics 1-D MT/CSAMT inversion.

pycsamt.ai.inversion.hybrid2d

Hybrid AI + physics 2-D MT inversion.

pycsamt.ai.inversion.hybrid3d

Hybrid AI + physics quasi-3D MT inversion.

pycsamt.ai.inversion.duhi2d

Dual-Uncertainty Hybrid Inversion preparation for Occam2D.

pycsamt.ai.inversion.mapping2d

Geometry-aware mapping between regular AI and Occam2D grids.

pycsamt.ai.inversion.reliability2d

Observation-reliability factors for two-dimensional inversion.

pycsamt.ai.inversion.config

Configuration for pyCSAMT 1-D AI-based EM inversion.

pycsamt.ai.inversion.run_config

End-to-end run configuration for pyCSAMT AI inversion.

pycsamt.ai.inversion.schema

Parameter-validation schemas for AI inversion interfaces.