pycsamt.tdem.transform#
TEM time-domain → frequency-domain transforms.
Three loop configurations are fully supported:
- Central / coincident loop (
offset = 0) Receiver at the centre of the transmitter loop. Uses the Ward & Hohmann (1988) late-time formula directly:
\[\rho_a(t) = \left( \frac{M\,\mu_0^{5/2}} {10\sqrt{\pi}\,|\partial B_z/\partial t|\,t^{5/2}} \right)^{2/3}\]- In-loop / large-loop (
0 < offset < inner_radius) Receiver is inside the transmitter loop but not at the centre. A Biot-Savart static-field correction factor \(\eta = H_z^{\rm static}(r_x, r_y)\,/\,H_z^{\rm static}(0,0)\) is applied to the effective transmitter moment:
\[\rho_a(t) = \left( \frac{M\,\eta\,\mu_0^{5/2}} {10\sqrt{\pi}\,|\partial B_z/\partial t|\,t^{5/2}} \right)^{2/3}\]The Biot-Savart integral is evaluated analytically for rectangular / square loops and numerically for circular loops.
- Offset / separated loop (
offset ≥ inner_radius) Transmitter and receiver are separated by horizontal distance d. Uses the Ward & Hohmann (1988) offset-dipole formula:
\[\rho_a(t) = \left( \frac{M\,\mu_0^{5/2}} {20\sqrt{\pi}\,|\partial B_z/\partial t|\,d^3\,t^{5/2}} \right)^{2/3}\]LateTimeTransformFast, approximate method — standard industry approach.
FourierTransformRigorous Fourier cosine transform with Kramers-Kronig reconstruction. Valid at all time gates; first-order waveform deconvolution for all waveform types (Fitterman & Stewart 1986).
TEMtoEDIHigh-level dispatcher →
EDICollection.
Classes
|
Rigorous TDEM → MT impedance via numerical Fourier cosine transform and Kramers-Kronig reconstruction (Meju 1996; Christensen 1990). |
|
Convert TEM soundings to frequency-domain apparent impedance using the late-time apparent-resistivity approximation. |
|
Convert one or more TEM soundings to a |
- class pycsamt.tdem.transform.LateTimeTransform(freq_convention='skin_depth', phase_mode='homogeneous', drop_nan=True, loop_geometry_correction=True, in_loop_n_iter=3, waveform_correction=True, verbose=0, logger=None)[source]#
Bases:
PyCSAMTObjectConvert TEM soundings to frequency-domain apparent impedance using the late-time apparent-resistivity approximation.
Three loop configurations are handled automatically based on the
offsetandloop_dimsattributes of eachTEMSounding:central / coincident loop (
offset == 0): standard Ward & Hohmann (1988) formula.in-loop off-centre receiver (
0 < offset < inner_radius): Biot-Savart geometric correction applied to the effective moment.separated / offset loop (
offset ≥ inner_radius): offset dipole formula with \(d^3\) denominator.
- Parameters:
freq_convention (str) – Time-to-pseudo-frequency mapping:
"skin_depth"(default) or"diffusion".phase_mode (str) – MT phase estimate:
"homogeneous"(default, 45°) or"weidelt"(dispersion relation).drop_nan (bool) – Remove gates with undefined ρ_a. Default
True.loop_geometry_correction (bool) – Apply the Biot-Savart in-loop correction when the receiver is off-centre. Set
Falseto use the central-loop formula for all configurations (legacy behaviour). DefaultTrue.in_loop_n_iter (int) – Number of iterations for the time-dependent geometry correction (see
_rho_a_in_loop()).0uses the static Biot-Savart approximation only. Default 3.waveform_correction (bool) – Apply first-order waveform deconvolution when
sounding.waveformis set (Fitterman & Stewart 1986). SupportsRampWaveform(analytic),HalfSineWaveform, andCustomWaveform(both numerical).SquareWaveformis a no-op (ideal step-off). DefaultTrue.verbose (int)
logger (object | None)
Examples
Central-loop sounding:
>>> import numpy as np >>> from pycsamt.tdem import TEMSounding >>> from pycsamt.tdem.transform import LateTimeTransform >>> t = np.logspace(-5, -2, 30) >>> M = 8.0 * 100.0 ** 2 >>> MU0_loc = 4e-7 * np.pi >>> dBdt = M * MU0_loc**2.5 / (10*np.sqrt(np.pi)*100.**1.5*t**2.5) >>> snd = TEMSounding(t, dBdt, current=8.0, tx_area=100.**2) >>> tr = LateTimeTransform() >>> result = tr.transform(snd) >>> result["freq"].shape (30,)
Offset-loop sounding:
>>> snd_off = TEMSounding(t, dBdt, current=8.0, tx_area=100.**2, ... offset=500.0, loop_dims=(100.0,)) >>> result_off = tr.transform(snd_off)
- transform(sounding)[source]#
Transform one
TEMSoundingto frequency-domain arrays.- Parameters:
sounding (TEMSounding)
- Returns:
freq,Z,Z_err,rho_a,phase_xy,station_name,x,y,elevation,loop_config.- Return type:
dict with keys
- class pycsamt.tdem.transform.FourierTransform(n_freq=50, freq_min=None, freq_max=None, n_aux=200, n_interp=400, waveform_correction=True, drop_nan=True, verbose=0, logger=None)[source]#
Bases:
PyCSAMTObjectRigorous TDEM → MT impedance via numerical Fourier cosine transform and Kramers-Kronig reconstruction (Meju 1996; Christensen 1990).
The 1-D step-off forward relation gives:
\[\frac{\partial B_z}{\partial t}(t) = \frac{2M}{\pi} \int_0^\infty \omega\,\mathrm{Im}[K(\omega)]\,\cos(\omega t)\,\mathrm{d}\omega\]Inverting by the half-range cosine transform:
\[\mathrm{Im}[K(\omega)] = \frac{1}{M\omega} \int_0^\infty \frac{\partial B_z}{\partial t}(t) \cos(\omega t)\,\mathrm{d}t\]The real part \(\mathrm{Re}[K(\omega)]\) is recovered via the Kramers-Kronig relation, then:
\[Z(\omega) = i\omega\mu_0 K(\omega),\qquad \rho_a(\omega) = \omega\mu_0\,|K(\omega)|^2\]- Parameters:
n_freq (int) – Number of output frequency points. Default 50.
freq_min (float or None) – Minimum output frequency [Hz] (
None→ auto from time gates).freq_max (float or None) – Maximum output frequency [Hz] (
None→ auto from time gates).n_aux (int) – Auxiliary frequency points for the K-K integral (≥ 4 × n_freq). Default 200.
n_interp (int) – Interpolation grid for the cosine transform (0 = disabled). Default 400.
waveform_correction (bool) – Apply first-order waveform deconvolution (Fitterman & Stewart 1986) when
sounding.waveformis set. Supports all waveform types via_apply_waveform_correction(). DefaultTrue.drop_nan (bool) – Remove output frequencies with undefined ρ_a. Default
True.verbose (int)
logger (object | None)
Examples
>>> import numpy as np >>> from pycsamt.tdem import TEMSounding >>> from pycsamt.tdem.transform import FourierTransform >>> t = np.logspace(-5, -2, 40) >>> MU0_v = 4e-7 * np.pi; rho0 = 100.0; M = 8.0 * 1e4 >>> dBdt = M * MU0_v**2.5 / (10*np.sqrt(np.pi)*rho0**1.5*t**2.5) >>> snd = TEMSounding(t, dBdt, current=8.0, tx_area=1e4) >>> res = FourierTransform(n_freq=30).transform(snd) >>> np.isfinite(res["rho_a"]).all() True
References
- transform(sounding)[source]#
Transform one
TEMSoundingto frequency-domain arrays.Returns the same dict structure as
LateTimeTransform.transform()plus key"method": "fourier".- Parameters:
sounding (TEMSounding)
- Return type:
- class pycsamt.tdem.transform.TEMtoEDI(method='late_time', freq_convention='skin_depth', phase_mode='homogeneous', loop_geometry_correction=True, out_dir='edi_out/tem', verbose=0, logger=None)[source]#
Bases:
PyCSAMTObjectConvert one or more TEM soundings to a
EDICollection.Wraps either
LateTimeTransform(default) orFourierTransformand writes one synthetic EDI file per sounding intoout_dir.All three loop configurations (central, in-loop, offset) are handled automatically via
LateTimeTransform.- Parameters:
method (str) –
"late_time"(default) or"fourier".freq_convention (str) –
"skin_depth"(default) or"diffusion".phase_mode (str) –
"homogeneous"(default) or"weidelt".loop_geometry_correction (bool) – Forward to
LateTimeTransform. DefaultTrue.out_dir (str or Path) – EDI output directory. Default
"edi_out/tem".verbose (int) – Verbosity. Default 0.
logger (object | None)
Examples
Central-loop:
>>> from pycsamt.tdem import TEMSounding, TEMtoEDI >>> import numpy as np >>> t = np.logspace(-5, -2, 25) >>> dBdt = 5e-5 * t ** (-5.0 / 2.0) >>> snd = TEMSounding(t, dBdt, current=8.0, tx_area=1e4, ... station_name="S01") >>> conv = TEMtoEDI(method="late_time") >>> coll = conv.transform(snd)
Offset (separated) loop — just set
offset:>>> snd_off = TEMSounding(t, dBdt, current=8.0, tx_area=1e4, ... offset=500.0, loop_dims=(100.0,), ... station_name="S01") >>> coll_off = conv.transform(snd_off)
- transform(sounding)[source]#
- Parameters:
sounding (TEMSounding)
- transform_many(soundings)[source]#
- Parameters:
soundings (Sequence[TEMSounding])
- pycsamt.tdem.transform.build_adjacency(coords, radius, *, self_loops=True, normalise=True)#
Build a symmetric adjacency matrix from 2-D station coordinates.
- Parameters:
coords (ndarray, shape (n_stations, 2)) – Station (x, y) positions in any consistent unit (metres, degrees).
radius (float) – Maximum inter-station distance for an edge to exist. Uses the same unit as coords.
self_loops (bool) – If
True(default), add \(\tilde{A} = A + I\).normalise (bool) – If
True(default), apply symmetric normalisation \(\tilde{D}^{-1/2}\tilde{A}\tilde{D}^{-1/2}\).
- Returns:
A – Adjacency matrix, optionally normalised.
- Return type:
ndarray, shape (n_stations, n_stations), float32