pycsamt.ai.inversion.hybrid3d#

Hybrid AI + physics quasi-3D MT inversion.

HybridInverter3D combines the spatially coherent output of a pre-trained GCNInverter3D with the physics-informed refinement of PINNInverter3D.

Stage 1 — GCN initial model

The graph-convolutional network produces per-station 1-D models by message-passing over the station network:

X, A --> GCNInverter3D --> params_0

where params_0 has shape (n_stations, 2*n_layers-1) in log10 scale.

Stage 2 — Physics refinement

Starting from params_0, Adam minimises the same loss used by PINNInverter3D:

\[\mathcal{L} = \mathcal{L}_{\rm data} + \lambda_z \mathcal{L}_{\rm vert} + \lambda_g \mathcal{L}_{\rm spatial}\]

Example

>>> from pycsamt.ai.inversion import (
...     GCNInverter3D,
...     HybridInverter3D,
... )
>>> gcn = GCNInverter3D.load(
...     "checkpoints/gcn3d.npz"
... )
>>> inv = HybridInverter3D(
...     "edi/survey/",
...     ai_inverter=gcn,
...     epochs=150,
...     graph_weight=0.003,
... )
>>> inv.fit()
HybridInverter3D(n_stations=25, fitted)
>>> vol = inv.resistivity_volume()
>>> s1 = inv.stage1_volume()

Classes

HybridInverter3D(sites, ai_inverter, *[, ...])

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

class pycsamt.ai.inversion.hybrid3d.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)[source]

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)[source]

Run both inversion stages.

Return type:

self

Parameters:
resistivity_volume(*, as_log10=True)[source]

Return the Stage-2 quasi-3D volume.

Return type:

ndarray (n_layers, n_stations)

Parameters:

as_log10 (bool)

thickness_volume()[source]

Return Stage-2 layer thicknesses [m].

Return type:

ndarray (n_layers-1, n_stations)

stage1_volume(*, as_log10=True)[source]

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

Return type:

ndarray (n_layers, n_stations)

Parameters:

as_log10 (bool)

convergence_curve()[source]

Return Stage-2 Adam loss history.

Returns:

Columns: epoch, loss.

Return type:

pandas.DataFrame

residuals(stage=2)[source]

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()[source]

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

Return type:

ndarray

adjacency()[source]

Return the station adjacency matrix.

Return type:

ndarray

property stations: list[str][source]

Station names in order.

property n_sites: int[source]

Number of loaded stations.