2.14.2.1. pycsamt.emtools.afmag#
AFMAG-specific processing, diagnostics, and plotting.
AFMAG (Audio-Frequency Magnetics) is a passive, magnetic-field-only
method: there is no electric-field channel, so the usual impedance
tensor Z is not part of its data model. This module’s own
“Tilt-angle diagnostics” and “Motion-coupling QC” sections below are
built, like pycsamt.emtools.tf, entirely on Site.tipper
(the complex (Tzx, Tzy) pair, Hz = Tzx*Hx + Tzy*Hy) — they
never touch Site.z, and apply equally to ground data and to a
ZTEM-style airborne tipper (see pycsamt.airborne.ztem, which
documents that equivalence explicitly).
That is deliberately not a claim that every real AFMAG measurement
is a (Tzx, Tzy) tipper pair, though. pycsamt.airborne.afmag
keeps two genuinely different response families, neither of them
tipper-shaped:
the historical two-coil comparator (Ward 1959) reports a bare scalar tilt/deflection angle – one real number per frequency, no input/output channels, no polarization-ellipse decomposition possible at all;
modern tensor AFMAG/AirMt reports a complex
(nf, 3, 2)interstation magnetic transfer function (ground-reference Hx,Hy -> airborne Hx,Hy,Hz) plus a derived rotation-invariant amplification parameter – six components, not two.
Section “AFMAG-family tilt diagnostics” below handles those two real
shapes explicitly and separately (original_afmag_tilt_table(),
airmt_tilt_angles()), reading them via
AirborneSite’s
afmag_tilt_deg/
interstation_tensor
rather than forcing either into the tipper-shaped functions above.
Three genuinely different things live here side by side:
Sections “Tilt-angle diagnostics” and “Motion-coupling QC” are ordinary
emtoolsfunctions built on the ZTEM-style tipper: they takesites(coerced throughensure_any_sites(), likepycsamt.emtools.ztem) and either return aSites-or-AirborneSitescontainer (the one mutating function,flag_motion_susceptible_band()) or a tidypandas.DataFrame(matching the convention already used bypsd_table(),coherence_table(), and friends — not everyemtools“processing” function returns a container; only the data-mutating ones do).Section “AFMAG-family tilt diagnostics” is built on the two real AFMAG-family shapes described above instead, and therefore only ever accepts
AirborneSites(there is no ground-EDI equivalent of either shape).Section “Motion-coupling physics” implements the rotation-matrix / Euler-angle motion-induced-noise method of Liu et al. (2018) directly from their Eq. 1-14. These functions are deliberately not
Sites-based: they operate on raw attitude (yaw/pitch/roll) time series and geomagnetic field geometry, neither of whichSitecarries (it is strictly frequency-domain, with no time axis and no attitude field — the only attitude container in pyCSAMT today isNavigationTrack, which belongs to the unrelatedpycsamt.airborneline/dataset model). They are included here, as pure reusable physics, because the paper’s actual deliverable is exactly this math; forcing it ontoSiteswould require either fabricating time-series storage that does not exist or silently doing nothing. The one bridge between the two halves ismotion_susceptibility_table()/flag_motion_susceptible_band(), which use the Section-1 math together with a nominal attitude-amplitude envelope to flag which stations are most exposed to motion noise in their low-frequency band — the paper’s own finding is that this noise concentrates at low frequency and depends on attitude amplitude and geomagnetic geometry, not on a raw time series.
No apparent-resistivity/conductivity formula is derived from tilt angle here: unlike full MT, classical AFMAG tilt data has no simple closed form for that (nothing in Ward 1959 or Liu et al. 2018 provides one), so none is invented.
References
Ward, S. H. (1959). AFMAG – Airborne and ground. Geophysics, 24(4), 761-787.
Liu, F., Huang, L., Pang, Y., Shi, Z., Xiao, P., & Fang, G. (2018). Airborne AFMAG method motion-induced noise simulation and suppression. Journal of Applied Geophysics, 158, 129-138.
Functions
|
Return per-station, per-frequency AFMAG tilt angles as a table. |
|
Per-station, per-frequency tensor AFMAG/AirMt tilt angles. |
|
Return the coil-normal unit vector \(N_L\) in the local frame. |
|
Return the motion-noise-corrected signal (Liu et al. 2018 Sec. |
|
Return the ZYX "airline convention" attitude rotation matrix. |
|
Flag/mask a station's low-frequency tipper band as motion-suspect. |
|
Return the local geomagnetic field unit vector \(B_E\). |
|
Return \(\theta(t)\) in degrees. |
|
Return \(\cos\theta(t)\), the coil/field coupling factor. |
|
Score each station's exposure to motion-induced noise. |
|
Ward (1959)-style dual-frequency conductor diagnostics. |
|
Per-station, per-frequency original comparator AFMAG tilt table. |
|
Plot before/after/delta AFMAG tilt pseudosections. |
|
Polar view of AFMAG tilt: azimuth and magnitude vs. period. |
|
Plot the classic AFMAG flight-line tilt-angle profile. |
|
Plot an AFMAG tilt-angle pseudosection (station x log-period). |
|
Plot the tensor AFMAG/AirMt flight-line tilt-angle profile. |
|
Plot an AirMt tilt-angle pseudosection (station x log-period). |
|
Plot \(\theta(t)\) and \(\cos\theta(t)\) for attitude data. |
|
Map each station's motion-noise susceptibility score. |
|
Ward (1959) Fig. |
|
Plot the original comparator AFMAG flight-line tilt profile. |
|
Return the simulated motion-induced noise voltage \(V(t)\). |
- pycsamt.emtools.afmag.euler_rotation_matrix(yaw, pitch, roll, *, degrees=True)[source]
Return the ZYX “airline convention” attitude rotation matrix.
Implements Liu et al. (2018) Eq. 3-4 (equivalently their appendix A1-A6): the standard aerospace Tait-Bryan rotation \(R_{LS} = R_z(\psi)\,R_y(\theta)\,R_x(\phi)\) from the sensor/body frame (S) to the local, fixed east-north-up frame (L), where \(\psi\) = yaw (about \(Z_S\)), \(\theta\) = pitch (about the once-rotated \(Y_S'\)), \(\phi\) = roll (about the twice-rotated \(X_S''\)).
- Parameters:
yaw (array-like) – Attitude angles, broadcastable to a common shape
(...,). Degrees by default; radians whendegrees=False.pitch (array-like) – Attitude angles, broadcastable to a common shape
(...,). Degrees by default; radians whendegrees=False.roll (array-like) – Attitude angles, broadcastable to a common shape
(...,). Degrees by default; radians whendegrees=False.degrees (bool, default True) – Whether yaw, pitch, roll are in degrees.
- Returns:
Rotation matrix
R_LSfor each broadcast attitude sample;x_L = R_LS @ x_S.- Return type:
ndarray of shape (…, 3, 3)
Notes
R_LSis a proper rotation: it is always orthogonal (R_LS.T == inv(R_LS)) with determinant+1, for any input angles. Identity input (yaw=pitch=roll=0) returns the identity matrix.Examples
>>> from pycsamt.emtools.afmag import euler_rotation_matrix >>> R = euler_rotation_matrix(0.0, 0.0, 0.0) >>> np.allclose(R, np.eye(3)) True
- pycsamt.emtools.afmag.geomagnetic_field_direction(inclination, declination, *, degrees=True)[source]
Return the local geomagnetic field unit vector \(B_E\).
In the (east, north, up) local frame used throughout this module (Liu et al. 2018 Eq. 8-9), with inclination the dip angle below horizontal (positive downward) and declination the horizontal bearing from geographic north toward east:
\[\hat{B}_E = (\cos I \sin D,\ \cos I \cos D,\ -\sin I)\]- Parameters:
inclination (array-like) – Geomagnetic inclination and declination, broadcastable to a common shape
(...,). Degrees by default.declination (array-like) – Geomagnetic inclination and declination, broadcastable to a common shape
(...,). Degrees by default.degrees (bool, default True) – Whether inclination and declination are in degrees.
- Returns:
Unit vector(s)
(east, north, up).- Return type:
ndarray of shape (…, 3)
Examples
>>> from pycsamt.emtools.afmag import geomagnetic_field_direction >>> np.round(geomagnetic_field_direction(90.0, 0.0), 6) # straight down array([ 0., 0., -1.])
- pycsamt.emtools.afmag.coil_normal_direction(yaw, pitch, roll, *, degrees=True)[source]
Return the coil-normal unit vector \(N_L\) in the local frame.
Implements Liu et al. (2018) Eq. 10-12 for a single z-axis coil (\(N_S = (0, 0, 1)^T\), “we use z-axis hereinafter for the sake of simplification”): \(N_L = R_{LS}\,N_S\), i.e. the third column of
euler_rotation_matrix().- Parameters:
yaw (array-like) – Forwarded to
euler_rotation_matrix().pitch (array-like) – Forwarded to
euler_rotation_matrix().roll (array-like) – Forwarded to
euler_rotation_matrix().degrees (bool, default True) – Forwarded to
euler_rotation_matrix().
- Returns:
Unit vector(s)
(east, north, up). Identity attitude (yaw=pitch=roll=0) returns straight up,(0, 0, 1).- Return type:
ndarray of shape (…, 3)
- pycsamt.emtools.afmag.motion_coupling_cosine(yaw, pitch, roll, inclination, declination, *, degrees=True)[source]
Return \(\cos\theta(t)\), the coil/field coupling factor.
Implements Liu et al. (2018) Eq. 13-14: \(\cos\theta(t) = \hat{B}_E \cdot N_L(t)\), the cosine of the angle between the (fixed) geomagnetic field direction and the (time-varying) coil-normal direction. This is the quantity whose time derivative drives the motion-induced noise voltage; see
simulate_motion_induced_voltage().- Parameters:
yaw (array-like) – Attitude time series, broadcastable to a common shape.
pitch (array-like) – Attitude time series, broadcastable to a common shape.
roll (array-like) – Attitude time series, broadcastable to a common shape.
inclination (array-like) – Geomagnetic field geometry, broadcastable against the attitude shape (a single site typically supplies scalars here while attitude varies over time).
declination (array-like) – Geomagnetic field geometry, broadcastable against the attitude shape (a single site typically supplies scalars here while attitude varies over time).
degrees (bool, default True) – Whether all five angle inputs are in degrees.
- Returns:
\(\cos\theta(t)\), broadcast shape of the inputs, values in
[-1, 1].- Return type:
ndarray
Examples
At identity attitude the coil normal points straight up; at 90 degrees inclination (a magnetic pole) the field points straight down, so the two are exactly anti-aligned and \(\cos\theta = -1\):
>>> from pycsamt.emtools.afmag import motion_coupling_cosine >>> motion_coupling_cosine(0.0, 0.0, 0.0, 90.0, 0.0) -1.0
- pycsamt.emtools.afmag.motion_coupling_angle(yaw, pitch, roll, inclination, declination, *, degrees=True)[source]
Return \(\theta(t)\) in degrees.
See
motion_coupling_cosine()for the underlying computation.
- pycsamt.emtools.afmag.simulate_motion_induced_voltage(cos_theta, *, dt, gain=1.0, axis=-1)[source]
Return the simulated motion-induced noise voltage \(V(t)\).
Implements Liu et al. (2018) Eq. 2, \(V(t) \propto -\,d(\cos\theta(t))/dt\), via a centred finite difference (
numpy.gradient()).- Parameters:
cos_theta (array-like) – \(\cos\theta(t)\), typically from
motion_coupling_cosine().dt (float) – Sample interval in seconds along axis.
gain (float, default 1.0) – Bundles the instrument-specific scale factor \(S \cdot N \cdot |B_E|\) (coil area x turns x field magnitude) from Eq. 2. The paper calibrates this from the instrument rather than deriving it analytically, so it is left as an explicit, user-supplied factor rather than a fabricated constant.
axis (int, default -1) – Time axis of cos_theta.
- Returns:
Simulated noise voltage, same shape as cos_theta.
- Return type:
ndarray
- pycsamt.emtools.afmag.correct_motion_induced_noise(measured, predicted_noise)[source]
Return the motion-noise-corrected signal (Liu et al. 2018 Sec. 5).
The corrected signal is the measured signal minus the predicted noise voltage from
simulate_motion_induced_voltage(). This function exists to give that one-line step an obvious, documented name rather than leaving every caller to subtract the two arrays themselves.- Parameters:
measured (array-like) – Raw movement-system signal.
predicted_noise (array-like) – Predicted motion-induced noise, same shape as measured.
- Returns:
measured - predicted_noise.- Return type:
ndarray
- Raises:
ValueError – If measured and predicted_noise have different shapes.
- pycsamt.emtools.afmag.afmag_tilt_angles(sites, *, recursive=True, on_dup='replace', strict=False, verbose=0)[source]
Return per-station, per-frequency AFMAG tilt angles as a table.
The classical AFMAG comparator readout is, in modern tipper terms, \(\arctan|T|\) decomposed into in-phase (“real”) and quadrature (“imag”) components (Ward 1959) — the same real/imag split
pycsamt.emtools.tf’s induction-arrow functions already use (plot_induction_arrows()), computed directly fromtipper. No electric field or apparent-resistivity quantity is involved or derived.- Parameters:
sites (Sites-like or AirborneSites-like) – Anything accepted by
ensure_any_sites(). Ground tipper-shaped input goes throughSitesas before; airborne input goes throughAirborneSitesbut only finds data here when it carries a ZTEM-style tipper transfer function – seeairmt_tilt_angles()/original_afmag_tilt_table()for the real AFMAG-family shapes instead.recursive (bool) – Forwarded to
ensure_any_sites().on_dup (str) – Forwarded to
ensure_any_sites().strict (bool) – Forwarded to
ensure_any_sites().verbose (int) – Forwarded to
ensure_any_sites().
- Returns:
Columns:
station,freq,period,tilt_real_deg,tilt_real_azimuth_deg,tilt_imag_deg,tilt_imag_azimuth_deg,tilt_resultant_deg. Stations with no tipper are omitted, not filled with a fabricated zero.- Return type:
- pycsamt.emtools.afmag.airmt_tilt_angles(sites, *, recursive=True, on_dup='replace', strict=False, verbose=0)[source]
Per-station, per-frequency tensor AFMAG/AirMt tilt angles.
Applies the exact same real/imaginary tilt-angle decomposition as
afmag_tilt_angles()(_tilt_components()) – but to the(Hzx, Hzy)row ofinterstation_tensorrather than totipper. That row is physically the same Hz-to-horizontal relation a ZTEM tipper describes; the other two rows (Hxx, Hxy, Hyx, Hyy, the horizontal-to-horizontal part of the tensor) are not used here.- Parameters:
sites (AirborneSites-like) – Anything accepted by
ensure_asites(). Sites with nointerstation_transfer_functionstransfer function attached (including original-comparator AFMAG sites – seeoriginal_afmag_tilt_table()for those) are silently omitted, not filled with a fabricated zero.recursive (bool) – Forwarded to
ensure_asites().on_dup (str) – Forwarded to
ensure_asites().strict (bool) – Forwarded to
ensure_asites().verbose (int) – Forwarded to
ensure_asites().
- Returns:
Same columns as
afmag_tilt_angles():station,freq,period,tilt_real_deg,tilt_real_azimuth_deg,tilt_imag_deg,tilt_imag_azimuth_deg,tilt_resultant_deg.- Return type:
- pycsamt.emtools.afmag.original_afmag_tilt_table(sites, *, recursive=True, on_dup='replace', strict=False, verbose=0)[source]
Per-station, per-frequency original comparator AFMAG tilt table.
Reads
afmag_tilt_degdirectly – the historical comparator measurement already is a tilt angle in degrees, not a complex tipper pair, so there is noarctan/decomposition step here the way there is inafmag_tilt_angles()/airmt_tilt_angles(): a single real number per frequency is all the instrument ever reported (see the module docstring).- Parameters:
sites (AirborneSites-like) – Anything accepted by
ensure_asites(). Sites with noafmag_tilttransfer function attached are silently omitted, not filled with a fabricated zero.recursive (bool) – Forwarded to
ensure_asites().on_dup (str) – Forwarded to
ensure_asites().strict (bool) – Forwarded to
ensure_asites().verbose (int) – Forwarded to
ensure_asites().
- Returns:
Columns:
station,freq,period,tilt_deg.- Return type:
- pycsamt.emtools.afmag.motion_susceptibility_table(sites, *, inclination, declination, roll_amplitude_deg, pitch_amplitude_deg, yaw_amplitude_deg=0.0, recursive=True, on_dup='replace', strict=False, verbose=0)[source]
Score each station’s exposure to motion-induced noise.
Sweeps a nominal sinusoidal attitude envelope (amplitude only, no assumed platform frequency) through
motion_coupling_cosine()at the survey’s geomagnetic geometry, and reports the resulting \(\cos\theta\) swing — the geometric factor that Liu et al. (2018) show drives motion-induced noise. This is independent of EM frequency; the paper’s finding that the noise concentrates at low EM frequency comes from the platform’s own physical oscillation spectrum (typically a few Hz), not from a frequency-dependence of this coupling geometry itself.- Parameters:
sites (Sites-like or AirborneSites-like) – Anything accepted by
ensure_any_sites(). Ground tipper-shaped input goes throughSitesas before; airborne input goes throughAirborneSitesbut only finds data here when it carries a ZTEM-style tipper transfer function – seeairmt_tilt_angles()/original_afmag_tilt_table()for the real AFMAG-family shapes instead.inclination (float) – Geomagnetic inclination/declination in degrees, assumed common to the survey (a local, small-region approximation matching the paper’s own “geomagnetic field is a constant vector in a local region” assumption).
declination (float) – Geomagnetic inclination/declination in degrees, assumed common to the survey (a local, small-region approximation matching the paper’s own “geomagnetic field is a constant vector in a local region” assumption).
roll_amplitude_deg (float) – Nominal peak platform roll/pitch amplitude in degrees.
pitch_amplitude_deg (float) – Nominal peak platform roll/pitch amplitude in degrees.
yaw_amplitude_deg (float, default 0.0) – Nominal peak yaw amplitude; the paper finds yaw has no effect for a z-axis coil (rotation about the coil’s own normal), so this defaults to zero rather than an assumed value.
recursive (bool) – Forwarded to
ensure_any_sites().on_dup (str) – Forwarded to
ensure_any_sites().strict (bool) – Forwarded to
ensure_any_sites().verbose (int) – Forwarded to
ensure_any_sites().
- Returns:
Columns:
station,inclination_deg,declination_deg,cos_theta_min,cos_theta_max,susceptibility_score(cos_theta_max - cos_theta_min; larger means more exposed).- Return type:
- pycsamt.emtools.afmag.flag_motion_susceptible_band(sites, *, inclination, declination, roll_amplitude_deg, pitch_amplitude_deg, yaw_amplitude_deg=0.0, band_hz=(150.0, 510.0), threshold=0.05, action='mask', inplace=False, recursive=True, on_dup='replace', strict=False, verbose=0)[source]
Flag/mask a station’s low-frequency tipper band as motion-suspect.
For stations whose
motion_susceptibility_table()score exceeds threshold, the tipper values within band_hz are either masked (set tonan, distinguishing “suspect” from “measured zero”) or dropped entirely. For groundSitesinput, this mirrors the sameensure_sites->_apply_eachmutation contract used bynotch_powerline()anddrop_freqs_manual(). Onlyaction="mask"is offered forAirborneSitesinput (see Raises), matchingmask_outside_ztem_band()’s identical restriction for the identical reason – this function only ever finds real tipper data on anAirborneSitein the first place when it carries a ZTEM-style tipper transfer function, since neither real AFMAG-family shape (afmag_tilt_deg,interstation_tensor) is one.This does not attempt the paper’s literal time-domain subtraction (see the module docstring for why:
Sitescarries no raw time series to subtract from). It is a QC gate, not a noise-removal algorithm — flagging a plausibly contaminated band so it can be reviewed or excluded, the same way the rest ofemtoolstreats other unreliable-frequency conditions.- Parameters:
sites (Sites-like or AirborneSites-like) – Anything accepted by
ensure_any_sites(). Ground tipper-shaped input goes throughSitesas before; airborne input goes throughAirborneSitesbut only finds data here when it carries a ZTEM-style tipper transfer function – seeairmt_tilt_angles()/original_afmag_tilt_table()for the real AFMAG-family shapes instead.inclination (float) – Forwarded to
motion_susceptibility_table().declination (float) – Forwarded to
motion_susceptibility_table().roll_amplitude_deg (float) – Forwarded to
motion_susceptibility_table().pitch_amplitude_deg (float) – Forwarded to
motion_susceptibility_table().yaw_amplitude_deg (float, default 0.0) – Forwarded to
motion_susceptibility_table().band_hz ((float, float), default (150.0, 510.0)) – Frequency band to flag on a susceptible station, in Hz. Defaults to the historical original-AFMAG comparator band.
threshold (float, default 0.05) – Minimum
susceptibility_score(seemotion_susceptibility_table()) for a station to be flagged at all.action ({"mask", "drop"}, default "mask") –
"mask"sets flagged tipper values tonanin place;"drop"removes the corresponding frequency rows entirely.inplace (bool) – Standard
emtoolsprocessing-function tail; seenotch_powerline()for the established convention this mirrors.recursive (bool) – Standard
emtoolsprocessing-function tail; seenotch_powerline()for the established convention this mirrors.on_dup (str) – Standard
emtoolsprocessing-function tail; seenotch_powerline()for the established convention this mirrors.strict (bool) – Standard
emtoolsprocessing-function tail; seenotch_powerline()for the established convention this mirrors.verbose (int) – Standard
emtoolsprocessing-function tail; seenotch_powerline()for the established convention this mirrors.
- Returns:
The (optionally new) sites collection with flagged bands masked or dropped.
- Return type:
- Raises:
ValueError – If action is not
"mask"or"drop", or if action is"drop"and sites resolves toAirborneSites.
- pycsamt.emtools.afmag.plot_afmag_tilt_profile(sites, *, component='real', frequency_hz=None, period_s=None, figsize=(9.5, 4.0), station_label_step=1, station_preset='pseudosection', station_style=None, ax=None)[source]
Plot the classic AFMAG flight-line tilt-angle profile.
One value per station at a single reference frequency/period — the historical field presentation of AFMAG data, analogous to
plot_emap_filter_profile()’s station-profile layout.- Parameters:
sites (Sites-like or AirborneSites-like) – Anything accepted by
ensure_any_sites(). Ground tipper-shaped input goes throughSitesas before; airborne input goes throughAirborneSitesbut only finds data here when it carries a ZTEM-style tipper transfer function – seeairmt_tilt_angles()/original_afmag_tilt_table()for the real AFMAG-family shapes instead.component ({"real", "imag", "resultant"}, default "real") – Which
afmag_tilt_angles()column to plot.frequency_hz (float, optional) – Reference frequency/period; nearest available value is used per station. Exactly one may be given; the median frequency across all stations is used when neither is given.
period_s (float, optional) – Reference frequency/period; nearest available value is used per station. Exactly one may be given; the median frequency across all stations is used when neither is given.
figsize ((float, float), default (9.5, 4.0)) – Used only when ax is not supplied.
station_label_step (int | None) – Forwarded to
style_for()via_apply_station_rendering(), matching the top-of- section station convention used throughout pyCSAMT.station_preset (str) – Forwarded to
style_for()via_apply_station_rendering(), matching the top-of- section station convention used throughout pyCSAMT.station_style (Any | None) – Forwarded to
style_for()via_apply_station_rendering(), matching the top-of- section station convention used throughout pyCSAMT.ax (matplotlib.axes.Axes, optional) – Existing axes to draw on.
- Return type:
- pycsamt.emtools.afmag.plot_afmag_tilt_psection(sites, *, component='resultant', cmap='RdYlBu_r', clim=None, clim_pct=(2.0, 98.0), figsize=(9.0, 5.0), station_label_step=1, station_preset='pseudosection', station_style=None, ax=None)[source]
Plot an AFMAG tilt-angle pseudosection (station x log-period).
- Parameters:
sites (Sites-like or AirborneSites-like) – Anything accepted by
ensure_any_sites(). Ground tipper-shaped input goes throughSitesas before; airborne input goes throughAirborneSitesbut only finds data here when it carries a ZTEM-style tipper transfer function – seeairmt_tilt_angles()/original_afmag_tilt_table()for the real AFMAG-family shapes instead.component ({"real", "imag", "resultant"}, default "resultant") – Which
afmag_tilt_angles()column to image.cmap (str, default "RdYlBu_r") – Colormap name.
clim ((float, float), optional) – Explicit color limits; overrides clim_pct.
clim_pct ((float, float), default (2.0, 98.0)) – Percentile color limits when clim is not given.
figsize ((float, float), default (9.0, 5.0)) – Used only when ax is not supplied.
station_label_step (int | None) – See
plot_afmag_tilt_profile().station_preset (str) – See
plot_afmag_tilt_profile().station_style (Any | None) – See
plot_afmag_tilt_profile().ax (matplotlib.axes.Axes, optional) – Existing axes to draw on.
- Return type:
- pycsamt.emtools.afmag.plot_afmag_tilt_polar(sites, *, station=None, component='real', cmap='viridis', figsize=(5.5, 5.5), recursive=True, on_dup='replace', strict=False, verbose=0, ax=None)[source]
Polar view of AFMAG tilt: azimuth and magnitude vs. period.
Direct AFMAG-labelled counterpart of
plot_tipper_polar(): each frequency is one scatter point, colour encodes \(\log_{10}(\text{period})\).- Parameters:
sites (Sites-like)
station (str, optional) – Station to plot; defaults to the first (sorted) station.
component ({"real", "imag"}, default "real")
cmap (str, default "viridis")
recursive (bool) – Forwarded to
ensure_any_sites().on_dup (str) – Forwarded to
ensure_any_sites().strict (bool) – Forwarded to
ensure_any_sites().verbose (int) – Forwarded to
ensure_any_sites().ax (matplotlib.axes.Axes, optional) – Existing polar axes to draw on.
- Return type:
- pycsamt.emtools.afmag.plot_airmt_tilt_profile(sites, *, component='real', frequency_hz=None, period_s=None, figsize=(9.5, 4.0), station_label_step=1, station_preset='pseudosection', station_style=None, ax=None)[source]
Plot the tensor AFMAG/AirMt flight-line tilt-angle profile.
Direct AirMt-tensor analogue of
plot_afmag_tilt_profile(); seeairmt_tilt_angles()for the underlying table.- Parameters:
sites (AirborneSites-like) – Anything accepted by
ensure_asites().component ({"real", "imag", "resultant"}, default "real")
frequency_hz (float, optional) – Reference frequency/period; nearest available value is used per station. At most one may be given; the median frequency across all stations is used when neither is given.
period_s (float, optional) – Reference frequency/period; nearest available value is used per station. At most one may be given; the median frequency across all stations is used when neither is given.
figsize ((float, float), default (9.5, 4.0)) – Used only when ax is not supplied.
station_label_step (int | None) – Forwarded to
style_for()via_apply_station_rendering().station_preset (str) – Forwarded to
style_for()via_apply_station_rendering().station_style (Any | None) – Forwarded to
style_for()via_apply_station_rendering().ax (matplotlib.axes.Axes, optional) – Existing axes to draw on.
- Return type:
- pycsamt.emtools.afmag.plot_airmt_tilt_psection(sites, *, component='resultant', cmap='RdYlBu_r', clim=None, clim_pct=(2.0, 98.0), show_grid=True, show_contour=True, n_contour_levels=3, figsize=(9.0, 5.0), station_label_step=1, station_preset='pseudosection', station_style=None, ax=None)[source]
Plot an AirMt tilt-angle pseudosection (station x log-period).
Direct AirMt-tensor analogue of
plot_afmag_tilt_psection(), readingairmt_tilt_angles()instead – the same station-x-log-period layout every other airborne technology in this project already gets (plot_ztem_divergence_psection(),plot_mobilemt_conductivity_psection()), closing the one real gap left when only a single-frequency profile (plot_airmt_tilt_profile()) existed for this technology. The underlying image stays a plainimshow()grid (matching the rest of this module’s psection functions), with two optional overlays matching the cell-boundary/level-line convention already used forpycsamt.emtools.fieldzone’s ownimshow/contourpseudosections: cell-boundary gridlines (show_grid) and white contour level lines with inline labels (show_contour) tracing the same imaged field.- Parameters:
sites (AirborneSites-like) – Anything accepted by
ensure_asites().component ({"real", "imag", "resultant"}, default "resultant") – Which
airmt_tilt_angles()column to image.cmap (str, default "RdYlBu_r")
clim ((float, float), optional) – Explicit color limits; overrides clim_pct.
clim_pct ((float, float), default (2.0, 98.0)) – Percentile color limits when clim is not given.
show_grid (bool, default True) – Draw thin gridlines at every station/period cell boundary.
show_contour (bool, default True) – Overlay n_contour_levels - 2 evenly-spaced contour lines (dropping the two outermost levels, which would otherwise hug the colour-scale edges) with inline value labels, tracing the same imaged field – a visual aid for the broad trend on top of a necessarily coarse, noisy station/period grid, not a claim of smooth structure between stations.
n_contour_levels (int, default 3) – Number of evenly-spaced levels spanning the image’s own colour range before dropping the two outermost; must be at least 3 for any contour line to be drawn.
figsize ((float, float), default (9.0, 5.0)) – Used only when ax is not supplied.
station_label_step (int | None) – See
plot_afmag_tilt_profile().station_preset (str) – See
plot_afmag_tilt_profile().station_style (Any | None) – See
plot_afmag_tilt_profile().ax (matplotlib.axes.Axes, optional) – Existing axes to draw on.
- Return type:
- pycsamt.emtools.afmag.plot_original_afmag_tilt_profile(sites, *, frequency_hz=None, period_s=None, figsize=(9.5, 4.0), station_label_step=1, station_preset='pseudosection', station_style=None, ax=None)[source]
Plot the original comparator AFMAG flight-line tilt profile.
Direct original-comparator analogue of
plot_afmag_tilt_profile(); seeoriginal_afmag_tilt_table()for the underlying table. There is nocomponentargument here (unlikeplot_afmag_tilt_profile()/plot_airmt_tilt_profile()): the historical measurement is a single real number per frequency, not a complex pair with a real/imaginary/resultant split.- Parameters:
sites (AirborneSites-like) – Anything accepted by
ensure_asites().frequency_hz (float, optional) – Reference frequency/period; nearest available value is used per station. At most one may be given; the median frequency across all stations is used when neither is given.
period_s (float, optional) – Reference frequency/period; nearest available value is used per station. At most one may be given; the median frequency across all stations is used when neither is given.
figsize ((float, float), default (9.5, 4.0)) – Used only when ax is not supplied.
station_label_step (int | None) – Forwarded to
style_for()via_apply_station_rendering().station_preset (str) – Forwarded to
style_for()via_apply_station_rendering().station_style (Any | None) – Forwarded to
style_for()via_apply_station_rendering().ax (matplotlib.axes.Axes, optional) – Existing axes to draw on.
- Return type:
- pycsamt.emtools.afmag.original_afmag_conductor_diagnostics(sites, *, freq_low_hz=None, freq_high_hz=None, recursive=True, on_dup='replace', strict=False, verbose=0)[source]
Ward (1959)-style dual-frequency conductor diagnostics.
Reproduces the two semi-quantitative measurements Ward’s own worked example computes from a dual-frequency tilt profile (Ward 1959, Fig. 16 and Results section (a)-(b)):
the along-line crossover position at each frequency – the station-to-station zero-crossing of the signed
original_afmag_tilt_table()tilt angle nearest the profile’s own peak-to-trough swing, i.e. the conductor axis – and the shift between the two frequencies’ crossovers, which Ward reads as evidence of inhomogeneity along the conductor;the peak-to-peak amplitude ratio between the two frequencies, \((\max-\min)_{\text{low}} / (\max-\min)_{\text{high}}\), Ward’s semi-quantitative conductivity indicator (his own worked example on a pyrite-pyrrhotite body gives \(0.74\); values range from 0, very poor, toward 1, excellent).
Ward’s further estimates – depth to the top of the conductor from the crossover slope, depth extent from the profile’s beyond-peak slope, and dip from profile asymmetry – all require a scale-model calibration curve that the paper does not give in closed form, so, matching this module’s stated policy of not inventing a formula the cited literature does not itself supply, they are not computed here.
- Parameters:
sites (AirborneSites-like) – Anything accepted by
ensure_asites().freq_low_hz (float, optional) – The two frequencies to compare. Default to the minimum and maximum frequency actually present (Ward’s own instrument always operated 150 and 510 Hz together, but this stays general rather than hardcoding that pair).
freq_high_hz (float, optional) – The two frequencies to compare. Default to the minimum and maximum frequency actually present (Ward’s own instrument always operated 150 and 510 Hz together, but this stays general rather than hardcoding that pair).
recursive (bool) – Forwarded to
ensure_asites().on_dup (str) – Forwarded to
ensure_asites().strict (bool) – Forwarded to
ensure_asites().verbose (int) – Forwarded to
ensure_asites().
- Returns:
Keys:
freq_low_hz,freq_high_hz,crossover_low_m,crossover_high_m(along-line position in metres,nanif the profile does not change sign),crossover_shift_m(crossover_high_m - crossover_low_m),peak_to_peak_low,peak_to_peak_high,peak_to_peak_ratio(degrees;low / high), andprofile– apandas.DataFramewith columnsstation,position_m,tilt_low_deg,tilt_high_deg, the shared input the companion plot function draws from.- Return type:
- Raises:
ValueError – If fewer than 2 distinct frequencies are present, or a requested freq_low_hz/freq_high_hz is not found.
- pycsamt.emtools.afmag.plot_original_afmag_dual_frequency_profile(sites, *, freq_low_hz=None, freq_high_hz=None, figsize=(9.5, 4.5), ax=None)[source]
Ward (1959) Fig. 16-style dual-frequency tilt-angle profile.
Plots both frequencies together along real chainage – the classic AFMAG field product, not a single-frequency curve – and marks the crossover (“axis of conductor”) at each frequency using
original_afmag_conductor_diagnostics(), the same interpretation Ward’s own worked example applies.- Parameters:
sites (AirborneSites-like) – Anything accepted by
ensure_asites().freq_low_hz (float, optional) – Forwarded to
original_afmag_conductor_diagnostics().freq_high_hz (float, optional) – Forwarded to
original_afmag_conductor_diagnostics().figsize ((float, float), default (9.5, 4.5)) – Used only when ax is not supplied.
ax (matplotlib.axes.Axes, optional) – Existing axes to draw on.
- Return type:
- pycsamt.emtools.afmag.plot_motion_coupling(yaw, pitch, roll, inclination, declination, *, x=None, xlabel='sample', degrees=True, ax=None, figsize=(8.0, 4.0))[source]
Plot \(\theta(t)\) and \(\cos\theta(t)\) for attitude data.
Direct visualization of the paper’s own Fig. 2-5 style curves, from raw attitude arrays – not
Sites-based (see the module docstring for why).- Parameters:
yaw (array-like) – Attitude time series (or a swept parameter), same shape.
pitch (array-like) – Attitude time series (or a swept parameter), same shape.
roll (array-like) – Attitude time series (or a swept parameter), same shape.
inclination (float) – Geomagnetic field geometry in degrees.
declination (float) – Geomagnetic field geometry in degrees.
x (array-like, optional) – X-axis values (time or sample index); defaults to a plain integer index.
xlabel (str, default "sample") – X-axis label used when x is not supplied a label of its own.
degrees (bool, default True) – Whether yaw/pitch/roll are in degrees.
ax (matplotlib.axes.Axes, optional) – Existing axes to draw on; a right-hand twin axes is added for \(\cos\theta\).
figsize ((float, float), default (8.0, 4.0)) – Used only when ax is not supplied.
- Returns:
The primary (\(\theta\)) axes; the twin axes is reachable via
ax.figure.axes[-1].- Return type:
- pycsamt.emtools.afmag.plot_motion_susceptibility_map(sites, *, inclination, declination, roll_amplitude_deg, pitch_amplitude_deg, yaw_amplitude_deg=0.0, cmap='magma_r', figsize=(7.0, 5.5), ax=None)[source]
Map each station’s motion-noise susceptibility score.
Uses station coordinates when available (
coords), otherwise falls back to a station-index profile – the same fallback strategypycsamt.emtools.tfuses for stations with no geo-referencing.- Parameters:
sites (Sites-like)
inclination (float) – Forwarded to
motion_susceptibility_table().declination (float) – Forwarded to
motion_susceptibility_table().roll_amplitude_deg (float) – Forwarded to
motion_susceptibility_table().pitch_amplitude_deg (float) – Forwarded to
motion_susceptibility_table().yaw_amplitude_deg (float, default 0.0) – Forwarded to
motion_susceptibility_table().cmap (str, default "magma_r")
figsize ((float, float), default (7.0, 5.5)) – Used only when ax is not supplied.
ax (matplotlib.axes.Axes, optional) – Existing axes to draw on.
- Return type:
- pycsamt.emtools.afmag.plot_afmag_correction_comparison(before_sites, after_sites=None, *, component='resultant', inclination=None, declination=None, roll_amplitude_deg=None, pitch_amplitude_deg=None, yaw_amplitude_deg=0.0, cmap='RdYlBu_r', delta_cmap='RdBu_r', figsize=(11.0, 8.2), axes=None, **flag_kws)[source]
Plot before/after/delta AFMAG tilt pseudosections.
Structurally identical to
plot_emap_filter_psection()’s triptych: before, after, and \(\Delta\) (after - before) panels. If after_sites is omitted, it is computed by callingflag_motion_susceptible_band()on before_sites with inclination, declination, roll_amplitude_deg, and pitch_amplitude_deg (all then required).- Parameters:
before_sites (Sites-like)
after_sites (Sites-like, optional) – Already-corrected sites; computed from before_sites when omitted.
component ({"real", "imag", "resultant"}, default "resultant")
inclination (float, optional) – Forwarded to
flag_motion_susceptible_band()when after_sites is omitted; required in that case.declination (float, optional) – Forwarded to
flag_motion_susceptible_band()when after_sites is omitted; required in that case.roll_amplitude_deg (float, optional) – Forwarded to
flag_motion_susceptible_band()when after_sites is omitted; required in that case.pitch_amplitude_deg (float, optional) – Forwarded to
flag_motion_susceptible_band()when after_sites is omitted; required in that case.yaw_amplitude_deg (float, default 0.0) – Forwarded to
flag_motion_susceptible_band()when after_sites is omitted.cmap (str) – Colormaps for the before/after and delta panels.
delta_cmap (str) – Colormaps for the before/after and delta panels.
figsize ((float, float), default (11.0, 8.2)) – Used only when axes is not supplied.
axes (sequence of 3 Axes, optional) – Existing axes (before, after, delta) to draw on.
**flag_kws – Forwarded to
flag_motion_susceptible_band()(for exampleband_hz,threshold,action).
- Return type:
matplotlib.Figure
- Raises:
ValueError – If after_sites is omitted and inclination, declination, roll_amplitude_deg, or pitch_amplitude_deg is not given.