pycsamt.ai.inversion.pinn2d#

Physics-informed AI 2-D MT inversion.

PINNInverter2D optimises a joint 2-D resistivity section by minimising a physics-informed loss that combines per-station 1-D MT data misfits with lateral and vertical smoothness penalties.

The earth model is parameterised as \(\log_{10}(\rho)\) tensors with shape (n_stations, n_layers) for resistivity and (n_stations, n_layers-1) for layer thicknesses. All parameters are optimised simultaneously via Adam so the lateral-smoothness term couples adjacent stations.

Forward model#

The differentiable Wait (1954) MT-1D recursion (implemented in pinn1d) is applied in batch over all stations on a shared frequency grid.

Loss function#

\[\mathcal{L} = \underbrace{ \frac{1}{N_v} \sum_{s,f}^{\text{valid}} \left[ \left( \log_{10}\rho_a^{\rm pred} - \log_{10}\rho_a^{\rm obs} \right)^2 + \left( \frac{\phi^{\rm pred} - \phi^{\rm obs}}{90} \right)^2 \right] }_{\text{data}} + \lambda_z \underbrace{ \frac{1}{S(L-1)} \sum_{s,k} (m_{s,k+1} - m_{s,k})^2 }_{\text{vertical smooth}} + \lambda_x \underbrace{ \frac{1}{(S-1)L} \sum_{s,k} (m_{s+1,k} - m_{s,k})^2 }_{\text{lateral smooth}}\]

where \(m_{s,k} = \log_{10}\rho_{s,k}\), \(N_v\) is the number of valid (non-NaN) station-frequency pairs, \(S\) is the number of stations, and \(L\) is the number of layers.

Example

>>> from pycsamt.ai.inversion import PINNInverter2D
>>> inv = PINNInverter2D(
...     "edi/profile1/",
...     n_layers=12,
...     depth_max=3000.0,
...     epochs=300,
... )
>>> inv.fit()
PINNInverter2D(n_stations=20, fitted)
>>> section = inv.resistivity_section()
>>> df = inv.residuals()

Classes

PINNInverter2D(sites, *[, n_layers, ...])

Physics-informed 2-D MT inversion.

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

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

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

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

Return layer thicknesses in metres.

Return type:

ndarray (n_layers-1, n_stations)

convergence_curve()[source]

Return Adam loss history.

Returns:

Columns: epoch, loss.

Return type:

pandas.DataFrame

residuals()[source]

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

Station names in profile order.

property n_sites: int[source]

Number of loaded stations.