2.14.2.2. pycsamt.emtools.ztem#

ZTEM-specific spatial-domain processing, diagnostics, and plotting.

ZTEM (Z-axis Tipper Electromagnetic; Lo and Zang 2008) is an airborne AFMAG technique: a helicopter-towed vertical-field (\(H_z\)) coil is referenced to fixed-ground horizontal-field (\(H_x\), \(H_y\)) base-station coils, giving exactly the same \(H_z = T_{zx}H_x + T_{zy}H_y\) tipper relationship that Site already carries as tipper – the equivalence pycsamt.emtools.afmag documents and that pycsamt.airborne.ztem encodes at the file-format/adapter layer. This module is therefore built, like pycsamt.emtools.afmag and pycsamt.emtools.tf, entirely on Site.tipper.

It does not repeat what those two modules already do:

  • pycsamt.emtools.afmag gives per-station, per-frequency tilt- angle diagnostics (afmag_tilt_angles()) and motion-coupling QC – purely local quantities, no derivative across stations.

  • pycsamt.emtools.tf gives generic tipper visualization (induction arrows, polar plots, period sections) that applies equally to ground MT tipper and airborne ZTEM tipper alike.

What is missing from both, and what ZTEM survey practice specifically relies on for a first-pass, pre-inversion read of a flight line (Sattel and Witherly 2012), is along-profile spatial-derivative processing – comparing a station’s tipper to its neighbours’ rather than looking at one station in isolation:

  • total_divergence_table() – the horizontal derivative of the in-line tipper component along the flight line. Lo and Zang (2008) define the (map-grid) “Total Divergence” as \(DT = \partial T_{zx}/\partial x + \partial T_{zy}/\partial y\) (the literal equation is reproduced, e.g., as eq. 6 of wang2025). Sattel and Witherly (2012) note that “for the 2D case, the horizontal derivative is equivalent to the Peaker (Pedersen et al., 1994) and the total divergence” – i.e. along one flight line the two published image products, DT and the VLF-style “Peaker”, coincide with a single along-line derivative \(\partial T/\partial x\). Reproducing the full map-grid DT (needing a true 2-D grid across multiple flight lines, not a single Sites profile) is out of scope here; this function computes exactly the along-profile quantity the source papers show is equivalent for the 2D/profile case, and says so in its own docstring rather than silently overclaiming the 3-D grid product.

  • phase_rotate_table() – the “phase-rotated response” image product Sattel and Witherly (2012, their Fig. 2) show converts a tipper crossover anomaly (over a lateral contact) into a peak anomaly, “obtained by reduction-to-pole (RTP) filtering and Hilbert transformation”, noting “little difference between the RTP-filtered and the Hilbert-transformed profiles”. This function implements the Hilbert-transform half of that pair directly (scipy.signal.hilbert()), a standard, unambiguous spatial analytic-signal transform.

  • mask_outside_ztem_band() – a Sites-in/Sites-out QC gate (mirroring flag_motion_susceptible_band()’s mask/drop contract) that reuses the survey’s own published usable bandwidth, pycsamt.airborne.ztem.ZTEMSystemSpec .practical_frequency_range_hz, rather than inventing a new band definition – the one function in this module meant to sit inside a processing pipeline rather than only produce a diagnostic table.

Karous-Hjelt (1983) pseudo-depth current-density sections and the Becken and Pedersen (2003) tipper-gradient apparent-resistivity/phase transform are both mentioned by Sattel and Witherly (2012) as further image products, but neither closed-form is reproduced in the papers available locally (data/ZTEM/), so neither is implemented here – consistent with not silently converting an unverified formula into a “working” function.

References

[Lo2008]

Lo, B., and Zang, M. (2008). Numerical modeling of Z-TEM (airborne AFMAG) responses to guide exploration strategies. SEG Expanded Abstracts, 27, 1098-1101.

[Legault2012]

Legault, J. M., Zhao, S., and Fitch, R. (2012). ZTEM airborne AFMAG survey results over low sulphidation epithermal gold-silver vein systems at Gold Springs, south eastern Nevada. 22nd International Geophysical Conference and Exhibition (ASEG), Brisbane.

[Sattel2012]

Sattel, D., and Witherly, K. (2012). An overview of ZTEM data interpretation tools. 2012 NFEM Forum.

[Pedersen1994]

Pedersen, L. B., Qian, W., Dynesius, L., and Zhang, P. (1994). An airborne tensor VLF system. From concept to realization. Geophysical Prospecting, 42, 863-883.

[wang2025]

Wang, Y., Qu, J., Chen, T., Zhou, S., and Li, Y. (2025). Studies of three dimensional staggered-grid finite difference for Z-axis tipper electromagnetic numerical simulation. Frontiers in Earth Science, 13:1496312.

Functions

mask_outside_ztem_band(sites, *[, band_hz, ...])

Mask/drop tipper frequencies outside the usable ZTEM band.

phase_rotate_table(sites, *[, frequency_hz, ...])

Hilbert-transform "phase-rotated" ZTEM profile at one frequency.

plot_ztem_band_mask_psection(sites, *[, ...])

Plot before/after \(|T|\) pseudosections around the ZTEM usable band.

plot_ztem_divergence_profile(sites, *[, ...])

Plot the ZTEM total-divergence / Peaker flight-line profile.

plot_ztem_divergence_psection(sites, *[, ...])

Plot a ZTEM total-divergence pseudosection (station x log-period).

plot_ztem_divergence_psection_grid(sites, *)

Compare several flight lines' divergence pseudosections at once.

plot_ztem_flight_lines(sites, *[, figsize, ...])

Plot a Sattel and Witherly (2012, Fig.

plot_ztem_map(sites, *[, quantity, part, ...])

Legault et al. (2012, Fig.

plot_ztem_phase_rotation_profile(sites, *[, ...])

Plot raw vs. Hilbert-phase-rotated ZTEM response at one frequency.

plot_ztem_tipper_profile(sites, *[, ...])

Legault et al. (2012, Fig.

total_divergence_table(sites[, spacing_m, ...])

Along-profile ZTEM total-divergence / Peaker table.

ztem_crossover_diagnostics(sites, *[, ...])

Legault et al. (2012, Fig.

pycsamt.emtools.ztem.ztem_crossover_diagnostics(sites, *, frequency_hz=None, period_s=None, component='tzx', spacing_m=200.0, recursive=True, on_dup='replace', strict=False, verbose=0)[source]

Legault et al. (2012, Fig. 6)-style raw in-phase/quadrature crossover.

Their own synthetic forward-model example over a mushroom-shaped epithermal target reads a negative-to-positive in-phase crossover directly above the target, generally accompanied by a (noisier) negative-to-positive quadrature crossover at every frequency – the qualitative, single-frequency, pre-processing read of a flight line before any derivative or transform is applied (contrast total_divergence_table()/ phase_rotate_table(), both of which operate on this same raw tipper but convert the crossover into a peak). This function finds those two crossovers and reports the peak-to-peak swing of each, the same crossover/amplitude measurements original_afmag_conductor_diagnostics() reports for the AFMAG comparator, applied here to the real/ imaginary parts of one tipper component instead of two hardware frequencies.

Warning

Like total_divergence_table(), sites is assumed to be one flight line; pre-filter a multi-line survey to one line first (see that function’s own warning for why).

Parameters:
  • sites (Sites-like or AirborneSites-like) – Anything accepted by ensure_any_sites().

  • frequency_hz (float, optional) – Reference frequency/period; nearest available value is used. At most one may be given; the median frequency across the profile is used when neither is given.

  • period_s (float, optional) – Reference frequency/period; nearest available value is used. At most one may be given; the median frequency across the profile is used when neither is given.

  • component ({"tzx", "tzy"}, default "tzx") – "tzx" is the classical in-line choice (Legault et al. 2012); "tzy" highlights cross-line structure (Sattel and Witherly 2012).

  • spacing_m (float, default 200.0) – Forwarded to _station_positions().

  • 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:

Keys: freq_hz, crossover_real_m, crossover_imag_m (along-profile position, nan if that part never changes sign between its own max and min), peak_to_peak_real, peak_to_peak_imag, and profile – a pandas.DataFrame with columns station, position_m, real, imag (the raw, dimensionless tipper values; multiply by 100 for the percent convention Legault et al. 2012 and Sattel and Witherly 2012 both plot).

Return type:

dict

Raises:

ValueError – If component is not "tzx"/"tzy", or fewer than 2 stations have a usable value at the resolved frequency.

pycsamt.emtools.ztem.total_divergence_table(sites, spacing_m=200.0, *, component='tzx', recursive=True, on_dup='replace', strict=False, verbose=0)[source]

Along-profile ZTEM total-divergence / Peaker table.

Computes the along-line horizontal derivative of the selected tipper component by first-order central-in-space finite differences between adjacent stations, at every frequency:

\[DT(j,\,f) \approx \frac{T(j+1,\,f) - T(j,\,f)}{x(j+1) - x(j)}\]

where stations are ordered by chainage along the profile (see _station_positions()). Per Sattel and Witherly (2012), this single along-line derivative is both the “Total Divergence” (Lo and Zang 2008) and the VLF-style “Peaker” (Pedersen et al. 1994) in the 2D/profile case – the full 3-D map-grid divergence (\(\partial T_{zx}/\partial x + \partial T_{zy}/\partial y\)) would additionally require a genuine cross-line (y) sampling that a single Sites profile does not carry, and is not attempted here.

Warning

sites is assumed to be one flight line. Chainage comes from _station_positions(), which projects every station onto a single bearing; passing a multi-line survey directly differentiates across line boundaries too, producing a physically meaningless value at every line-to-line join. Pre-filter to one line first (e.g. select() on a per-line predicate) before calling this function on a multi-line dataset – see plot_ztem_map()’s own quantity="divergence" branch for a worked example that does this per-line grouping automatically.

Parameters:
  • sites (Sites-like or AirborneSites-like) – Anything accepted by ensure_any_sites(): ground Sites when the data carries an impedance channel too, or AirborneSites for genuine tipper-only ZTEM/AFMAG data (a path/directory of EMTF-XML is routed automatically based on what it contains).

  • spacing_m (float, default 200.0) – Fall-back inter-station spacing [m] used only when no station coordinates are available; see _station_positions().

  • component ({"tzx", "tzy"}, default "tzx") – Tipper component to differentiate. "tzx" is the classical in-line (flight-direction) choice (Legault et al. 2012); "tzy" highlights structures striking across the line (Sattel and Witherly 2012).

  • 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:

One row per (adjacent-station pair, frequency). Columns: station_a, station_b (left/right station of the pair), x_m (pair midpoint chainage), dx_m (station spacing), freq_hz, period_s, divergence_real, divergence_imag [each in units of tipper per metre], divergence_abs. Pairs/frequencies with a missing tipper value on either side are omitted, not filled with zero.

Return type:

pandas.DataFrame

Raises:

ValueError – If component is not "tzx" or "tzy".

pycsamt.emtools.ztem.phase_rotate_table(sites, *, frequency_hz=None, period_s=None, component='tzx', part='real', spacing_m=200.0, n_resample=None, recursive=True, on_dup='replace', strict=False, verbose=0)[source]

Hilbert-transform “phase-rotated” ZTEM profile at one frequency.

Reproduces the Hilbert-transform half of the phase-rotation image product described by Sattel and Witherly (2012, Fig. 2): the tipper component’s along-profile crossover anomaly (odd about the causative contact) is converted into a peak anomaly (even about it) by taking the imaginary part of its spatial analytic signal (scipy.signal.hilbert()). Because the Hilbert transform assumes uniform sampling, the selected component is first linearly interpolated onto a uniform grid along chainage; the returned table is indexed by that uniform grid (with the nearest real station attached for reference), not by the original, generally unevenly spaced, station positions.

Warning

Like total_divergence_table(), sites is assumed to be one flight line: chainage is a single-bearing projection of every station, so a multi-line survey passed directly gets interpolated across line boundaries too. Pre-filter to one line first for a multi-line dataset.

Parameters:
  • sites (Sites-like or AirborneSites-like) – Anything accepted by ensure_any_sites(): ground Sites when the data carries an impedance channel too, or AirborneSites for genuine tipper-only ZTEM/AFMAG data (a path/directory of EMTF-XML is routed automatically based on what it contains).

  • frequency_hz (float, optional) – Target frequency/period; the nearest available frequency is used. At most one may be given; the median frequency across the profile is used when neither is given.

  • period_s (float, optional) – Target frequency/period; the nearest available frequency is used. At most one may be given; the median frequency across the profile is used when neither is given.

  • component ({"tzx", "tzy"}, default "tzx")

  • part ({"real", "imag"}, default "real") – Which part of the complex tipper to phase-rotate. "real" (in-phase) matches the classical VLF/ZTEM crossover-to-peak image product.

  • spacing_m (float, default 200.0) – Fall-back inter-station spacing [m]; see _station_positions().

  • n_resample (int, optional) – Number of points on the uniform resampling grid. Defaults to the number of stations with a valid value (minimum 64).

  • 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: x_m (uniform-grid chainage), nearest_station, freq_hz, period_s, raw (the interpolated, un- rotated component), rotated (its Hilbert transform), envelope (the analytic-signal magnitude \(\sqrt{raw^2 + rotated^2}\)).

Return type:

pandas.DataFrame

Raises:

ValueError – If component is not "tzx"/"tzy", if part is not "real"/"imag", or if both frequency_hz and period_s are given.

pycsamt.emtools.ztem.mask_outside_ztem_band(sites, *, band_hz=None, system_spec=None, action='mask', inplace=False, recursive=True, on_dup='replace', strict=False, verbose=0)[source]

Mask/drop tipper frequencies outside the usable ZTEM band.

Reuses the published usable bandwidth already carried by pycsamt.airborne.ztem.ZTEMSystemSpec (default practical_frequency_range_hz of 22-720 Hz) rather than inventing a new band definition. For ground Sites input, this mirrors the same ensure_sites -> _apply_each mutation contract used by flag_motion_susceptible_band() and notch_powerline(): this is the one function in this module meant to sit inside a processing pipeline (container in, container out) rather than only produce a diagnostic table. For AirborneSites input, action="drop" is refused (see Raises) because it would leave the EMTF document’s shared period axis inconsistent with the tipper transfer function’s own periods; only action="mask" is offered there, matching mask_outside_mobilemt_band()’s identical restriction for the identical reason.

Parameters:
  • sites (Sites-like or AirborneSites-like) – Anything accepted by ensure_any_sites(): ground Sites when the data carries an impedance channel too, or AirborneSites for genuine tipper-only ZTEM/AFMAG data (a path/directory of EMTF-XML is routed automatically based on what it contains).

  • band_hz ((float, float), optional) – Explicit (low, high) band in Hz. Mutually exclusive with system_spec; when neither is given, a default ZTEMSystemSpec’s practical_frequency_range_hz is used.

  • system_spec (pycsamt.airborne.ztem.ZTEMSystemSpec, optional) – Survey-specific system metadata to read the band from.

  • action ({"mask", "drop"}, default "mask") – "mask" sets out-of-band tipper values to nan in place; "drop" removes the corresponding frequency rows entirely.

  • inplace (bool) – Standard emtools processing-function tail; see flag_motion_susceptible_band() for the established convention this mirrors.

  • recursive (bool) – Standard emtools processing-function tail; see flag_motion_susceptible_band() for the established convention this mirrors.

  • on_dup (str) – Standard emtools processing-function tail; see flag_motion_susceptible_band() for the established convention this mirrors.

  • strict (bool) – Standard emtools processing-function tail; see flag_motion_susceptible_band() for the established convention this mirrors.

  • verbose (int) – Standard emtools processing-function tail; see flag_motion_susceptible_band() for the established convention this mirrors.

Returns:

The (optionally new) sites collection with out-of-band tipper frequencies masked or dropped.

Return type:

Sites

Raises:
  • ValueError – If action is not "mask" or "drop"; if both band_hz and system_spec are given; or if action is "drop" and sites resolves to AirborneSites.

  • TypeError – If system_spec is given and is not a ZTEMSystemSpec.

pycsamt.emtools.ztem.plot_ztem_tipper_profile(sites, *, frequency_hz=None, period_s=None, component='tzx', as_percent=True, figsize=(9.5, 4.2), recursive=True, on_dup='replace', strict=False, verbose=0, ax=None)[source]

Legault et al. (2012, Fig. 6)-style raw in-phase/quadrature profile.

The classic ZTEM field-presentation figure: real (in-phase) and imaginary (quadrature) tipper plotted together at one frequency, in percent, along real flight-line chainage – Fig. 6’s own “METERS” x-axis, not a discrete station index – with each part’s crossover marked; see ztem_crossover_diagnostics().

Parameters:
  • sites (Sites-like or AirborneSites-like) – Anything accepted by ensure_any_sites().

  • frequency_hz (float | None) – Forwarded to ztem_crossover_diagnostics().

  • period_s (float | None) – Forwarded to ztem_crossover_diagnostics().

  • component (str) – Forwarded to ztem_crossover_diagnostics().

  • as_percent (bool, default True) – Multiply the plotted tipper values by 100, matching the percent convention both cited papers use. Set False to plot the raw dimensionless tipper instead.

  • figsize ((float, float), default (9.5, 4.2)) – Used only when ax is not supplied.

  • recursive (bool) – Forwarded to ztem_crossover_diagnostics().

  • on_dup (str) – Forwarded to ztem_crossover_diagnostics().

  • strict (bool) – Forwarded to ztem_crossover_diagnostics().

  • verbose (int) – Forwarded to ztem_crossover_diagnostics().

  • ax (matplotlib.axes.Axes, optional) – Existing axes to draw on.

Return type:

matplotlib.axes.Axes

pycsamt.emtools.ztem.plot_ztem_divergence_profile(sites, *, component='tzx', part='real', frequency_hz=None, period_s=None, spacing_m=200.0, figsize=(9.5, 4.0), station_label_step=1, station_preset='pseudosection', station_style=None, ax=None)[source]

Plot the ZTEM total-divergence / Peaker flight-line profile.

One value per adjacent-station pair at a single reference frequency/period – the along-line, pre-gridding form of the “Total Divergence” / “Peaker” image product (see total_divergence_table()).

Parameters:
  • sites (Sites-like or AirborneSites-like) – Anything accepted by ensure_any_sites(): ground Sites when the data carries an impedance channel too, or AirborneSites for genuine tipper-only ZTEM/AFMAG data (a path/directory of EMTF-XML is routed automatically based on what it contains).

  • component ({"tzx", "tzy"}, default "tzx")

  • part ({"real", "imag"}, default "real")

  • frequency_hz (float, optional) – Reference frequency/period; nearest available value is used per station pair. At most one may be given; the median frequency across all pairs is used when neither is given.

  • period_s (float, optional) – Reference frequency/period; nearest available value is used per station pair. At most one may be given; the median frequency across all pairs is used when neither is given.

  • spacing_m (float, default 200.0) – Forwarded to total_divergence_table().

  • 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 the shared top-of-section station convention.

  • station_preset (str) – Forwarded to style_for() via the shared top-of-section station convention.

  • station_style (Any | None) – Forwarded to style_for() via the shared top-of-section station convention.

  • ax (matplotlib.axes.Axes, optional) – Existing axes to draw on.

Return type:

matplotlib.axes.Axes

pycsamt.emtools.ztem.plot_ztem_divergence_psection(sites, *, component='tzx', part='real', spacing_m=200.0, cmap='RdBu_r', clim=None, clim_pct=95.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 a ZTEM total-divergence pseudosection (station x log-period).

A diverging, zero-centred colour scale is used, matching the physical sign convention of a spatial derivative (positive on one side of an anomaly, negative on the other – see total_divergence_table()). Optional cell-boundary gridlines and a contour overlay (default n_contour_levels=3, one interior level – here the physically meaningful zero-divergence line itself) match the same imshow/contour convention used by plot_airmt_tilt_psection() and pycsamt.emtools.fieldzone’s own pseudosections. For several flight lines compared side by side on one shared colour scale, see plot_ztem_divergence_psection_grid().

Parameters:
  • sites (Sites-like or AirborneSites-like) – Anything accepted by ensure_any_sites(): ground Sites when the data carries an impedance channel too, or AirborneSites for genuine tipper-only ZTEM/AFMAG data (a path/directory of EMTF-XML is routed automatically based on what it contains).

  • component ({"tzx", "tzy"}, default "tzx")

  • part ({"real", "imag"}, default "real")

  • spacing_m (float, default 200.0) – Forwarded to total_divergence_table().

  • cmap (str, default "RdBu_r") – Diverging colormap name.

  • clim ((float, float), optional) – Explicit, zero-centred color limits; overrides clim_pct.

  • clim_pct (float, default 95.0) – Percentile of |divergence| used to size a symmetric colour range when clim is not given.

  • show_grid (bool, default True) – Draw thin gridlines at every station-pair/period cell boundary.

  • show_contour (bool, default True) – Overlay n_contour_levels - 2 evenly-spaced contour lines with inline labels; with the default zero-centred colour scale and 3 levels, the single interior level drawn is the zero-divergence contour itself, i.e. the crossover/conductor axis at every period simultaneously.

  • n_contour_levels (int, default 3) – Number of evenly-spaced levels spanning clim before dropping the two outermost; must be at least 3 for any line to be drawn. Kept low deliberately – a coarse station/period grid does not support many contour levels without the lines tangling into visual noise (see plot_airmt_tilt_psection()’s docstring for the same reasoning).

  • figsize ((float, float), default (9.0, 5.0)) – Used only when ax is not supplied.

  • station_label_step (int | None) – See plot_ztem_divergence_profile().

  • station_preset (str) – See plot_ztem_divergence_profile().

  • station_style (Any | None) – See plot_ztem_divergence_profile().

  • ax (matplotlib.axes.Axes, optional) – Existing axes to draw on.

Return type:

matplotlib.axes.Axes

pycsamt.emtools.ztem.plot_ztem_divergence_psection_grid(sites, *, component='tzx', part='real', spacing_m=200.0, max_lines=6, n_cols=3, cmap='seismic', clim=None, clim_pct=95.0, show_grid=True, show_contour=True, n_contour_levels=3, panel_size=(4.3, 3.4), station_label_step=2, station_preset='pseudosection', station_style=None, axes=None, recursive=True, on_dup='replace', strict=False, verbose=0)[source]

Compare several flight lines’ divergence pseudosections at once.

Every panel shares one colour scale (unlike calling plot_ztem_divergence_psection() once per line, where each panel would size its own), so colour differences between lines are directly comparable – the multi-line counterpart of a single plot_ztem_divergence_psection() call, laid out on a grid with n_cols columns. Flight lines are detected from station geometry (see _detect_line_groups()); when more than max_lines are found, a spatially even subset is kept rather than just the first max_lines encountered.

Parameters:
  • sites (Sites-like or AirborneSites-like) – Anything accepted by ensure_any_sites(), spanning several flight lines.

  • component (str) – Forwarded to total_divergence_table() for every line.

  • part (str) – Forwarded to total_divergence_table() for every line.

  • spacing_m (float) – Forwarded to total_divergence_table() for every line.

  • max_lines (int, default 6) – Maximum number of lines to draw.

  • n_cols (int, default 3) – Number of grid columns; rows are added as needed.

  • cmap (str, default "seismic") – Diverging colormap name.

  • clim ((float, float), optional) – Explicit, zero-centred color limits shared by every panel; overrides clim_pct.

  • clim_pct (float, default 95.0) – Percentile of |divergence|, pooled across every drawn line, used to size the shared symmetric colour range when clim is not given.

  • show_grid (bool) – See plot_ztem_divergence_psection().

  • show_contour (bool) – See plot_ztem_divergence_psection().

  • n_contour_levels (int) – See plot_ztem_divergence_psection().

  • panel_size ((float, float), default (4.3, 3.4)) – Per-panel figure size in inches; the full figure scales with the number of rows/columns actually used. Ignored when axes is supplied.

  • station_label_step (int | None) – See plot_ztem_divergence_profile().

  • station_preset (str) – See plot_ztem_divergence_profile().

  • station_style (Any | None) – See plot_ztem_divergence_profile().

  • axes (sequence of Axes, optional) – Existing axes to draw the (up to max_lines) panels on, flattened in the same row-major order the auto-created grid would use; must provide at least as many axes as lines are actually drawn. When not given, a new figure and grid of axes is created.

  • 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().

Return type:

matplotlib.Figure

pycsamt.emtools.ztem.plot_ztem_phase_rotation_profile(sites, *, component='tzx', part='real', frequency_hz=None, period_s=None, spacing_m=200.0, n_resample=None, figsize=(9.5, 4.2), recursive=True, on_dup='replace', strict=False, verbose=0, ax=None)[source]

Plot raw vs. Hilbert-phase-rotated ZTEM response at one frequency.

Direct reproduction of the crossover-to-peak comparison in Sattel and Witherly (2012, Fig. 2); see phase_rotate_table().

Parameters:
  • sites (Sites-like or AirborneSites-like) – Anything accepted by ensure_any_sites(): ground Sites when the data carries an impedance channel too, or AirborneSites for genuine tipper-only ZTEM/AFMAG data (a path/directory of EMTF-XML is routed automatically based on what it contains).

  • component (str) – Forwarded to phase_rotate_table().

  • part (str) – Forwarded to phase_rotate_table().

  • frequency_hz (float | None) – Forwarded to phase_rotate_table().

  • period_s (float | None) – Forwarded to phase_rotate_table().

  • spacing_m (float) – Forwarded to phase_rotate_table().

  • n_resample (int | None) – Forwarded to phase_rotate_table().

  • figsize ((float, float), default (9.5, 4.2)) – Used only when ax is not supplied.

  • recursive (bool) – Forwarded to phase_rotate_table().

  • on_dup (str) – Forwarded to phase_rotate_table().

  • strict (bool) – Forwarded to phase_rotate_table().

  • verbose (int) – Forwarded to phase_rotate_table().

  • ax (matplotlib.axes.Axes, optional) – Existing axes to draw on.

Return type:

matplotlib.axes.Axes

pycsamt.emtools.ztem.plot_ztem_band_mask_psection(sites, *, band_hz=None, system_spec=None, component='abs', cmap='RdBu_r', figsize=(9.5, 8.0), axes=None, recursive=True, on_dup='replace', strict=False, verbose=0)[source]

Plot before/after \(|T|\) pseudosections around the ZTEM usable band.

Reuses plot_induction_section() for both panels rather than re-implementing pseudosection gridding, and mask_outside_ztem_band() to compute the “after” sites.

Parameters:
  • sites (Sites-like) – Anything accepted by ensure_any_sites(): ground Sites or tipper-only AirborneSites transparently, since plot_induction_section() now accepts both.

  • band_hz (tuple[float, float] | None) – Forwarded to mask_outside_ztem_band().

  • system_spec (Any | None) – Forwarded to mask_outside_ztem_band().

  • component ({"real", "imag", "abs"}, default "abs") – Forwarded to plot_induction_section().

  • cmap (str, default "RdBu_r")

  • figsize ((float, float), default (9.5, 8.0)) – Used only when axes is not supplied.

  • axes (sequence of 2 Axes, optional) – Existing axes (before, after) to draw on.

  • recursive (bool) – Forwarded to ensure_any_sites() and mask_outside_ztem_band().

  • on_dup (str) – Forwarded to ensure_any_sites() and mask_outside_ztem_band().

  • strict (bool) – Forwarded to ensure_any_sites() and mask_outside_ztem_band().

  • verbose (int) – Forwarded to ensure_any_sites() and mask_outside_ztem_band().

Return type:

matplotlib.Figure

pycsamt.emtools.ztem.plot_ztem_flight_lines(sites, *, figsize=(7.0, 6.0), recursive=True, on_dup='replace', strict=False, verbose=0, ax=None)[source]

Plot a Sattel and Witherly (2012, Fig. 7)-style flight-line map.

Every detected flight line (see _detect_line_groups()) is drawn as its own connected navigation trace, coloured distinctly (a viridis sample per line) and labelled near its first station – with the real flight-line identifier when a technology note carries one (e.g. ZTEM’s own metadata["notes"]["ZTEM"]["LineId"]), or else a generic L1, L2, … in detected-group order, which need not match any real line numbering – with station markers. The plan-view counterpart of every other function in this module, which reads a single profile at a time. Longitude tick labels are rotated 45 degrees to avoid overlapping.

Parameters:
  • sites (Sites-like or AirborneSites-like) – Anything accepted by ensure_any_sites().

  • figsize ((float, float), default (7.0, 6.0)) – Used only when ax is not supplied.

  • 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 axes to draw on.

Return type:

matplotlib.axes.Axes

pycsamt.emtools.ztem.plot_ztem_map(sites, *, quantity='tipper', part='real', component='tzx', frequency_hz=None, period_s=None, n_grid=120, cmap='RdBu_r', clim=None, clim_pct=95.0, show_stations=True, figsize=(8.0, 6.5), recursive=True, on_dup='replace', strict=False, verbose=0, ax=None)[source]

Legault et al. (2012, Fig. 7) and Sattel and Witherly (2012, Fig. 8-11)-style map-view grid.

Interpolates one scalar field at one frequency across every flight line in sites onto a regular map grid (scipy.interpolate.griddata(), linear inside the convex hull of the stations, unfilled – left nan – outside it rather than extrapolated) and images it with a diverging, zero-centred colour scale – the genuine multi-line map product both papers show (their “DT map”/”XIP grid”/”phase-rotated grid”), as opposed to every other function in this module, which reads one flight line as a profile or pseudosection.

Parameters:
  • sites (Sites-like or AirborneSites-like) – Anything accepted by ensure_any_sites(). A genuine map needs several roughly-parallel flight lines; a single line still renders, as a thin interpolated strip along it.

  • quantity ({"tipper", "divergence"}, default "tipper") – "tipper" images the raw, un-processed tipper component (Legault et al. 2012, Fig. 7’s own “In-Phase” map); "divergence" images the along-line total-divergence / Peaker value (total_divergence_table()) at each station’s own flight line, matching Sattel and Witherly (2012)’s “DT” grid.

  • part ({"real", "imag"}, default "real")

  • component ({"tzx", "tzy"}, default "tzx")

  • frequency_hz (float, optional) – Reference frequency/period; nearest available value is used per station. At most one may be given; the median frequency 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 is used when neither is given.

  • n_grid (int, default 120) – Number of grid points along the longer map axis; the shorter axis is scaled to preserve the survey’s aspect ratio.

  • cmap (str, default "RdBu_r")

  • clim ((float, float), optional) – Explicit, zero-centred color limits; overrides clim_pct.

  • clim_pct (float, default 95.0) – Percentile of |value| used to size a symmetric colour range when clim is not given.

  • show_stations (bool, default True) – Overlay the actual station positions as small markers.

  • figsize ((float, float), default (8.0, 6.5)) – Used only when ax is not supplied.

  • 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 axes to draw on.

Return type:

matplotlib.axes.Axes

Raises:

ValueError – If quantity is not "tipper"/"divergence", or part/component is invalid.