AEM Inversion & Forward Modelling
Documents the inversion and forward modelling processes for airborne electromagnetics (TEM), powered by a custom fork of SimPEG.
Source Files
docker/base-runner/aem_processes/inversion_process.py— Inversion process wrapperdocker/base-runner/aem_processes/forward_process.py— Forward modelling process wrapperSimPEG/electromagnetics/utils/static_instrument/— Custom instrument classesdocker/base-runner/aem_processes/directives.py— Custom SimPEG directives (reporting, iteration output)
System Description Architecture
The SimPEG/electromagnetics/utils/static_instrument/ directory defines a hierarchy of instrument descriptions for moving EM platforms (AEM, TTEM). The key design principle is that the geometric configuration of transmitter(s) and receiver(s) is independent of the data — only their absolute positions change per-sounding.
XYZSystem (base class)
Source: base.py
Base class for all AEM system descriptions. Core responsibilities:
- System instantiation: Takes a
libaarhusxyz.XYZobject and optional keyword parameters (which override class-level defaults via__getattribute__) - Filtering: Applies
sounding_filter(row-wise) andgate_filter(column-wise) viaxyzfilter.FilteredXYZ - Survey construction:
make_survey()iterates over all soundings, callingmake_system()to create SimPEG sources for each - Inversion assembly: Chain of
make_thicknesses()→make_misfit()→make_regularization()→make_optimizer()→make_inversion() - Model <-> XYZ conversion:
inverted_model_to_xyz()andforward_data_to_xyz()convert between SimPEG model vectors and libaarhusxyz format
SingleMomentTEMXYZSystem
Source: single.py
A simple single-moment system suitable for synthetic data:
- One
CircularLooptransmitter with configurable area and current - Default perfect
StepOffWaveform; optional customPiecewiseLinearWaveform - One
PointMagneticFluxTimeDerivativereceiver at the transmitter location dipole_moments = [area × i_max]
DualMomentTEMXYZSystem
Source: dual.py
Dual-moment system describing SkyTEM instruments. Cannot be instantiated directly — use DualMomentTEMXYZSystem.load_gex(gex) to generate a subclass from a GEX instrument file.
Key features:
- Two sources: Low Moment (Channel 1) and High Moment (Channel 2), each as a MagDipole with separate PiecewiseLinearWaveform from the GEX file
- Receiver offset: Rx position relative to Tx from gex.General['RxCoilPosition']
- Tilt correction: correct_tilt_pitch_for1Dinv() — same (cos·cos)² factor as the pipeline
- Gate factor scaling: Data multiplied by gex.ChannelN['GateFactor'] (Aarhus Workbench convention)
- Automatic sounding filter: Excludes soundings where no usable gates remain in either channel
- Gate filtering: Configurable gate ranges via gate_filter__start_lm/end_lm and gate_filter__start_hm/end_hm
Inversion Formulation
Forward Model
Uses SimPEG.electromagnetics.time_domain.Simulation1DLayeredStitched — a 1D layered earth EM solution computed independently per sounding, stitched into a pseudo-2D model. For each sounding, the solver computes the vertical magnetic field response of a layered half-space to a magnetic dipole source with an arbitrary waveform.
The 1D solution uses the Hankel transform for the frequency-domain solution (via digital filter coefficients) and the Fourier transform (via cosine/sine transforms) to convert to time domain. Layer conductivities are transformed using maps.ExpMap (σ = exp(m)).
Solver options:
- Pardiso (direct sparse, multi-threaded) — requires pymatsolver.PardisoSolver
- Default spLU (SuperLU) — fallback
Parallelism: Configurable n_cpu threads for sounding-parallel computation.
Model Discretization
Layer thicknesses: Three options via startmodel__thicknesses_type:
1. Log-spaced (default): 30 layers, 1 m → 400 m depth range. build_log_spaced_layer_thick() iteratively adjusts the last layer to match the target depth exactly.
2. Geometric: Constant geometric factor (default 1.15309) between successive layers.
3. Time-based: Derived from gate times and background conductivity using get_vertical_discretization_time().
The model vector has n_param = n_layers × n_soundings parameters (plus one half-space). Resistivity is log-transformed: σ = exp(m), initialized to 1/startmodel__res (default 100 Ω·m).
Data Misfit
Weighted L2 norm with weights = 1 / uncertainty:
φ_d = ||W_d · (d_obs - d_pred)||²
where the per-datum uncertainty combines:
- STD from stacking (STD_Ch## values, capped to minimum uncertainties__std_data = 3%)
- Time-dependent noise floor: noise_level_1ms × (t × 1000)^(noise_exponent) / moment
- W_d = diag(1 / (std × |data| + noise))
Regularization
LaterallyConstrainedInversion (LCI) using two-mesh approach:
- Horizontal mesh:
SimplexMeshbuilt from Delaunay triangulation of sounding (x, y) coordinates - Vertical mesh: 1D tensor mesh from layer thicknesses
- Regularization mesh: Cartesian product of horizontal × vertical meshes
-
Objective:
φ_m = α_s · ||W_s · (m - m_ref)||² + α_r · ||W_r · (G_r · m)||² + α_z · ||W_z · (G_z · m)||² -
α_s(smallness): Penalizes deviation from reference model (default 10⁻⁴) α_r(radial smoothness): Penalizes lateral roughness between adjacent soundings (default 1)α_z(vertical smoothness): Penalizes vertical roughness between adjacent layers (default 1)
Reference model: m_ref = 1/100 S/m (uniform 100 Ω·m half-space).
Reference: Auken et al. (2002) for laterally constrained inversion theory.
Inversion Algorithm
- Beta estimation:
BetaEstimate_ByEig— computes initial regularization weight from eigenvalue spectrum ofJᵀJ - Beta schedule:
BetaSchedule(coolingFactor=2, coolingRate=1)— halves beta every iteration - Target misfit: Stopping criterion when
φ_d ≈ N_data - Optimization:
InexactGaussNewton— inexact Newton with conjugate-gradient inner iterations (max 20 CG per step, max 40 GN iterations total)
Sparse Model (IRLS)
Optional iterative reweighted least squares (IRLS) produces a "focused" model with sharper boundaries:
IRLS iteration: update weights W_R^(k+1) = diag(1 / (|m_j - m_ref|^(2-p/2) + ε))
where p ≈ 1 gives blocky/sparse solutions. Runs after L2 convergence.
References: Last & Kubik (1983), Farquharson & Oldenburg (1998).
Forward Modelling
Source: forward_process.py
Takes a resistivity model (libaarhusxyz format with resistivity, dep_top, dep_bot) and computes synthetic dB/dt responses using the same system description and Simulation1DLayeredStitched.
The model is passed directly as log-conductivity: m = ln(1/resistivity). The output is a libaarhusxyz dataset with:
- Synthetic Gate_Ch## data
- InUse_Ch## flags set to 1 where data is finite
- STD_Ch## set to GEX UniformDataSTD
- Current_Ch## filled from GEX approximate current
- Tilt columns defaulted to zero
Result Conversion
inverted_model_to_xyz()
Converts the inversion result back to libaarhusxyz format:
resistivity = 1 / exp(model.reshape(n_soundings, n_layers))
forward_data_to_xyz()
Converts predicted data to libaarhusxyz format, computing per-sounding residuals and RMS misfit:
resdata[i] = sqrt(mean(derr[i, :]²)) where derr = (d_obs - d_pred) × W_d
Custom Directives
Source: directives.py
- ReportingDirective: Logs iteration progress (phi_d, phi_m, beta, CG iterations)
- SaveOutputEveryIteration: Saves intermediate model and synthetic data at every GN iteration, enabling convergence monitoring
References
- Auken, E., Christiansen, A. V., Westergaard, J. H., Kirkegaard, C., Foged, N., & Viezzoli, A. (2009). "An integrated processing scheme for high-resolution airborne electromagnetic surveys, the SkyTEM system." Exploration Geophysics, 40(2), 184-192. DOI: 10.1071/eg08128
- Auken, E., Foged, N., & Sørensen, K. I. (2002). "Model recognition by 1-D laterally constrained inversion of resistivity data." Geophysics, 67(5), 1468-1475. DOI: 10.1190/1.1512750
- Cockett, R., Kang, S., Heagy, L. J., Pidlisecky, A., & Oldenburg, D. W. (2015). "SimPEG: An open source framework for simulation and gradient based parameter estimation in geophysical applications." Computers & Geosciences, 85, 142-154. DOI: 10.1016/j.cageo.2015.09.015
- Farquharson, C. G., & Oldenburg, D. W. (1998). "Non-linear inversion using general measures of data misfit and model structure." Geophysical Journal International, 134(1), 213-227. DOI: 10.1046/j.1365-246x.1998.00555.x