pycsamt.emtools#
Electromagnetic processing, diagnostics, tensor analysis, quality control, static-shift correction, source-effect tools, and plotting helpers.
See also
../emtools/index for narrative, runnable examples built module by module (currently: ../emtools/tf).
pycsamt.emtools — public API#
All user-facing functions and constants from the emtools sub-modules, organised by workflow stage.
- class pycsamt.emtools.RoseStyle(bar_style='gradient', bar_color='#e53935', bar_alpha=0.88, bar_edgecolor='none', bar_edgelw=0.4, cmap='YlOrRd', outer_ring_lw=2.5, outer_ring_color='0.12', n_rings=3, ring_color='0.75', ring_ls=':', ring_lw=0.7, ring_labels=None, ring_label_angle=22.5, ring_label_fontsize=7.0, ring_label_color='0.30', ring_label_fmt='{:.0f}', spoke_every=45.0, spoke_color='0.72', spoke_ls=':', spoke_lw=0.7, compass_labels='NESW', compass_fontsize=8.5, compass_color='0.15', compass_fontweight='bold', show_mean=True, mean_color='crimson', mean_lw=2.2, mean_ls='-', show_secondary=True, secondary_color=None, secondary_ls='--', secondary_lw=None, show_annotation=True, annotation_pos=(0.05, 0.93), annotation_fontsize=8.0, annotation_bg='white', annotation_ec='0.25', show_n=True)#
Bases:
objectVisual style bundle shared by all pycsamt rose diagram functions.
Every attribute maps 1-to-1 to a keyword argument accepted by
plot_strike_rose()andplot_phase_tensor_rose(). Pass an instance via thestyle=parameter of either function; individual kwargs still override the style.- Variables:
bar_style ({"gradient", "solid", "bands"}) –
"gradient"— bar height mapped to cmap;"solid"— uniform bar_color;"bands"— one colour per period sub-band (stacked).bar_color (str) – Bar fill colour for
bar_style="solid".bar_alpha (float) – Bar opacity (0–1).
bar_edgecolor (str) – Bar edge colour.
"none"disables edges.bar_edgelw (float) – Bar edge line-width.
cmap (str) – Matplotlib colormap name for
bar_style="gradient".outer_ring_lw (float) – Line-width of the bold outer bounding circle.
outer_ring_color (str) – Colour of the outer bounding circle.
n_rings (int) – Number of concentric reference rings inside the plot.
ring_color (str) – Colour of concentric rings.
ring_ls (str) – Line-style of concentric rings (
":"dotted,"--"dashed, …).ring_lw (float) – Line-width of concentric rings.
ring_labels (list[float] or None) – Explicit count values to annotate on the rings (e.g.
[25, 50, 75, 100]).None→ evenly auto-spaced.ring_label_angle (float) – Clockwise degrees from North where count labels appear.
ring_label_fontsize (float) – Font size for ring count annotations.
ring_label_color (str) – Colour for ring count annotations.
ring_label_fmt (str) – Python format string for ring labels (e.g.
"{:.0f}").spoke_every (float) – Angular spacing (degrees) between radial spokes / tick marks.
spoke_color (str) – Colour of radial spokes.
spoke_ls (str) – Line-style of radial spokes.
spoke_lw (float) – Line-width of radial spokes.
compass_labels ({"NESW", "degrees", "none"}) – Perimeter labels.
"NESW"→ N/E/S/W;"degrees"→ 0°/45°/…;"none"→ hidden.compass_fontsize (float) – Font size for compass / degree perimeter labels.
compass_color (str) – Colour for perimeter labels.
compass_fontweight (str) – Font weight for perimeter labels (
"bold","normal", …).show_mean (bool) – Draw the mean direction as a diameter line through the centre.
mean_color (str) – Colour of the mean direction line.
mean_lw (float) – Line-width of the mean direction line.
mean_ls (str) – Line-style of the mean direction line.
show_secondary (bool) – Draw the 180°-conjugate (axial-symmetry) mean line.
secondary_color (str or None) – Colour of the conjugate line.
None→ same as mean_color.secondary_ls (str) – Line-style of the conjugate line.
secondary_lw (float or None) – Line-width of the conjugate line.
None→ same as mean_lw.show_annotation (bool) – Show the text box with mean angle and count.
annotation_pos ((float, float)) – Axes-fraction
(x, y)position of the annotation box.annotation_fontsize (float) – Font size of the annotation text.
annotation_bg (str) – Background colour of the annotation box.
annotation_ec (str) – Edge colour of the annotation box.
show_n (bool) – Append
n = N(data-point count) to the annotation text.
- Parameters:
bar_style (str)
bar_color (str)
bar_alpha (float)
bar_edgecolor (str)
bar_edgelw (float)
cmap (str)
outer_ring_lw (float)
outer_ring_color (str)
n_rings (int)
ring_color (str)
ring_ls (str)
ring_lw (float)
ring_label_angle (float)
ring_label_fontsize (float)
ring_label_color (str)
ring_label_fmt (str)
spoke_every (float)
spoke_color (str)
spoke_ls (str)
spoke_lw (float)
compass_labels (str)
compass_fontsize (float)
compass_color (str)
compass_fontweight (str)
show_mean (bool)
mean_color (str)
mean_lw (float)
mean_ls (str)
show_secondary (bool)
secondary_color (str | None)
secondary_ls (str)
secondary_lw (float | None)
show_annotation (bool)
annotation_pos (tuple)
annotation_fontsize (float)
annotation_bg (str)
annotation_ec (str)
show_n (bool)
- copy(**overrides)#
Return a shallow copy with overrides applied.
- Parameters:
**overrides (Any) – Any
RoseStyleattribute name and its new value.- Returns:
A new
RoseStyleinstance.- Return type:
- Raises:
ValueError – If an unknown attribute name is passed.
Examples
>>> rs = RoseStyle() >>> rs2 = rs.copy(compass_labels="degrees", show_secondary=False)
- pycsamt.emtools.resolve_rose_style(style, **overrides)#
Resolve style to a
RoseStyle, then apply overrides.- Parameters:
- Return type:
- Raises:
ValueError – If style is a string that does not match a known preset.
TypeError – If style is not a str, RoseStyle, or None.
Examples
>>> rs = resolve_rose_style("pycsamt", compass_labels="degrees") >>> rs.compass_labels 'degrees'
- pycsamt.emtools.ensure_sites(sites, *, recursive=True, on_dup='replace', strict=False, verbose=0)#
Normalize arbitrary user input to a
Sitesobject.This is the single entry-point validator for all
emtoolspublic APIs. It guarantees that downstream code receives aSitesinstance, no matter whether the caller passed a path, anEDIFile/EDICollection, a singleSite, an existingSites, or an iterable of EDI-like items.- sitesAny
User-provided input. Accepts filesystem paths, glob patterns, EDI-like objects (
EDIFile,EDICollection),Site,Sites, or iterables of such.- recursivebool, default=True
When walking directories, recurse into subfolders if the lower-level coercion utility supports it.
on_dup : {“replace”, “keep_first”, “keep_last”, “raise”},
- default=”replace”
Duplicate site-name policy. See
pycsamt.seg.base.to_sites()for semantics.- strictbool, default=False
If
True, raise when no items can be resolved.- verboseint, default=0
Verbosity level.
>0emits warnings about duplicates and coercion steps.
- pycsamt.site.base.Sites
Canonical
Siteswrapper suitable for all processing tools.
- ValueError
If
sitesisNoneor, instrictmode, nothing could be resolved into EDI-like items.- TypeError
If the result is not a
Sitesinstance (indicates a broken installation or import cycle).
All
emtoolsmodule functions should start with:S = ensure_sites(sites, ...)
to guarantee API consistency across the package.
- pycsamt.emtools.bostick_depth_from_rho(rho, freq)#
Bostick depth estimate D(f) from apparent resistivity.
D(f) = 356 × √(ρ_a / f) [metres]
- Parameters:
- Returns:
Depth in metres, same shape as broadcast of rho and freq.
- Return type:
References
Zhang et al. (2025), Eq. (1), Measurement.
- pycsamt.emtools.vertical_resolution_pair(rho, f_lo, f_hi)#
Vertical resolution ΔD between two adjacent frequencies.
ΔD = 356 × √ρ_c × (1/√f_lo − 1/√f_hi) [metres; f_lo < f_hi]
- Parameters:
- Returns:
Vertical resolution in metres. Positive when f_lo < f_hi.
- Return type:
References
Zhang et al. (2025), Eq. (2), Measurement.
- pycsamt.emtools.frequency_for_depth(depth_m, rho)#
Invert the Bostick formula: return the frequency (Hz) that maps to a given depth for a background resistivity rho.
f = ρ × (356 / D)² [Hz]
- Parameters:
- Returns:
Frequency in Hz, same shape as depth_m.
- Return type:
- pycsamt.emtools.frequency_schedule(target_depths, rho_estimate, *, f_min=9600.0, f_max=614400.0, min_resolution_m=None, fill_decades=False, per_decade=3, as_khz=False)#
Design a CSUMT frequency schedule that samples a set of target depths.
Each target depth is converted to a frequency via
frequency_for_depth(), then clipped to [f_min, f_max]. Optionally, intermediate frequencies can be inserted to guarantee a minimum vertical resolution between consecutive depth levels.- Parameters:
target_depths (float or array) – Target depths in metres (deepest first or any order — sorted internally).
rho_estimate (float) – Background apparent resistivity ρ (Ω·m) used for the conversion.
f_min (float, default=9.6e3) – Minimum transmitter frequency in Hz (lower bound of CSUMT range).
f_max (float, default=614.4e3) – Maximum transmitter frequency in Hz (upper bound of CSUMT range).
min_resolution_m (float or None) – If given, insert additional frequencies between adjacent target depths whenever their vertical resolution would exceed this value.
fill_decades (bool, default=False) – If True, add per_decade log-spaced frequencies within each decade of the schedule to smooth coverage.
per_decade (int, default=3) – Number of extra frequencies to insert per decade when fill_decades is True.
as_khz (bool, default=False) – If True, return frequencies in kHz instead of Hz.
- Returns:
Sorted frequencies in Hz (or kHz if as_khz is True).
- Return type:
References
Zhang et al. (2025), “Controlled source ultra-audio frequency magnetotellurics (CSUMT) transmitter”, Measurement.
- pycsamt.emtools.bostick_depth(sites, *, recursive=True, on_dup='replace', strict=False, verbose=0)#
Bostick depth estimate per station per frequency from measured data.
Uses the apparent resistivity derived from the off-diagonal impedance tensor components (geometric mean):
D(f) = 356 × √(ρ_a(f) / f) [metres]
- Parameters:
- Returns:
One row per (station, frequency) with columns:
station,freq_hz,period_s,rho_a_ohmm,depth_m.- Return type:
References
Zhang et al. (2025), Eq. (1).
- pycsamt.emtools.vertical_resolution(sites, *, rho_override=None, recursive=True, on_dup='replace', strict=False, verbose=0)#
Vertical resolution ΔD between adjacent frequencies per station.
For each consecutive pair (f_lo, f_hi) in the station’s frequency list (sorted ascending), computes:
ΔD = D(f_lo) − D(f_hi) [metres]
using the Bostick depths derived from the measured ρ_a. Alternatively, supply rho_override to use a fixed background resistivity with the analytical formula
356 × √ρ × (1/√f_lo − 1/√f_hi).- Parameters:
rho_override (float or None) – If given, use this constant resistivity for all ΔD calculations (analytical formula) instead of the per-frequency ρ_a.
recursive (bool) – Forwarded to
ensure_sites().on_dup (str) – Forwarded to
ensure_sites().strict (bool) – Forwarded to
ensure_sites().verbose (int) – Forwarded to
ensure_sites().
- Returns:
Columns:
station,freq_lo_hz,freq_hi_hz,depth_lo_m,depth_hi_m,delta_depth_m,rho_a_ohmm.- Return type:
- pycsamt.emtools.depth_coverage_table(sites, *, recursive=True, on_dup='replace', strict=False, verbose=0)#
Summary depth-coverage statistics per station.
- Parameters:
- Returns:
One row per station with columns:
station,n_freq,freq_min_hz,freq_max_hz,depth_min_m,depth_max_m,mean_resolution_m,median_resolution_m.- Return type:
- pycsamt.emtools.plot_depth_section(sites, *, log_color=True, sort_by='name', cmap='viridis_r', figsize=(10.0, 5.0), period_axis=True, recursive=True, on_dup='replace', strict=False, verbose=0, ax=None)#
Pseudosection of Bostick depth across stations and periods/frequencies.
Each cell (station × period) is coloured by the Bostick depth D(f) = 356 √(ρ_a / f).
- Parameters:
log_color (bool, default=True) – Color by log10(depth) instead of depth.
sort_by ({"name", "lon", "lat"}) – Station ordering along the x-axis.
cmap (str, default="viridis_r") – Matplotlib colormap name.
period_axis (bool, default=True) – If True y-axis is period (s); otherwise frequency (Hz).
recursive (bool) – Forwarded to
ensure_sites().on_dup (str) – Forwarded to
ensure_sites().strict (bool) – Forwarded to
ensure_sites().verbose (int) – Forwarded to
ensure_sites().ax (matplotlib.axes.Axes, optional)
- Return type:
- pycsamt.emtools.wavenumber(freq, rho=None)#
Effective real wavenumber k [m⁻¹] for CSAMT or free-space propagation.
- Parameters:
- Returns:
k – Wavenumber [m⁻¹].
- Return type:
Notes
The complex earth wavenumber is k₁ = √(i ω μ₀ / ρ). Its real part equals |k₁| / √2 = √(π f μ₀ / ρ). The corresponding wavelength is λ = 2π / k_eff ≈ 2π × 503 × √(ρ/f) [m].
- pycsamt.emtools.sdas_element_pattern(theta_deg, l, k, *, normalize=True)#
Far-field element pattern for a single finite-length SDAS (eq. 7).
The dipole / array axis is the y-axis. The angle θ is measured FROM the y-axis, so θ = 0° is along the dipole (null) and θ = 90° is broadside (maximum).
- Parameters:
theta_deg (float or ndarray) – Angle(s) from the dipole axis [degrees], range [0, 180].
l (float) – SDAS (dipole) physical length [m].
k (float) – Wavenumber [m⁻¹]. Use
wavenumber()to compute for given frequency and resistivity.normalize (bool) – Normalize the peak to 1.0 (default True).
- Returns:
F – |F(θ)| pattern values (≥ 0).
- Return type:
ndarray
Notes
F(θ) = |[cos(kl cosθ/2) − cos(kl/2)]| / |sinθ|. The singularity at θ = 0° and 180° resolves to zero by L’Hôpital’s rule.
- pycsamt.emtools.array_factor(theta_b_deg, N, d, k, beta=0.0)#
Normalised array factor AF_n for an N-element linear PAS (eq. 19).
The angle θ_b is measured FROM BROADSIDE (perpendicular to the array axis). θ_b = 0° is the maximum direction when β = 0; θ_b = ±90° is along the array (end-fire direction).
- Parameters:
theta_b_deg (float or ndarray) – Broadside angle(s) [degrees], range [−90, 90].
N (int) – Number of SDAS elements.
d (float) – Element-to-element spacing [m].
k (float) – Wavenumber [m⁻¹].
beta (float) – Inter-element phase shift [rad]. β = 0 → broadside array; use
beam_steer()to compute β for a target angle.
- Returns:
AF – Normalised |AF_n(θ_b)| ∈ [0, 1].
- Return type:
ndarray
Notes
AF_n = sin(N ψ/2) / [N sin(ψ/2)], ψ = k d sinθ_b + β.
- pycsamt.emtools.pas_pattern(theta_b_deg, N, d, k, beta=0.0, l=1000.0, *, normalize=True)#
Total normalised far-field pattern of an N-element PAS.
The combined pattern is the product of the SDAS element pattern and the array factor, evaluated at the same observation angle.
- Parameters:
theta_b_deg (float or ndarray) – Broadside angle(s) [degrees], range [−90, 90].
N (int) – Number of SDAS elements.
d (float) – Element spacing [m].
k (float) – Wavenumber [m⁻¹].
beta (float) – Inter-element phase shift [rad].
l (float) – SDAS length [m] (default 1000 m matching gxac023).
normalize (bool) – Normalize peak to 1.0 (default True).
- Returns:
pattern – Combined |E_total(θ_b)| pattern (≥ 0).
- Return type:
ndarray
- pycsamt.emtools.beam_steer(theta_m_deg, d, k)#
Inter-element phase shift β [rad] to steer the main lobe to θ_m (eq. 23).
- Parameters:
- Returns:
beta – Required phase shift [rad]. Apply the same β to each SDAS via the feed-network inductance delay.
- Return type:
Notes
Condition (eq. 23): β = −k d sinθ_m.
- pycsamt.emtools.steering_angles(N, d, k, beta, *, n_range=3)#
All main-lobe broadside angles [degrees] for the given PAS configuration.
Solves k d sinθ + β = ±2nπ (eq. 21) for n = 0, ±1, ±2, …
- Parameters:
N (int / float) – Array parameters (as in
array_factor()).d (int / float) – Array parameters (as in
array_factor()).k (int / float) – Array parameters (as in
array_factor()).beta (float) – Inter-element phase shift [rad].
n_range (int) – Search over n = −n_range … +n_range (default 3).
- Returns:
angles – Sorted array of main-lobe broadside angles [degrees] inside [−90°, 90°].
- Return type:
ndarray
- pycsamt.emtools.sdas_directivity(l, k, *, n_theta=2000)#
2-D horizontal-plane directivity D₀ = 2π U_max / ∫ U(θ) dθ (eq. 12).
- pycsamt.emtools.snr_gain_db(N)#
SNR improvement of an N-element PAS relative to a single SDAS [dB].
For coherent beam forming, the gain scales as N²: G_PAS / G_SDAS = N² → 10 log₁₀(N²) = 20 log₁₀(N) dB.
- pycsamt.emtools.plot_radiation_pattern(theta_b_deg, patterns, *, labels=None, polar=True, normalize=True, log_scale=False, db_floor=-40.0, title='Radiation pattern', figsize=(7.0, 7.0), ax=None)#
Plot one or more radiation patterns in polar or Cartesian format.
- Parameters:
theta_b_deg (array-like) – Broadside angles [degrees], range [−90, 90].
patterns (array-like or list of array-like) – Pattern amplitude(s). A 2-D array is treated as multiple patterns with shape (n_patterns, n_angles).
labels (list of str or None) – Legend labels for each pattern.
polar (bool) – Polar (default) or Cartesian plot.
normalize (bool) – Normalise each pattern to its peak before plotting.
log_scale (bool) – Convert to dB (20 log₁₀) for the radial / y-axis.
db_floor (float) – Minimum dB value when
log_scale=True(default −40 dB).title (str) – Axes title.
figsize (tuple) – Figure size if a new figure is created.
ax (matplotlib.axes.Axes or None) – Axes to draw on; created if None.
- Returns:
ax
- Return type:
- pycsamt.emtools.classify_field_zones(sites, source_offset=None, *, far_threshold=3.0, near_threshold=0.3, recursive=True, on_dup='replace', strict=False, verbose=0)#
Classify CSAMT measurement zones per station per frequency.
For each (station, frequency) pair the dimensionless field-zone parameter is computed as:
|k·r| = r / δ_B where δ_B = 356 √(ρ_a / f) (metres)
and the zone is assigned as:
- Parameters:
sites (path, EDI-like, Sites, or iterable) – Any input accepted by
ensure_sites().source_offset (float, dict {station: float}, or None) – Source–receiver separation r in metres. If a dict is given, missing stations are skipped (or read from
ed.offset/ed.source_offset).far_threshold (float, default=3.0) – |k·r| threshold for the far-field (plane-wave) zone.
near_threshold (float, default=0.3) – |k·r| threshold below which the near-field zone is declared.
recursive (bool) – Forwarded to
ensure_sites().on_dup (str) – Forwarded to
ensure_sites().strict (bool) – Forwarded to
ensure_sites().verbose (int) – Forwarded to
ensure_sites().
- Returns:
Tidy table, one row per (station, frequency):
station,freq_hz,period_s,offset_m,rho_a_ohmm,delta_bostick_m,kr,zone- Return type:
References
Chen & Yan (2005), J. Geophysics and Engineering 2, 105–120. Yan & Fu (2004), analytical shadow/overprint estimation.
- pycsamt.emtools.near_field_factor(sites, source_offset=None, *, recursive=True, on_dup='replace', strict=False, verbose=0)#
Near-field correction factor for apparent resistivity (equatorial HED).
For the equatorial E_y component from a horizontal electric dipole over a homogeneous half-space, the ratio of the measured E_y to the plane-wave (far-field) E_y is:
F(p) = 1 − 3/p² + 3/p³ p = k·r (complex)
so the apparent resistivity is biased by factor |F(p)|². When |F(p)| ≈ 1 the data are in the plane-wave zone; strong departures indicate near-field contamination.
- Parameters:
- Returns:
Columns:
station,freq_hz,period_s,offset_m,rho_a_ohmm,kr,nf_factor.nf_factor= |F(p)|; close to 1.0 → far-field (safe).nf_factorfar from 1.0 → near-field bias present.
- Return type:
References
Chen & Yan (2005), eqs. (8)–(10).
- pycsamt.emtools.plot_field_zones(sites, source_offset=None, *, far_threshold=3.0, near_threshold=0.3, contour_kr=True, kr_levels=(0.1, 0.3, 1.0, 3.0, 10.0), sort_by='name', period_axis=True, log_y=True, figsize=(10.0, 5.0), recursive=True, on_dup='replace', strict=False, verbose=0, ax=None)#
Pseudosection of CSAMT field zones across stations and frequencies.
Each cell (station × period/frequency) is filled by zone colour:
green → far field (|k·r| ≥ far_threshold)
orange → transition
red → near field (|k·r| < near_threshold)
Dashed white contours of constant |k·r| can be overlaid.
- Parameters:
source_offset (float, dict {station: float}, or None) – Source–receiver separation r in metres.
sort_by ({"name", "lon", "lat"}) – Station ordering along the x-axis.
period_axis (bool, default=True) – If True y-axis shows period (s), else frequency (Hz).
log_y (bool, default=True) – Use a quasi-log y-axis (log-spaced tick labels).
recursive (bool) – Forwarded to
ensure_sites().on_dup (str) – Forwarded to
ensure_sites().strict (bool) – Forwarded to
ensure_sites().verbose (int) – Forwarded to
ensure_sites().ax (matplotlib.axes.Axes, optional) – Draw on existing axes.
- Return type:
- pycsamt.emtools.overprint_beta(rho, freq, offset, *, dh_frac=0.001)#
Ground-wave / surface-wave amplitude ratio β_Ey (%).
Evaluates equation (6) of Yan & Fu (2004) analytically at the surface receiver position broadside to the source dipole.
- Parameters:
- Returns:
beta_pct – β × 100 [%]. Values above
BETA_THRESH_PCT(3 %) indicate potential shadow / source overprint (yan2004).- Return type:
ndarray
Notes
The function uses central finite differences to evaluate the partial derivatives of the Sommerfeld term P = e^{−k₁r}/r and the Foster term N = I₀(p) K₀(q), where k₁ = √(iωμ₀/ρ) is the complex wavenumber and p, q are related to the 3-D distance and depth.
- pycsamt.emtools.detect_source_overprint(sites, source_offset=None, *, beta_threshold=3.0, recursive=True, on_dup='replace', strict=False, verbose=0)#
Per-frequency source overprint β index for a set of CSAMT sites.
Computes the ground-wave / surface-wave ratio β_Ey (yan2004) for every measurement frequency at each site and returns a long-form DataFrame.
- Parameters:
sites (Sites | list) – EDI-like objects or a
Sitescontainer.source_offset (float | dict | None) – Source–receiver offset [m]. A scalar applies to all sites; a dict maps
{station: offset}. If None the function tries to read the offset from site attributes (source_offset,offset,dist).beta_threshold (float) – β [%] above which the overprint flag is raised (default 3.0).
recursive (bool)
on_dup (str)
strict (bool)
verbose (int)
- Returns:
Columns: station, freq_hz, period_s, offset_m, rho_a_ohmm, kr, beta_pct, overprint_flag. Rows with unknown offset have NaN in kr/beta_pct.
- Return type:
pd.DataFrame
- pycsamt.emtools.source_overprint_table(sites, source_offset=None, *, beta_threshold=3.0, f_split=1.0, recursive=True, on_dup='replace', strict=False, verbose=0)#
Per-station summary of source overprint metrics.
In addition to the maximum and mean β values (yan2004), the table includes the log-log ρ_a–frequency slope in the low-frequency (LF) and high-frequency (HF) bands and their difference (da2016). A strongly negative
slope_delta(LF slope << HF slope) indicates a resistivity contrast beneath the source (da2016 §2.2–2.3).- Parameters:
- Returns:
Columns: station, n_freq, offset_m, beta_max_pct, beta_mean_pct, n_overprint, overprint_frac, lf_slope, hf_slope, slope_delta, overprint_flag.
- Return type:
pd.DataFrame
- pycsamt.emtools.plot_overprint_section(sites, source_offset=None, *, beta_threshold=3.0, log_color=True, cmap='hot_r', figsize=(10, 5), period_axis=True, log_y=True, contour_beta=True, beta_levels=(1.0, 3.0, 10.0, 30.0), recursive=True, on_dup='replace', strict=False, verbose=0, ax=None)#
Plot source overprint β pseudo-section (station × frequency).
A colour-coded pseudo-section of the ground-wave / surface-wave ratio β_Ey is drawn for each site. Contour lines at selected β levels highlight the overprint-prone zones.
- Parameters:
beta_threshold (float) – Dashed contour drawn at this level [%] (default 3.0).
log_color (bool) – Use log₁₀(β) colour scale.
cmap (str) – Matplotlib colormap name.
period_axis (bool) – Show periods on the right y-axis when True.
log_y (bool) – Logarithmic frequency axis.
contour_beta (bool) – Overlay β contour lines.
beta_levels (tuple) – β [%] values for contour lines.
ax (matplotlib.axes.Axes or None) – Axes to draw on; created if None.
figsize (tuple)
recursive (bool)
on_dup (str)
strict (bool)
verbose (int)
- Returns:
ax
- Return type:
- pycsamt.emtools.normalize_response(sites, rho_ref=100.0, source_offset=None, *, comp='det', phi_ref_deg=45.0, recursive=True, on_dup='replace', strict=False, verbose=0)#
Normalized apparent resistivity and subtracted phase (Wang & Lin 2023).
For each (station, frequency) pair computes:
ρ_n = ρ_obs / ρ_ref φ_diff = φ_obs − φ_ref
and classifies the measurement zone using the skin-depth formula proposed by Wang & Lin (2023, eq. 1):
δ = 503 √(ρ_a / f) [m]
with thresholds: near (r/δ < 0.5), transition (0.5–4), far (>4).
- Parameters:
sites (Sites | list) – EDI-like objects or a
Sitescontainer.rho_ref (float) – Reference half-space resistivity [Ω·m] (default 100).
source_offset (float | dict | None) – Source–receiver offset r [m]. A dict maps
{station: r}. If None, zone and kr are NaN.comp ({"det", "xy", "yx"}) – Impedance component used for ρ_a and φ (
"det"= geometric-mean determinant).phi_ref_deg (float) – Reference half-space phase [°]. 45° (default) is the far-field plane-wave value for a homogeneous 1-D half-space.
recursive (bool)
on_dup (str)
strict (bool)
verbose (int)
- Returns:
Columns: station, freq_hz, period_s, offset_m, rho_a_ohmm, rho_n, phi_obs_deg, phi_ref_deg, phi_diff_deg, zone, kr.
zone/krareNone/ NaN when no offset is available.- Return type:
References
Wang & Lin (2023), Geophysics **88**(6), E215–E230.
- pycsamt.emtools.correct_near_field(sites, source_offset, *, inplace=False, recursive=True, on_dup='replace', strict=False, verbose=0)#
Correct impedance tensor for CSAMT near-field contamination.
Divides each element of Z by the complex near-field factor F(p):
Z_corrected = Z_obs / F(p)
where F(p) = 1 − 3/p² + 3/p³ is the equatorial HED transfer-function ratio and p = k · r, k = √(i·ω·μ₀ / ρ_a). In the far field F(p) → 1 so no correction is applied; in the near/transition zone the correction restores the plane-wave equivalent impedance.
Uses
_apply_each()to apply the per-site correction and return a newSites(or modify in-place).- Parameters:
- Returns:
Sites with corrected impedance tensors.
- Return type:
References
Wang & Lin (2023), Geophysics **88**(6), E215–E230. Chen & Yan (2005), eqs. (8)–(10).
- pycsamt.emtools.plot_normalized_response(sites, rho_ref=100.0, source_offset=None, *, comp='det', phi_ref_deg=45.0, period_axis=True, figsize=(12.0, 5.0), cmap_rho='RdBu_r', cmap_phi='RdBu', rho_n_lim=None, phi_diff_lim=None, recursive=True, on_dup='replace', strict=False, verbose=0, axes=None)#
Pseudosection of normalized ρ_a and subtracted phase (Wang & Lin 2023).
Produces a two-panel figure analogous to Fig. 8(e–f) of Wang & Lin (2023):
Left panel: ρ_n = ρ_obs / ρ_ref (centred at 1.0; red = high).
Right panel: φ_diff = φ_obs − φ_ref [°] (centred at 0°).
- Parameters:
rho_ref (float) – Reference half-space resistivity [Ω·m].
comp ({"det", "xy", "yx"})
phi_ref_deg (float) – Reference half-space phase [°] (default 45°).
period_axis (bool) – Use period (s) on the y-axis when True (default).
cmap_rho (str) – Matplotlib colormap names for the two panels.
cmap_phi (str) – Matplotlib colormap names for the two panels.
rho_n_lim ((vmin, vmax) or None) – Colour limits for ρ_n. Default: symmetric about 1.
phi_diff_lim ((vmin, vmax) or None) – Colour limits for φ_diff. Default: symmetric about 0.
axes ((ax1, ax2) or None) – Draw on existing axes; created if None.
recursive (bool)
on_dup (str)
strict (bool)
verbose (int)
- Returns:
(ax1, ax2)
- Return type:
References
Wang & Lin (2023), Geophysics **88**(6), E215–E230 (Figs. 8e–f).
- pycsamt.emtools.coverage_score(y_true, y_lo, y_hi)#
Empirical coverage fraction of a prediction interval (kouadio2025 eq. 1).
- Parameters:
y_true (array-like) – Observed values.
y_lo (array-like) – Lower and upper bounds of the predicted interval.
y_hi (array-like) – Lower and upper bounds of the predicted interval.
- Returns:
cov – Fraction of observations that fall inside [y_lo, y_hi] ∈ [0, 1].
- Return type:
- pycsamt.emtools.rho_coverage(sites, q_lo, q_hi, *, rho_comp='xy', recursive=True, on_dup='replace', strict=False, verbose=0)#
Per-frequency coverage of observed ρ_a within predicted quantile bounds.
For each site and frequency, checks whether the Cagniard apparent resistivity extracted from the observed Z tensor falls inside the predicted interval [L_j, U_j] (kouadio2025 eq. 1):
c_j = 1(L_j ≤ ρ_a,obs,j ≤ U_j)
- Parameters:
q_lo (dict {station: array} or array or scalar) – Lower / upper quantile bounds aligned with each site’s frequency array. When a dict, keys must match station names; sites without a key are skipped. A scalar broadcasts to every frequency of every site.
q_hi (dict {station: array} or array or scalar) – Lower / upper quantile bounds aligned with each site’s frequency array. When a dict, keys must match station names; sites without a key are skipped. A scalar broadcasts to every frequency of every site.
rho_comp ({"xy", "yx"}) – Impedance component used to derive ρ_a (default
"xy").recursive (bool) – Forwarded to
ensure_sites().on_dup (str) – Forwarded to
ensure_sites().strict (bool) – Forwarded to
ensure_sites().verbose (int) – Forwarded to
ensure_sites().
- Returns:
Columns: station, freq_hz, period_s, rho_obs, q_lo, q_hi, covered, width_pct.
coveredis bool;width_pct= 100 × (q_hi−q_lo) / ρ_obs.- Return type:
pd.DataFrame
- pycsamt.emtools.rho_error_stats(sites, model_rho, *, rho_comp='xy', recursive=True, on_dup='replace', strict=False, verbose=0)#
Per-frequency relative error between observed and predicted ρ_a.
Computes ε_j = (ρ_a,pred,j − ρ_a,obs,j) / ρ_a,obs,j × 100 % for each station and frequency, analogous to the error distribution visualised in the k-diagram polar violin (kouadio2025 Fig 2b).
- Parameters:
model_rho (dict {station: array} or Sites-like) – Predicted apparent resistivity. Either a dict mapping station names to 1-D arrays (same length as the corresponding site’s frequency array), or a Sites-like container from which ρ_a is extracted with the same
rho_compsetting.rho_comp ({"xy", "yx"}) – Impedance component.
recursive (bool) – Forwarded to
ensure_sites().on_dup (str) – Forwarded to
ensure_sites().strict (bool) – Forwarded to
ensure_sites().verbose (int) – Forwarded to
ensure_sites().
- Returns:
Columns: station, freq_hz, period_s, rho_obs, rho_pred, rel_err_pct, abs_err_pct.
- Return type:
pd.DataFrame
- pycsamt.emtools.coverage_table(sites, q_lo, q_hi, *, rho_comp='xy', nominal=0.9, recursive=True, on_dup='replace', strict=False, verbose=0)#
Per-station coverage summary.
- Parameters:
- Returns:
Columns: station, n_freq, empirical_cov, mean_width_pct, calibrated_flag.
- Return type:
pd.DataFrame
- pycsamt.emtools.plot_polar_coverage(sites, q_lo, q_hi, *, rho_comp='xy', log_radius=True, n_freq_ticks=8, figsize=(7, 7), title='Coverage evaluation', recursive=True, on_dup='replace', strict=False, verbose=0, ax=None)#
Polar coverage plot: angle ∝ log₁₀(f), radius ∝ ρ_a,obs.
Green markers = observed ρ_a within predicted interval (covered); red = outside. Thin radial segments show each [q_lo, q_hi] range. Every station shares (almost) the same frequency grid, so each angular position is really one frequency shared by every station — the angle axis is labelled with that frequency directly (rather than the otherwise-meaningless default degree ticks) so a reader can tell which part of the band a cluster of misses falls in.
- Parameters:
- Returns:
ax
- Return type:
matplotlib.axes.Axes (polar projection)
- pycsamt.emtools.plot_polar_errors(sites, model_rho, *, rho_comp='xy', n_bins=18, figsize=(7, 7), title='Error distribution', recursive=True, on_dup='replace', strict=False, verbose=0, ax=None)#
Polar rose diagram of relative residuals (ε = (ρ_pred − ρ_obs)/ρ_obs × 100 %).
Each angular sector spans one frequency decade. Bar length = mean |ε| within that sector; red = over-prediction (mean ε > 0), blue = under. Analogous to the polar violin in kouadio2025 Fig 2b.
- pycsamt.emtools.plot_width_drift(sites, q_lo, q_hi, *, rho_comp='xy', n_bands=8, polar=False, figsize=(8, 4), title='Frequency-width drift', recursive=True, on_dup='replace', strict=False, verbose=0, ax=None)#
Mean relative interval width per frequency band (horizon-drift analogue).
Visualises how the predicted interval width — relative to observed ρ_a — changes across the frequency spectrum, a proxy for how model uncertainty grows with probing depth (kouadio2025 § Forecast Horizon Drift).
- Parameters:
- Returns:
ax
- Return type:
- pycsamt.emtools.build_qc_table(sites, *, include_skew=True, recursive=True, on_dup='replace', strict=False, verbose=0, api=None)#
- pycsamt.emtools.confidence_ratio(scores, *, weights=None, n_freq=1, return_error=False)#
Compute the composite confidence ratio from diagnostic scores.
The confidence ratio is a weighted finite-score mean:
\[\mathrm{CR} = \frac{\sum_k w_k s_k \mathbf{1}_{s_k\ finite}} {\sum_k w_k \mathbf{1}_{s_k\ finite}}, \qquad 0 \leq s_k \leq 1.\]The default score vector is
coverage, uncertainty, offdiag, diagonal, phase, spatialwith weights0.35, 0.20, 0.15, 0.10, 0.10, 0.10. Missing scores are ignored and all finite scores are clipped to[0, 1].The optional error is the population spread of available component scores; when only one score is available it falls back to the binomial standard error
sqrt(CR * (1 - CR) / n_freq).
- pycsamt.emtools.frequency_confidence_table(sites, *, method='composite', weights=None, ci_hi=0.95, ci_lo=0.85, relerr_threshold=0.2, offdiag_tolerance_log10=0.35, diagonal_leakage_max=0.35, phase_jump_tolerance_deg=90.0, spatial_tolerance_log10=0.6, spacing_m=200.0, recursive=True, on_dup='replace', strict=False, verbose=0, api=None)#
Return frequency-level confidence scores for EM stations.
The returned table has one row for each station-frequency sample. It is designed as a reusable quality-control source for plots, masking rules, and inversion-preparation reports.
method="presence"scores only finite impedance-tensor availability.method="composite"combines coverage, tensor uncertainty, off-diagonal consistency, diagonal leakage, phase smoothness, and same-frequency spatial coherence.- Parameters:
- Return type:
- pycsamt.emtools.qc_flags(sites, *, min_frac_ok=0.6, min_snr_med=2.0, max_skew_med=6.0, recursive=True, on_dup='replace', strict=False, verbose=0)#
- pycsamt.emtools.station_confidence_table(sites, *, method='composite', weights=None, relerr_threshold=0.2, offdiag_tolerance_log10=0.35, diagonal_leakage_max=0.35, phase_jump_tolerance_deg=90.0, spatial_tolerance_log10=0.6, spacing_m=200.0, recursive=True, on_dup='replace', strict=False, verbose=0, api=None)#
Return station-level confidence scores for EM transfer functions.
method="presence"reproduces the legacy criterion based only on finite tensor rows.method="composite"combines several station trust indicators: finite data coverage, tensor uncertainty when error tensors exist, off-diagonal consistency, diagonal leakage, phase smoothness, and spatial coherence with neighboring stations.
- pycsamt.emtools.plot_confidence_band_summary(sites, *, method='composite', ci_hi=0.95, ci_lo=0.85, figsize=(8.0, 4.0), spacing_m=200.0, recursive=True, on_dup='replace', strict=False, verbose=0, ax=None)#
Plot line-wide confidence statistics for each period sample.
- pycsamt.emtools.plot_confidence_profile(sites, *, method='presence', ci_hi=0.95, ci_lo=0.85, shade_recoverable=True, shade_mode='score', annotate_low=True, station_labels=True, station_label_step=None, show_errorbars=True, smart_ylim=True, ylim=None, weights=None, spacing_m=200.0, figsize=(9.0, 4.0), recursive=True, on_dup='replace', strict=False, verbose=0, ax=None)#
Profile confidence-ratio (CR) scatter plot along the survey line.
Reproduces the Fig. 3 style from Kouadio et al. (2024): one dot per station coloured green (CR >=
ci_hi), pink (ci_lo<= CR <ci_hi), or red (CR <ci_lo), with dashed threshold lines.With
method="presence", CR is the fraction of frequencies with a valid finite Z tensor. Withmethod="composite", CR combines coverage, tensor uncertainty, off-diagonal consistency, diagonal leakage, phase smoothness, and neighbor coherence.- Parameters:
ci_hi (float) – Upper CR threshold (default 0.95, “safe”, green).
ci_lo (float) – Lower CR threshold (default 0.85, “recoverable”, pink).
shade_recoverable (bool) – If
True, draw an interval cue for stations belowci_hi.shade_mode ({"score", "full", "none"}) –
"score"draws compact vertical intervals tied to each station point."full"preserves the older full-height station shading."none"disables station interval shading.station_label_step (int or None) – Gap between visible station labels on the top axis.
Nonechooses a readable spacing automatically while keeping all station tick marks.show_errorbars (bool) – If
True, draw the station-level confidence uncertainty returned bystation_confidence_table().smart_ylim (bool) – If
True, zoom the lower y-limit when every station confidence is aboveci_loso small departures from the safe threshold remain visible.ylim (tuple of float or None) – Explicit y-axis limits. Overrides
smart_ylimwhen provided.spacing_m (float) – Fallback station spacing [m] used when no coordinate metadata is available on the EDI objects.
figsize (tuple) – Figure size when a new figure is created.
recursive (bool) – Passed to
ensure_sites().on_dup (str) – Passed to
ensure_sites().strict (bool) – Passed to
ensure_sites().verbose (int) – Passed to
ensure_sites().ax (matplotlib.axes.Axes or None) – Axes to draw on; created if None.
method (str)
annotate_low (bool)
station_labels (bool)
- Returns:
ax
- Return type:
- pycsamt.emtools.plot_frequency_confidence_psection(sites, *, method='composite', ci_hi=0.95, ci_lo=0.85, metric='confidence', cmap='RdYlGn', section='dynamic', figsize=None, station_label_step=None, station_preset='pseudosection', station_style=None, spacing_m=200.0, recursive=True, on_dup='replace', strict=False, verbose=0, ax=None)#
Plot frequency confidence as a station-period pseudo-section.
- Parameters:
- Return type:
- pycsamt.emtools.plot_station_confidence_dashboard(sites, *, station=None, method='composite', ci_hi=0.95, ci_lo=0.85, axes=None, figsize=(10.5, 6.0), spacing_m=200.0, recursive=True, on_dup='replace', strict=False, verbose=0)#
Plot a 2-by-3 confidence dashboard for one station.
The dashboard separates the final confidence score from the diagnostic components used to build it, avoiding the visual crowding of a single overlay axis.
- pycsamt.emtools.plot_station_confidence_spectrum(sites, *, station=None, method='composite', ci_hi=0.95, ci_lo=0.85, figsize=(7.0, 4.0), spacing_m=200.0, recursive=True, on_dup='replace', strict=False, verbose=0, ax=None)#
Plot confidence components versus period for one station.
- pycsamt.emtools.plot_coverage_psection(sites, *, metric='presence', alpha_by='none', section='dynamic', figsize=None, recursive=True, on_dup='replace', strict=False, verbose=0, ax=None)#
Plot frequency coverage and data availability as a pseudosection.
- pycsamt.emtools.plot_snr_hist(sites, *, bins=40, figsize=(7.2, 3.6), recursive=True, on_dup='replace', strict=False, verbose=0, ax=None)#
Plot the distribution of signal-to-noise ratios across survey data.
- pycsamt.emtools.plot_qc_quicklook(sites, *, axes=None, figsize=(10.0, 8.0), recursive=True, on_dup='replace', strict=False, verbose=0)#
Create a compact multi-panel quality-control summary for a survey.
- pycsamt.emtools.plot_consistency_fan(sites, *, station=None, other=None, comps=('xy', 'yx'), pcts=(10.0, 50.0, 90.0), n_draws=200, figsize=(8.6, 4.2), recursive=True, on_dup='replace', strict=False, verbose=0, ax=None)#
Plot cross-station response consistency as a fan diagram.
- pycsamt.emtools.plot_xyyx_crossover_map(sites, *, figsize=(9.0, 4.6), recursive=True, on_dup='replace', strict=False, verbose=0, ax=None)#
Map XY/YX crossover behaviour across stations and frequencies.
- pycsamt.emtools.overlay_noise_cone(ax, period, lo, hi, *, color='0.6', alpha=0.18)#
Overlay lower and upper noise envelopes on an existing period axis.
- pycsamt.emtools.overlay_spectral_holes(ax, sites, *, thresh_dec=0.3, recursive=True, on_dup='replace', strict=False, verbose=0)#
Highlight gaps in spectral coverage on an existing QC plot.
- pycsamt.emtools.notch_powerline(sites, *, mains_hz=50.0, n_harm=30, tol_hz=0.08, mode='interp', also='both', inplace=False, recursive=True, on_dup='replace', strict=False, verbose=0)#
Suppress mains-frequency harmonics in impedance and tipper data.
- pycsamt.emtools.smooth_logfreq(sites, *, win=5, kind='tri', also='both', gate_snr=None, inplace=False, recursive=True, on_dup='replace', strict=False, verbose=0)#
- pycsamt.emtools.smooth_rho_phase(sites, *, components='offdiag', degree=3, min_points=None, smooth_rho=True, smooth_phase=True, robust=True, robust_iters=3, blend=1.0, inplace=False, recursive=True, on_dup='replace', strict=False, verbose=0)#
Smooth apparent resistivity and phase trends, then rebuild
Z.The function operates station-by-station along the frequency axis. It fits polynomial trends versus \(\log_{10}(f)\) to \(\log_{10}(\rho_a)\) and to unwrapped impedance phase, then writes the corresponding complex impedance back into each selected tensor component. This keeps apparent resistivity and phase physically coupled through the same complex
Ztensor instead of only smoothing display arrays.- sitesobject
Any input accepted by
ensure_sites().
components : {“offdiag”, “diagonal”, “all”, “xx”, “xy”, “yx”, “yy”}
- or sequence, default “offdiag”
Tensor components to smooth. The default targets
xyandyxbecause they are the usual MT/CSAMT apparent-resistivity and phase components used for interpretation and 2-D preparation.- degreeint, default 3
Polynomial degree for the log-frequency trend. It is automatically reduced when a station has too few valid frequencies.
- min_pointsint or None, default None
Minimum number of finite points required per component. If
None, usesdegree + 2.- smooth_rho, smooth_phasebool, default True
Select whether the impedance amplitude, phase angle, or both are replaced by the fitted trend.
- robustbool, default True
Use a Tukey-style iteratively reweighted polynomial fit to reduce the influence of isolated spikes.
- robust_itersint, default 3
Maximum robust reweighting iterations.
- blendfloat, default 1.0
Blend between original and smoothed curves.
1fully applies the trend;0.5applies half of the correction.- inplacebool, default False
If
True, mutate the normalized input sites. Otherwise work on a best-effort copy of the underlying EDI objects and return newSites.- recursive, on_dup, strict, verbose
Forwarded to
ensure_sites()/to_edis.
- pycsamt.site.base.Sites
Sites containing the smoothed impedance tensors.
Apparent resistivity is smoothed in logarithmic space because \(\rho_a\) commonly spans orders of magnitude. Phase is unwrapped before fitting so that crossings near \(\pm 180^\circ\) do not create artificial jumps.
- pycsamt.emtools.shrink_to_group_trend(sites, *, groups=None, group_key=None, lam=0.25, gate_harm=True, mains_hz=50.0, n_harm=30, tol_hz=0.08, also='both', inplace=False, recursive=True, on_dup='replace', strict=False, verbose=0)#
- pycsamt.emtools.remove_noise_pipeline(sites, *, mains_hz=50.0, n_harm=30, tol_hz=0.08, notch_mode='interp', smooth_win=5, smooth_kind='tri', gate_snr=2.5, group_shrink=False, shrink_lam=0.25, groups=None, also='both', inplace=False, recursive=True, on_dup='replace', strict=False, verbose=0)#
- Parameters:
- pycsamt.emtools.hampel_filter_freq(sites, *, win=3, nsig=3.0, on='both', domain='reim', inplace=False, recursive=True, on_dup='replace', strict=False, verbose=0)#
Remove frequency-domain outliers with a sliding Hampel filter.
- pycsamt.emtools.spatial_median_filter(sites, *, half_window=2, lam=0.25, on='z', inplace=False, recursive=True, on_dup='replace', strict=False, verbose=0)#
- pycsamt.emtools.fixed_length_moving_average(sites, *, window=5, component='all', frequency_rtol=1e-06, inplace=False, recursive=True, on_dup='replace', strict=False, verbose=0)#
Apply a fixed-length EMAP moving average along a profile.
The filter replaces each selected impedance component by the local arithmetic mean of neighboring stations at the same frequency. It is a v2 functional equivalent of the classic FLMA idea and preserves the input frequency grids.
- pycsamt.emtools.trimmed_moving_average(sites, *, window=5, component='all', frequency_rtol=1e-06, inplace=False, recursive=True, on_dup='replace', strict=False, verbose=0)#
Apply a trimmed EMAP moving average along a profile.
The filter is similar to
fixed_length_moving_average(), but when a full enough window is available it removes the smallest and largest magnitudes before averaging. This makes the profile smoothing less sensitive to isolated bad stations.
- pycsamt.emtools.apply_emap_filter(sites, *, method='ama', window=5, window_m=1500.0, spacing_m=200.0, component='all', comp='det', frequency_rtol=1e-06, inplace=False, recursive=True, on_dup='replace', strict=False, verbose=0)#
Apply one EMAP-style spatial filter to MT/AMT sites.
method='ama'delegates tocorrect_static_shift(), the existing Hanning adaptive moving-average correction.'flma'and'tma'apply count-based spatial smoothing along station order.
- pycsamt.emtools.confidence_gated_emap_filter(sites, *, before_sites=None, method='flma', confidence_method='composite', component='xy', ci_hi=0.9, ci_lo=0.5, weights=None, blend_power=1.0, window=5, window_m=1500.0, spacing_m=200.0, comp='det', frequency_rtol=1e-06, recursive=True, on_dup='replace', strict=False, verbose=0)#
Apply EMAP filtering only as strongly as confidence requires.
Rows with confidence greater than or equal to
ci_hiare preserved. Rows belowci_loare fully replaced by the EMAP-filtered estimate. Rows between the two limits are linearly blended, with optionalblend_powershaping.- Parameters:
- Return type:
- class pycsamt.emtools.EMAPFilterResult(sites, report, decisions, method, confidence_method, ci_hi, ci_lo)#
Bases:
objectContainer returned by confidence-gated EMAP filtering.
- Parameters:
- pycsamt.emtools.emap_filter_report(before_sites, after_sites, *, component='xy', period_s=None, frequency_hz=None)#
Summarize station-level changes after an EMAP-style filter.
The report compares the selected impedance component before and after a filter. When
period_sorfrequency_hzis provided, the table also includes a reference-row before/after profile value for each station.
- pycsamt.emtools.plot_emap_filter_profile(before_sites, after_sites=None, *, method='flma', component='xy', period_s=None, frequency_hz=None, window=5, window_m=1500.0, spacing_m=200.0, comp='det', figsize=(9.5, 4.0), station_label_step=1, station_preset='pseudosection', station_style=None, ax=None, **filter_kws)#
Plot a before/after EMAP filter station profile.
- Parameters:
- Return type:
- pycsamt.emtools.plot_emap_filter_psection(before_sites, after_sites=None, *, method='flma', component='xy', window=5, window_m=1500.0, spacing_m=200.0, comp='det', cmap='RdYlBu_r', delta_cmap='RdBu_r', clim=None, clim_pct=(2.0, 98.0), delta_vlim=None, delta_vlim_pct=95.0, axes=None, figsize=(11.0, 8.2), station_label_step=1, station_preset='pseudosection', station_style=None, **filter_kws)#
Plot before/after/delta pseudo-sections for an EMAP filter.
- Parameters:
before_sites (Any)
after_sites (Any | None)
method (str)
component (str)
window (int)
window_m (float)
spacing_m (float)
comp (str)
cmap (str)
delta_cmap (str)
delta_vlim (float | None)
delta_vlim_pct (float)
station_label_step (int | None)
station_preset (str)
station_style (Any | None)
filter_kws (Any)
- Return type:
- pycsamt.emtools.rpca_offdiag_denoise(sites, *, rank=2, keep_phase=True, inplace=False, recursive=True, on_dup='replace', strict=False, verbose=0)#
- pycsamt.emtools.enforce_offdiag_consistency(sites, *, mode='anti', lam=0.5, inplace=False, recursive=True, on_dup='replace', strict=False, verbose=0)#
- pycsamt.emtools.mask_incoherent_freqs(sites, *, snr_thresh=2.5, min_frac=0.4, also='both', inplace=False, recursive=True, on_dup='replace', strict=False, verbose=0)#
Mask frequencies that fail the requested cross-station SNR vote.
- pycsamt.emtools.drop_freqs_manual(sites, *, drop_freqs=(), tol_rel=0.005, inplace=False, recursive=True, on_dup='replace', strict=False, verbose=0)#
Drop Z (and tipper) rows at user-specified frequencies.
- Parameters:
sites (object) – Any input accepted by
ensure_sites().drop_freqs (sequence of float) – Frequencies (Hz) to remove. Each value is matched within
tol_relrelative tolerance:|f - f_drop| / f_drop < tol_rel.tol_rel (float, default 0.005) – Relative frequency tolerance for matching (≈0.5%).
inplace (bool, default False) – If
Truemutate; otherwise work on a copy.recursive (bool)
on_dup (str)
strict (bool)
verbose (int)
- Returns:
Sites with the specified frequency rows removed from Z, Z errors, apparent resistivity/phase, and tipper arrays where present.
- Return type:
- pycsamt.emtools.correct_static_shift(sites, *, window_m=1500.0, spacing_m=200.0, comp='det', inplace=False, recursive=True, on_dup='replace', strict=False, verbose=0)#
Remove static shift via Hanning adaptive moving-average (AMA) spatial filter.
Implements the Torres-Verdín & Bostick (1992) approach used in Kouadio et al. (2024):
For each frequency, build the spatial log(ρ_a) profile across stations.
Apply a Hanning low-pass spatial filter with full-width
window_m.The static-shift correction factor at station i is
C_i = sqrt(ρ_smooth_i / ρ_obs_i)(in log space:log C = 0.5 (log ρ_smooth − log ρ_obs)).Update every Z component:
Z_corrected = Z × C.
- Parameters:
window_m (float) – Full Hanning window width [m] (Torres-Verdín W_H). Stations further than
window_m/2contribute zero weight.spacing_m (float) – Fallback station spacing [m] used when EDI metadata carries no coordinate information.
comp ({"det", "xy", "yx"}) – Apparent-resistivity component used to estimate the static shift.
"det"uses the arithmetic mean of |Z_xy|² and |Z_yx|².inplace (bool) – If True, modify the input sites in place. If False (default), return a new Sites object with corrected Z tensors.
recursive (bool) – Passed to
ensure_sites().on_dup (str) – Passed to
ensure_sites().strict (bool) – Passed to
ensure_sites().verbose (int) – Passed to
ensure_sites().
- Returns:
Sites with static-shift-corrected Z tensors (when
inplace=False).- Return type:
- pycsamt.emtools.nr_qc_delta_offdiag_psection(sites, *, method='pipeline', vlim=None, figsize=(9.0, 4.8), ax=None, **denoise)#
Plot denoising changes in off-diagonal impedance as a pseudosection.
- pycsamt.emtools.nr_qc_snr_gain_profile(sites, *, method='pipeline', pband=None, figsize=(8.6, 3.6), ax=None, **denoise)#
Plot the station-by-station SNR gain produced by denoising.
- pycsamt.emtools.nr_qc_harmonic_waterfall(sites, *, method='notch', mains_hz=50.0, n_harm=30, tol_hz=0.08, figsize=(9.0, 4.6), ax=None, **denoise)#
Plot harmonic-noise reduction by station and mains harmonic.
- pycsamt.emtools.nr_qc_station_offdiag_curves(sites, *, method='pipeline', station=None, mains_hz=50.0, n_harm=12, tol_hz=0.08, figsize=(8.0, 4.2), ax=None, **denoise)#
Compare raw and denoised off-diagonal curves for one station.
- pycsamt.emtools.estimate_ss_ama(sites, *, sort_by='lon', half_window=3, weights='tri', pband=None, max_skew=6.0, robust_freq='median', robust_overall='median', recursive=True, on_dup='replace', strict=False, verbose=0, api=None)#
Estimate AMA static-shift correction factors.
Computes the Adaptive Moving-Average (AMA) spatial log10-resistivity trend across half_window neighbours, then returns the per-station deviation from that trend as a correction-factor table.
- Parameters:
sites (Sites, str, Path, list, EDICollection) – EDI data source accepted by
ensure_sites().sort_by (str, default
'lon') – Along-line order axis.'lon','lat', or'name'.half_window (int, default 3) – Neighbours on each side of the target.
weights (str, default
'tri') – Spatial weight scheme:'tri'(triangular),'gauss', or'uniform'.pband (tuple of float or None) – Period band
(p_min_s, p_max_s)in seconds.Noneuses all periods.max_skew (float or None, default 6.0) – Phase-tensor skew threshold. Points where
|beta| > max_skeware excluded.robust_freq (str, default
'median') – Neighbour aggregation per frequency.robust_overall (str, default
'median') – Reduce per-frequency deltas to a scalar.recursive (bool, default True) – Recursive EDI directory search.
on_dup (str, default
'replace') – Duplicate-station resolution.strict (bool, default False) – Raise on EDI parse errors.
verbose (int, default 0) – Verbosity level.
api (bool or None) – Return an APIFrame when True.
- Returns:
One row per station with columns:
stationStation identifier.
delta_log10_rhoEstimated log10 shift. Positive = rho above spatial trend.
fac_rhoResistivity correction factor \(10^{-\delta}\).
fac_zImpedance correction factor \(10^{-0.5\delta}\).
n_usedFrequencies used in the estimate.
- Return type:
See also
correct_ss_amaestimate + apply in one call.
apply_ss_factorsapply a pre-built table.
Examples
from pycsamt.api import read_edis from pycsamt.emtools.ss import ( estimate_ss_ama, ) survey = read_edis("L22PLT/") sites = survey.collection tbl = estimate_ss_ama( sites, half_window=3, sort_by="lon", ) print( tbl[[ "station", "delta_log10_rho", "fac_z", ]] )
- pycsamt.emtools.apply_ss_factors(sites, factors, *, key='fac_z', inplace=False, recursive=True, on_dup='replace', strict=False, verbose=0)#
Apply pre-computed static-shift correction factors to sites.
Scales each site’s impedance tensor Z by a per-station correction factor from a table (e.g. from
estimate_ss_ama(),estimate_ss_loess(), etc.) or dictionary.- Parameters:
sites (any) – EDI data source accepted by
ensure_sites().factors (dict or pandas.DataFrame) – If DataFrame, must contain
'station'and key columns. If dict, maps station names to correction factors.key (str, default
'fac_z') – Column name or dict key holding the impedance scaling factors. Common choices are'fac_z'(impedance) or'fac_rho'(resistivity).inplace (bool, default False) – Modify the input Sites object. When False, a corrected copy is returned.
recursive (bool, default True) – Recursive EDI directory search.
on_dup (str, default
'replace') – Duplicate-station resolution.strict (bool, default False) – Raise on EDI parse errors.
verbose (int, default 0) – Verbosity level.
- Returns:
Corrected Sites object (same type as input). When inplace is True the original is modified and returned.
- Return type:
See also
estimate_ss_amaEstimate factors via AMA.
estimate_ss_loessEstimate factors via LOESS.
- pycsamt.emtools.correct_ss_ama(sites, *, sort_by='lon', half_window=3, weights='tri', pband=None, max_skew=6.0, robust_freq='median', robust_overall='median', inplace=False, recursive=True, on_dup='replace', strict=False, verbose=0)#
Correct static shift by the AMA method.
Estimates per-station log10-resistivity shift factors with
estimate_ss_ama(), then scales each site’s impedance tensor Z by the correspondingfac_zcolumn.- Parameters:
sites (Sites, str, Path, list, EDICollection) – EDI data source.
sort_by (str, default
'lon') – Along-line order axis for AMA estimation.half_window (int, default 3) – Neighbours on each side of the target.
weights (str, default
'tri') – Spatial weight scheme ('tri','gauss', or'uniform').pband (tuple of float or None) – Period band
(p_min_s, p_max_s)in seconds.max_skew (float or None, default 6.0) – Phase-tensor skew exclusion threshold.
robust_freq (str, default
'median') – Neighbour aggregation per frequency.robust_overall (str, default
'median') – Reduce per-frequency deltas to a scalar.inplace (bool, default False) – Modify the input Sites object in place. When False, returns a corrected copy.
recursive (bool, default True) – Recursive EDI directory search.
on_dup (str, default
'replace') – Duplicate-station resolution.strict (bool, default False) – Raise on EDI parse errors.
verbose (int, default 0) – Verbosity level.
- Returns:
Corrected Sites object (same type as input). When inplace is True the original object is modified and returned.
- Return type:
See also
estimate_ss_amainspect factors before apply.
apply_ss_factorsapply a custom factor table.
Examples
from pycsamt.api import read_edis from pycsamt.emtools.ss import ( correct_ss_ama, ) survey = read_edis("L22PLT/") sites = survey.collection sites_corr = correct_ss_ama( sites, half_window=3, sort_by="lon", )
- pycsamt.emtools.estimate_ss_loess(sites, *, half_window=3, poly=1, it=2, pband=None, max_skew=6.0, summary='median', recursive=True, on_dup='replace', strict=False, verbose=0, api=None)#
Estimate static-shift factors via locally-weighted regression (LOESS).
Fits a local polynomial trend across neighbouring stations in the along-line direction, then returns the per-station deviation from that trend as correction factors.
- Parameters:
sites (Sites, str, Path, list, EDICollection) – EDI data source accepted by
ensure_sites().half_window (int, default 3) – Neighbours on each side of the target.
poly (int, default 1) – Polynomial degree (0=constant, 1=linear).
it (int, default 2) – Robust iteration count.
pband (tuple of float or None) – Period band \((p_{min}, p_{max})\) in seconds.
Noneuses all periods.max_skew (float or None, default 6.0) – Phase-tensor skew threshold. Points where :math:`|\\beta| > ` max_skew are excluded.
summary (str, default
'median') – Per-station aggregation:'median'or'mean'.recursive (bool, default True) – Recursive EDI directory search.
on_dup (str, default
'replace') – Duplicate-station resolution.strict (bool, default False) – Raise on EDI parse errors.
verbose (int, default 0) – Verbosity level.
api (bool or None) – Return an APIFrame when True.
- Returns:
One row per station with columns:
station,delta_log10_rho,fac_rho,fac_z,n_used.- Return type:
See also
estimate_ss_amaAMA (moving average) method.
estimate_ss_bilateralBilateral filtering method.
- pycsamt.emtools.estimate_ss_bilateral(sites, *, half_window=4, sig_dist=None, sig_val=None, pband=None, max_skew=6.0, summary='median', recursive=True, on_dup='replace', strict=False, verbose=0, api=None)#
Estimate static-shift factors via bilateral filtering.
Applies a combined spatial and range-based Gaussian filter (bilateral filter) to compute a local trend, then returns per-station deviations as correction factors.
- Parameters:
sites (Sites, str, Path, list, EDICollection) – EDI data source accepted by
ensure_sites().half_window (int, default 4) – Spatial window (neighbours each side).
sig_dist (float or None) – Spatial Gaussian width (in index units). When
None, defaults to \(0.5 \\times \\texttt{half\\_window}\).sig_val (float or None) – Range (value) Gaussian width. When
None, estimated from data.pband (tuple of float or None) – Period band \((p_{min}, p_{max})\) in seconds.
max_skew (float or None, default 6.0) – Phase-tensor skew threshold.
summary (str, default
'median') – Aggregation:'median'or'mean'.recursive (bool) – Forwarded to
ensure_sites().on_dup (str) – Forwarded to
ensure_sites().strict (bool) – Forwarded to
ensure_sites().verbose (int) – Forwarded to
ensure_sites().api (bool or None) – Return an APIFrame when True.
- Returns:
One row per station with columns:
station,delta_log10_rho,fac_rho,fac_z,n_used.- Return type:
See also
estimate_ss_amaMoving-average method.
estimate_ss_loessLocal polynomial method.
- pycsamt.emtools.estimate_ss_refmedian(sites, *, pband=None, max_skew=6.0, smooth_sites=0, summary='median', recursive=True, on_dup='replace', strict=False, verbose=0, api=None)#
Estimate static-shift factors via reference-median method.
Computes a global frequency-wise median resistivity across all stations, then estimates per-station shifts as deviations from this reference curve.
- Parameters:
sites (Sites, str, Path, list, EDICollection) – EDI data source.
pband (tuple of float or None) – Period band \((p_{min}, p_{max})\) in seconds.
max_skew (float or None, default 6.0) – Phase-tensor skew threshold.
smooth_sites (int, default 0) – Optional smoothing window (reserved for future use).
summary (str, default
'median') – Aggregation:'median'or'mean'.recursive (bool) – Forwarded to
ensure_sites().on_dup (str) – Forwarded to
ensure_sites().strict (bool) – Forwarded to
ensure_sites().verbose (int) – Forwarded to
ensure_sites().api (bool or None) – Return an APIFrame when True.
- Returns:
One row per station with columns:
station,delta_log10_rho,fac_rho,fac_z,n_used.- Return type:
See also
estimate_ss_amaMoving-average method.
estimate_ss_loessLocal polynomial method.
- pycsamt.emtools.plot_ss_delta_psection(before, after, *, axis_y='logperiod', vlim=None, pband=None, figsize=(9.0, 4.8), verbose=0, ax=None)#
Plot pseudosection of static-shift change (corrected minus original).
Displays a heatmap showing the pointwise difference \(\Delta\log_{10}\rho = \rho_{after} - \rho_{before}\) across all stations and frequencies on a log-period y-axis.
- Parameters:
before (any) – EDI data source (uncorrected sites).
after (any) – EDI data source (corrected sites).
axis_y (str, default
'logperiod') – Y-axis scale:'logperiod'or'period'.vlim (float or None) – Symmetric colour range \(\pm \texttt{vlim}\). When
None, auto-scales from data.pband (tuple of float or None) – Period band \((p_{min}, p_{max})\) in seconds.
verbose (int, default 0) – Verbosity level.
ax (matplotlib.axes.Axes or None) – Draw on existing axes.
- Return type:
- pycsamt.emtools.plot_ss_station_curves(before, after, *, station=None, pband=None, figsize=(7.8, 4.2), verbose=0, ax=None)#
Plot before-and-after apparent-resistivity curves for a single station.
Overlays two 1-D sounding curves (before correction and after correction) on a period x-axis to visualize the magnitude and frequency-dependence of the correction at one location.
- Parameters:
before (any) – Uncorrected EDI data.
after (any) – Corrected EDI data.
station (str or None) – Station identifier. When
None, the first common station is used.pband (tuple of float or None) – Period band \((p_{min}, p_{max})\) in seconds.
verbose (int, default 0) – Verbosity level.
ax (matplotlib.axes.Axes or None) – Draw on existing axes.
- Return type:
- pycsamt.emtools.plot_ss_delta_profile(before, after, *, pband=None, robust='median', figsize=(8.6, 3.6), verbose=0, ax=None)#
Plot per-station static-shift correction amplitudes as a bar chart.
Shows the median (or mean) of the frequency-dependent correction \(\Delta\log_{10}\rho\) at each station, making it easy to identify spatial patterns in the applied corrections.
- Parameters:
before (any) – Uncorrected EDI data.
after (any) – Corrected EDI data.
pband (tuple of float or None) – Period band \((p_{min}, p_{max})\) in seconds.
robust (str, default
'median') – Aggregation method:'median'or'mean'.verbose (int, default 0) – Verbosity level.
ax (matplotlib.axes.Axes or None) – Draw on existing axes.
- Return type:
- pycsamt.emtools.ss_qc_psection(sites, *, method='ama', return_sites=False, axis_y='logperiod', vlim=None, pband=None, figsize=(9.0, 4.8), verbose=0, ax=None, **corr)#
Estimate static-shift correction and plot delta pseudosection.
Combines automatic static-shift estimation with a heatmap visualization in one call. A convenience wrapper around a correction estimator and
plot_ss_delta_psection().- Parameters:
sites (any) – EDI paths or
Sites.method (str, default
'ama') – Correction method:'ama','loess','bilateral', or'refmedian'.return_sites (bool, default False) – When
True, return(ax, corrected_sites).axis_y (str) – Forwarded to
plot_ss_delta_psection().vlim (float | None) – Forwarded to
plot_ss_delta_psection().pband (tuple[float, float] | None) – Forwarded to
plot_ss_delta_psection().figsize (tuple[float, float]) – Forwarded to
plot_ss_delta_psection().verbose (int, default 0) – Verbosity level.
ax (matplotlib.axes.Axes or None) – Draw on existing axes.
**corr – Forwarded to the correction estimator.
- Return type:
matplotlib.axes.Axes or (Axes, Sites)
- pycsamt.emtools.ss_qc_station_curves(sites, *, method='ama', station=None, return_sites=False, pband=None, figsize=(7.8, 4.2), verbose=0, ax=None, **corr)#
Estimate correction and plot before/after curves for one station.
A convenience wrapper combining automatic static-shift estimation with 1-D curve visualization.
- Parameters:
sites (any) – EDI paths or Sites object.
method (str, default
'ama') – Correction method.station (str or None) – Station identifier. When
None, uses the first.return_sites (bool, default False) – When
True, return(ax, corrected_sites).pband (tuple[float, float] | None) – Forwarded to
plot_ss_station_curves().figsize (tuple[float, float]) – Forwarded to
plot_ss_station_curves().verbose (int) – Forwarded to
plot_ss_station_curves().ax (Axes | None) – Forwarded to
plot_ss_station_curves().**corr (Any) – Forwarded to the correction estimator.
- Return type:
matplotlib.axes.Axes or (Axes, Sites)
- pycsamt.emtools.ss_qc_profile(sites, *, method='ama', return_sites=False, pband=None, robust='median', figsize=(8.6, 3.6), verbose=0, ax=None, **corr)#
Estimate correction and plot per-station shift profile.
A convenience wrapper for automatic static-shift estimation with bar-chart visualization of the per-station amplitudes.
- Parameters:
sites (any) – EDI paths or Sites object.
method (str, default
'ama') – Correction method.return_sites (bool, default False) – When
True, return(ax, corrected_sites).pband (tuple[float, float] | None) – Forwarded to
plot_ss_delta_profile().robust (str) – Forwarded to
plot_ss_delta_profile().figsize (tuple[float, float]) – Forwarded to
plot_ss_delta_profile().verbose (int) – Forwarded to
plot_ss_delta_profile().ax (Axes | None) – Forwarded to
plot_ss_delta_profile().**corr (Any) – Forwarded to the correction estimator.
- Return type:
matplotlib.axes.Axes or (Axes, Sites)
- pycsamt.emtools.plot_ss_comparison_psection(logRho_before, logRho_after, *, freqs, station_labels=None, show_delta=True, cmap='RdYlBu_r', delta_cmap='RdBu_r', clim=None, clim_pct=(2.0, 98.0), delta_vlim=None, delta_vlim_pct=95.0, period_up=True, title_before='(a) Before static-shift correction', title_after='(b) After static-shift correction', title_delta='(c) Correction amplitude $\\Delta\\log_{10}\\rho$', suptitle='', xlabel='Station', ylabel='Period (s)', n_yticks=7, colorbar_label='$\\log_{10}\\,\\rho_a$ (Ω·m)', delta_colorbar_label='$\\Delta\\log_{10}\\rho$', tick_label_rotation=45.0, tick_fontsize=7, figsize=None, axes=None)#
Two- or three-panel pseudo-section comparison for static-shift correction.
The before and after panels share a colour scale so that the station-dependent vertical offsets are directly visible. The optional third panel shows the pointwise difference Δ log₁₀ ρ = after − before on a diverging scale, making the spatial pattern of the correction explicit.
- Parameters:
logRho_before (ndarray, shape
(n_st, n_f)) – Log₁₀ apparent resistivity before static-shift correction (Ω·m).logRho_after (ndarray, shape
(n_st, n_f)) – Log₁₀ apparent resistivity after static-shift correction (Ω·m).freqs (ndarray, shape
(n_f,)) – Frequency array in Hz. Need not be sorted.station_labels (list of str or None) – X-axis tick labels. Defaults to
"0", "1", ….show_delta (bool, default
True) – Append a third panel showing Δ log₁₀ ρ.cmap (str, default
"RdYlBu_r") – Colormap for the before/after panels.delta_cmap (str, default
"RdBu_r") – Diverging colormap for the Δ panel.clim ((vmin, vmax) or None) – Explicit colour limits (log₁₀ Ω·m) shared by the before/after panels.
clim_pct ((lo, hi), default
(2.0, 98.0)) – Percentile bounds for automatic clim.delta_vlim (float or None) – Symmetric limit
(−δ, +δ)for the Δ panel. WhenNone, derived from delta_vlim_pct of|Δ|.delta_vlim_pct (float, default
95.0)period_up (bool, default
True) – Long period at the top of each panel (MT convention).title_before (str) – Per-panel titles. Pass
""to suppress.title_after (str) – Per-panel titles. Pass
""to suppress.title_delta (str) – Per-panel titles. Pass
""to suppress.suptitle (str) – Figure-level title.
xlabel (str) – Axis labels.
ylabel (str) – Axis labels.
n_yticks (int, default
7) – Number of log-period y-ticks.colorbar_label (str)
delta_colorbar_label (str)
tick_label_rotation (float, default
45.0) – Station tick rotation (degrees).tick_fontsize (int, default
7)figsize ((w, h) or None) – Override automatic size.
axes (sequence of Axes or None) – Pre-created axes (length 2 without delta, 3 with).
- Returns:
fig
- Return type:
- pycsamt.emtools.plot_ss_1d_curves(logRho_before, logRho_after, *, freqs, stations=None, station_labels=None, n_cols=4, max_stations=16, color_before=<object object>, color_after=<object object>, ls_before=<object object>, ls_after=<object object>, marker_before=<object object>, marker_after=<object object>, marker_size=<object object>, lw=<object object>, log_period=True, show_shift_annotation=True, annotation_fontsize=7, ylabel='$\\log_{10}\\, \\rho_a$ (Ω·m)', xlabel='Period (s)', axes=None, figsize=None, title='', legend_loc='best', show_grid=True)#
Per-station 1-D apparent-resistivity curves: before and after correction.
Lays out a grid of subplots (one per selected station) each showing the before/after sounding curves on a period x-axis. A small annotation reports the mean correction amplitude Δ per station, making it easy to spot outliers.
- Parameters:
logRho_before (ndarray, shape
(n_st, n_f))logRho_after (ndarray, shape
(n_st, n_f))freqs (ndarray, shape
(n_f,)Hz.)stations (list of int, list of str, or None) – Stations to display. Integers are row indices into logRho_before. Strings are matched against station_labels.
None→ all stations, capped at max_stations.station_labels (list of str or None) – Label for each row. Defaults to
"0", "1", ….n_cols (int, default
4) – Subplot grid columns.max_stations (int, default
16) – Cap when stations isNone.color_before (str, default
"#2c7bb6"(blue))color_after (str, default
"#d7191c"(red))ls_before (str, default
"--")ls_after (str, default
"-")marker_before (str)
marker_after (str)
marker_size (float, default
3.0)lw (float, default
1.2)log_period (bool, default
True) – Log-scale period x-axis.show_shift_annotation (bool, default
True) – Print mean Δ log₁₀ ρ in the lower-right corner of each subplot.annotation_fontsize (int, default
7)ylabel (str)
xlabel (str)
figsize ((w, h) or None)
title (str) – Figure-level title.
legend_loc (str, default
"best") – Legend location (first subplot only).show_grid (bool, default
True)
- Returns:
fig
- Return type:
- pycsamt.emtools.plot_ss_summary(logRho_before, logRho_after, *, freqs, station_labels=None, cmap='RdYlBu_r', delta_cmap='RdBu_r', clim=None, clim_pct=(2.0, 98.0), delta_vlim=None, delta_vlim_pct=95.0, period_up=True, n_yticks=7, tick_label_rotation=45.0, tick_fontsize=7, colorbar_label='$\\log_{10}\\,\\rho_a$ (Ω·m)', shift_bar_color='#4c72b0', shift_bar_neg_color='#c44e52', shift_robust='median', suptitle='', axes=None, figsize=None)#
Four-panel summary figure for static-shift correction.
Layout:
┌──────────────┬──────────────┐ │ (a) Before │ (b) After │ shared y-axis · shared colorbar ├──────────────┴──────────────┤ │ (c) Δ log₁₀ ρ section │ diverging colorbar ├──────────────────────────── ┤ │ (d) Per-station shift bar │ positive/negative coloured bars └─────────────────────────────┘
- Parameters:
logRho_before (ndarray, shape
(n_st, n_f))logRho_after (ndarray, shape
(n_st, n_f))freqs (ndarray, shape
(n_f,)Hz.)station_labels (list of str or None) – X-axis tick labels for all panels.
cmap (str, default
"RdYlBu_r")delta_cmap (str, default
"RdBu_r")clim (see
plot_ss_comparison_psection().)clim_pct (see
plot_ss_comparison_psection().)delta_vlim (see
plot_ss_comparison_psection().)delta_vlim_pct (see
plot_ss_comparison_psection().)period_up (bool, default
True)n_yticks (int, default
7)tick_label_rotation (float, default
45.0)tick_fontsize (int, default
7)colorbar_label (str)
shift_bar_color (str) – Bar colour for positive per-station shifts (default blue).
shift_bar_neg_color (str) – Bar colour for negative shifts (default red).
shift_robust (
"median"|"mean") – Aggregation used to reduce per-frequency shifts to a scalar per station for panel (d).suptitle (str) – Figure-level title.
figsize ((w, h) or None)
- Returns:
fig
- Return type:
- pycsamt.emtools.ss_comparison_psection(sites, *, method='ama', return_sites=False, station_labels=None, show_delta=True, cmap='RdYlBu_r', delta_cmap='RdBu_r', clim=None, clim_pct=(2.0, 98.0), delta_vlim=None, delta_vlim_pct=95.0, period_up=True, suptitle='', tick_label_rotation=45.0, tick_fontsize=7, figsize=None, verbose=0, **corr)#
Correct sites for static shift and plot a comparison pseudo-section.
A convenience wrapper that combines
correct_ss_ama()(or the chosen method) withplot_ss_comparison_psection().- Parameters:
sites (any) – EDI paths, glob pattern, or
Sitesaccepted byensure_sites().method (
"ama"|"loess"|"bilateral"|"refmedian") – Static-shift estimator.return_sites (bool, default
False) – WhenTrue, return(fig, corrected_sites)instead of fig.**corr (Any) – Forwarded to the correction estimator.
show_delta (bool)
cmap (str)
delta_cmap (str)
delta_vlim (float | None)
delta_vlim_pct (float)
period_up (bool)
suptitle (str)
tick_label_rotation (float)
tick_fontsize (int)
verbose (int)
**corr
- Returns:
fig – Or
(fig, corrected_sites)when return_sites isTrue.- Return type:
See also
plot_ss_comparison_psectionLower-level function that accepts pre-built arrays directly.
- pycsamt.emtools.plot_ss_radar(sites, *, station=None, pband=None, rotate='pt', rotate_stat='median', rotate_deg=0.0, radial='log10rho', theta_axis='logperiod', fill_between=False, colors=<object object>, marker=<object object>, ms=<object object>, lw=<object object>, ls=<object object>, figsize=(4.8, 4.8), recursive=True, on_dup='replace', strict=False, verbose=0, eps=1e-24, ax=None)#
Plot apparent resistivity against period on a polar grid.
Displays the off-diagonal impedance components (xy and yx) as radial curves on a polar coordinate system, where the azimuthal angle encodes frequency (or period) and the radius encodes resistivity magnitude. Useful for detecting anisotropy and strike angles across the full frequency spectrum.
- Parameters:
sites (any) – EDI data source.
station (str or None) – Station identifier. When
None, uses the first.pband (tuple of float or None) – Period band \((p_{min}, p_{max})\) in seconds.
rotate (str, default
'pt') – Rotation mode:'pt'(phase-tensor strike),'deg'(fixed angle), or'none'(no rotation).rotate_stat (str, default
'median') – Per-frequency aggregation for phase-tensor rotation.rotate_deg (float, default 0.0) – Fixed rotation angle (degrees) when rotate=’deg’.
radial (str, default
'log10rho') – Radial scale:'log10rho'(log base 10 of apparent resistivity) or'rho'(linear resistivity).theta_axis (str, default
'logperiod') – Angular axis:'logperiod','period', or'freq'(Hz).fill_between (bool, default False) – Shade the region between xy and yx curves.
colors (tuple or _UNSET) – (color_xy, color_yx). Defaults from style.
marker (_UNSET or values) – Line and marker style. Defaults from style.
ms (_UNSET or values) – Line and marker style. Defaults from style.
lw (_UNSET or values) – Line and marker style. Defaults from style.
ls (_UNSET or values) – Line and marker style. Defaults from style.
recursive (bool) – Forwarded to
ensure_sites().on_dup (str) – Forwarded to
ensure_sites().strict (bool) – Forwarded to
ensure_sites().verbose (int) – Forwarded to
ensure_sites().eps (float, default 1e-24) – Numerical floor to avoid division by zero.
ax (matplotlib.axes.Axes or None) – Draw on existing axes (auto-creates polar if needed).
- Returns:
Polar axes object.
- Return type:
- pycsamt.emtools.detect_near_surface(sites, *, f_split=1.0, pband=None, ns_threshold=2.0, ss_threshold=0.1, sort_by='lon', half_window=3, weights='tri', max_skew=6.0, recursive=True, on_dup='replace', strict=False, verbose=0, api=None)#
Detect and classify near-surface distortion in CSAMT/MT apparent resistivity curves.
Distinguishes between two types of distortion:
Static effect — frequency-independent multiplicative shift of the whole ρ_a curve. Addressable by AMA/LOESS static-shift correction.
Near-surface effect — frequency-dependent distortion concentrated at high frequencies (f ≥ f_split), caused by shallow inhomogeneities. Not correctable by conventional static-shift methods; 2-D inversion is recommended.
Three per-station diagnostics are computed from the residuals of the ρ_a curve relative to an AMA spatial trend:
NS index η = σ_HF / σ_LF — spread ratio between the high-frequency (f ≥ f_split) and low-frequency bands. η >> 1 is the hallmark of near-surface contamination.
Gradient delta Δγ = |slope_HF − slope_LF| — absolute difference of the log-log slope d(log ρ_a)/d(log f) between the two bands.
Static shift δ = median(log10 ρ_a − AMA trend) — classic AMA shift estimate over the full frequency range.
Classification:
"clean"η ≤ ns_threshold, |δ| ≤ ss_threshold
"static"η ≤ ns_threshold, |δ| > ss_threshold
"near_surface"η > ns_threshold, |δ| ≤ ss_threshold
"mixed"η > ns_threshold, |δ| > ss_threshold
- Parameters:
sites (path, EDI-like, Sites, or iterable) – Any input accepted by
ensure_sites().f_split (float, default=1.0) – Frequency boundary in Hz separating the HF (f ≥ f_split) from the LF (f < f_split) band.
pband ((float, float) or None) – Period band
(lo_s, hi_s)pre-filter applied before all computations.ns_threshold (float, default=2.0) – η > this → near-surface flag.
ss_threshold (float, default=0.1) – |δ| > this (log10 units) → static-shift flag.
sort_by ({"lon", "lat", "name"}, default="lon") – Station ordering for the AMA spatial trend.
half_window (int, default=3) – Number of neighbouring stations each side in the AMA trend.
weights ({"tri", "gauss", "uniform"}, default="tri") – Spatial weighting for the AMA trend.
max_skew (float or None, default=6.0) – Phase-tensor skew ceiling; data above this are excluded.
recursive (bool) – Forwarded to
ensure_sites().on_dup (str) – Forwarded to
ensure_sites().strict (bool) – Forwarded to
ensure_sites().verbose (int) – Forwarded to
ensure_sites().api (bool | None)
- Returns:
One row per station with columns:
station,n_hf,n_lf,sigma_hf,sigma_lf,ns_index,slope_hf,slope_lf,gradient_delta,ss_delta_log10,ns_flag,ss_flag,distortion_type.- Return type:
References
Lei et al. (2017), “The non-static effect of near-surface inhomogeneity on CSAMT data”, Geophysics.
- pycsamt.emtools.plot_ns_detection(sites, *, f_split=1.0, pband=None, ns_threshold=2.0, ss_threshold=0.1, sort_by='lon', half_window=3, weights='tri', max_skew=6.0, show_ss=True, figsize=(9.0, 4.5), recursive=True, on_dup='replace', strict=False, verbose=0, ax=None)#
Bar chart of the NS index per station, colored by distortion type.
Each bar height is η = σ_HF / σ_LF. A dashed line marks ns_threshold. An optional secondary y-axis shows the static-shift estimate δ (log10 units) as a stem plot.
- Parameters:
f_split (float, default=1.0) – HF/LF split frequency in Hz.
ns_threshold (float)
ss_threshold (float)
sort_by ({"lon", "lat", "name"})
half_window (int) – Forwarded to
detect_near_surface().weights (str) – Forwarded to
detect_near_surface().max_skew (float | None) – Forwarded to
detect_near_surface().show_ss (bool, default=True) – If True and ax has room, overlay static-shift δ as a grey stem plot on a secondary y-axis.
recursive (bool) – Forwarded to
ensure_sites().on_dup (str) – Forwarded to
ensure_sites().strict (bool) – Forwarded to
ensure_sites().verbose (int) – Forwarded to
ensure_sites().ax (matplotlib.axes.Axes, optional) – Draw on existing axes.
- Return type:
- pycsamt.emtools.select_band(sites, *, fmin=None, fmax=None, band_hz=None, keep=None, inplace=False, recursive=True, on_dup='replace', strict=False, verbose=0)#
- pycsamt.emtools.drop_duplicates(sites, *, tol=1e-10, inplace=False, recursive=True, on_dup='replace', strict=False, verbose=0)#
- pycsamt.emtools.drop_low_confidence_frequencies(sites, *, method='composite', threshold=0.5, weights=None, also='both', inplace=False, recursive=True, on_dup='replace', strict=False, verbose=0)#
Drop rows whose frequency confidence is below
threshold.The confidence scores are computed with
pycsamt.emtools.qc.frequency_confidence_table(). The operation is station-aware: each station keeps or drops its own bad frequency rows. A newSitesobject is returned unlessinplace=True.
- pycsamt.emtools.edit_frequencies_by_confidence(sites, *, mode='recover', before_sites=None, method='composite', threshold=0.5, ci_hi=0.9, ci_lo=0.5, weights=None, interpolation='linear', reject='drop', also='both', inplace=False, recursive=True, on_dup='replace', strict=False, verbose=0, api=None)#
Edit frequency rows and return diagnostics in one workflow.
This is the high-level confidence-editing entry point. It applies one of the low-level edit strategies and immediately computes a station report and a station-frequency decision table. Use
before_siteswhensitesis already an in-memory object and a reliable before/after comparison is required, because lower-level site editors can mutate the wrapped impedance objects while constructing the edited return value.- Parameters:
sites (path-like, EDI-like, Sites, or sequence) – Input data to edit. Path-like inputs are normally safe to use directly because they can be loaded independently by the package. In-memory objects should be paired with
before_siteswhen the report must preserve an untouched baseline.mode ({'recover', 'drop', 'mask'}, default 'recover') – Frequency-editing strategy.
'recover'interpolates recoverable rows in log-frequency and handles rejected rows according toreject.'drop'removes rows belowthreshold.'mask'keeps the frequency grid but replaces low-confidence tensor rows by missing values when the container allows it.before_sites (optional) – Independent baseline used only for reporting and decision tracking. If omitted,
sitesis used as the baseline.method (str, default 'composite') – Confidence metric passed to
pycsamt.emtools.qc.frequency_confidence_table().threshold (float, default 0.50) – Confidence threshold used by
mode='drop'andmode='mask'.ci_hi (float, default 0.90 and 0.50) – High-confidence and low-confidence limits used by
mode='recover'and by the diagnostic report.ci_lo (float, default 0.90 and 0.50) – High-confidence and low-confidence limits used by
mode='recover'and by the diagnostic report.weights (dict or None, default None) – Optional confidence-metric weights.
interpolation ({'linear', 'nearest'}, default 'linear') – Interpolation strategy for recoverable rows in
mode='recover'.reject ({'drop', 'mask', 'keep'}, default 'drop') – Handling of rows below
ci_loinmode='recover'.also ({'z', 'tipper', 'both'}, default 'both') – Data blocks edited when present.
inplace (bool, default False) – Forwarded to the low-level edit function.
recursive (bool) – Site-loading options forwarded to
ensure_sites().on_dup (str) – Site-loading options forwarded to
ensure_sites().strict (bool) – Site-loading options forwarded to
ensure_sites().verbose (int) – Site-loading options forwarded to
ensure_sites().api (bool | None)
- Returns:
Edited sites together with station-level and station-frequency diagnostics.
- Return type:
- class pycsamt.emtools.FrequencyEditResult(sites, report, decisions, mode, method, ci_hi, ci_lo, reject, interpolation)#
Bases:
objectContainer returned by confidence-based frequency editing.
- Parameters:
- pycsamt.emtools.frequency_edit_decision_table(before_sites, after_sites, *, method='composite', ci_hi=0.9, ci_lo=0.5, weights=None, recursive=True, on_dup='replace', strict=False, verbose=0, api=None)#
Return one row per original station-frequency edit decision.
- pycsamt.emtools.frequency_edit_report(before_sites, after_sites, *, method='composite', ci_hi=0.9, ci_lo=0.5, weights=None, recursive=True, on_dup='replace', strict=False, verbose=0, api=None)#
Summarize station-level changes after frequency editing.
The report compares the native frequency rows and finite tensor rows before and after an edit such as dropping, masking, or recovery. It also carries the median confidence from
pycsamt.emtools.qc.frequency_confidence_table().
- pycsamt.emtools.mask_low_confidence_frequencies(sites, *, method='composite', threshold=0.5, weights=None, also='both', inplace=False, recursive=True, on_dup='replace', strict=False, verbose=0)#
Set low-confidence frequency rows to NaN without changing the grid.
- pycsamt.emtools.plot_frequency_edit_decisions(before_sites, after_sites, *, method='composite', ci_hi=0.9, ci_lo=0.5, figsize=(10.0, 5.0), station_label_step=1, station_preset='pseudosection', station_style=None, ax=None)#
Plot dropped, masked, recovered, and kept frequency decisions.
- pycsamt.emtools.plot_frequency_edit_summary(before_sites, after_sites, *, method='composite', ci_hi=0.9, ci_lo=0.5, figsize=(9.0, 4.0), station_label_step=1, station_preset='pseudosection', station_style=None, ax=None)#
Plot station-level before/after frequency-edit summary.
- pycsamt.emtools.recover_low_confidence_frequencies(sites, *, method='composite', ci_hi=0.9, ci_lo=0.5, weights=None, interpolation='linear', reject='mask', also='both', inplace=False, recursive=True, on_dup='replace', strict=False, verbose=0)#
Recover recoverable frequency rows using trusted neighboring rows.
Rows with confidence in
[ci_lo, ci_hi)are treated as recoverable and are interpolated in log-frequency from rows with confidence>= ci_hi. Rows belowci_loare considered rejected and are either masked, dropped, or kept depending onreject.
- pycsamt.emtools.regrid_to(sites, target_freq, *, method='nearest', inplace=False, recursive=True, on_dup='replace', strict=False, verbose=0)#
- pycsamt.emtools.regrid_logspace(sites, *, fmin=None, fmax=None, band_hz=None, per_decade=6, n_per_decade=None, method='nearest', inplace=False, recursive=True, on_dup='replace', strict=False, verbose=0)#
- pycsamt.emtools.decimate_step(sites, *, step=2, inplace=False, recursive=True, on_dup='replace', strict=False, verbose=0)#
- pycsamt.emtools.smooth_mavg(sites, *, k=3, window=None, on='both', inplace=False, recursive=True, on_dup='replace', strict=False, verbose=0)#
- pycsamt.emtools.align_grid(sites, *, mode='union', ref_station=None, method='nearest', inplace=False, recursive=True, on_dup='replace', strict=False, verbose=0)#
- pycsamt.emtools.plot_coverage_quality_heatmap(sites, *, axis='period', figsize=(7.5, 4.5), recursive=True, on_dup='replace', strict=False, verbose=0, ax=None)#
- pycsamt.emtools.plot_apparent_depth_psection(sites, *, axis_y='period', agg='median', figsize=(7.5, 4.5), log_color=True, recursive=True, on_dup='replace', strict=False, verbose=0, ax=None)#
- pycsamt.emtools.plot_band_microstrips(sites, *, bands=None, n_bands=6, figsize=(9.0, 6.0), marker_size=16.0, recursive=True, on_dup='replace', strict=False, verbose=0, ax=None)#
- pycsamt.emtools.bahr_skewness(Z)#
- pycsamt.emtools.skew_table(sites, *, recursive=True, on_dup='replace', strict=False, verbose=0)#
- pycsamt.emtools.mask_by_skew(sites, *, thresh=6.0, mode='abs_gt', also='both', inplace=False, recursive=True, on_dup='replace', strict=False, verbose=0)#
- pycsamt.emtools.keep_longest_low_skew(sites, *, thresh=3.0, min_len=3, pad=0, also='both', fallback='keep_all', recursive=True, on_dup='replace', strict=False, verbose=0, inplace=False)#
- pycsamt.emtools.close_skew_gaps(sites, *, thresh=3.0, max_gap=1, also='both', recursive=True, on_dup='replace', strict=False, verbose=0, inplace=False)#
- pycsamt.emtools.select_low_skew_band(sites, *, thresh=3.0, frac=0.6, min_len=3, pad=0, also='both', recursive=True, on_dup='replace', strict=False, verbose=0, inplace=False)#
- pycsamt.emtools.plot_skew_traffic_psection(sites, *, t1=3.0, t2=6.0, figsize=(9.0, 4.8), axis_y='logperiod', recursive=True, on_dup='replace', strict=False, verbose=0, ax=None)#
- pycsamt.emtools.plot_skew_percentile_ribbon(sites, *, n_bins=30, q_lo=25.0, q_hi=75.0, extra=(10.0, 90.0), figsize=(8.6, 3.8), recursive=True, on_dup='replace', strict=False, verbose=0, ax=None)#
- pycsamt.emtools.plot_skew_vote_band(sites, *, thresh=3.0, n_bins=40, figsize=(8.6, 3.4), recursive=True, on_dup='replace', strict=False, verbose=0, ax=None)#
- pycsamt.emtools.plot_skewness(f_hz, Z, *, threshold=0.4, ax=None, title=None)#
- pycsamt.emtools.analyze_anisotropy(sites, *, ratio_threshold=0.1, skew_threshold=0.2, recursive=True, on_dup='replace', strict=False, verbose=0)#
Per-frequency anisotropy metrics for a set of CSAMT sites.
Computes the two Cagniard apparent resistivities ρ_xy and ρ_yx (wang2017 eqs 17–18), their log-ratio Λ = log₁₀(ρ_xy/ρ_yx), the phase difference, and the Swift skew from the full Z tensor.
- Parameters:
sites (Sites | list) – EDI-like objects or a
Sitescontainer.ratio_threshold (float) – |log₁₀(Λ)| above which the anisotropy flag is raised (default 0.1).
skew_threshold (float) – Swift skew above which 3-D / anisotropy is suspected (default 0.2).
recursive (bool) – Forwarded to
ensure_sites().on_dup (str) – Forwarded to
ensure_sites().strict (bool) – Forwarded to
ensure_sites().verbose (int) – Forwarded to
ensure_sites().
- Returns:
Columns: station, freq_hz, period_s, rho_xy_ohmm, rho_yx_ohmm, phi_xy_deg, phi_yx_deg, ratio_log10, phase_diff_deg, swift_skew, strike_deg.
- Return type:
pd.DataFrame
Notes
ratio_log10 = 0andswift_skew = 0indicate a perfectly isotropic 1-D earth. Non-zero diagonal Z elements (contributing to swift_skew > 0) suggest 3-D structure or electrical anisotropy (wang2017 §5.3).
- pycsamt.emtools.anisotropy_table(sites, *, ratio_threshold=0.1, skew_threshold=0.2, recursive=True, on_dup='replace', strict=False, verbose=0)#
Per-station summary of anisotropy metrics.
- Parameters:
- Returns:
Columns: station, n_freq, mean_ratio_log10, max_abs_ratio_log10, mean_phase_diff_deg, mean_swift_skew, median_strike_deg, anisotropy_flag.
anisotropy_flagis True when|mean_ratio_log10| > ratio_thresholdORmean_swift_skew > skew_threshold.- Return type:
pd.DataFrame
- pycsamt.emtools.plot_anisotropy(sites, *, metric='ratio_log10', ratio_threshold=0.1, skew_threshold=0.2, cmap='RdBu_r', figsize=(10, 5), period_axis=True, log_y=True, contour_zero=True, recursive=True, on_dup='replace', strict=False, verbose=0, ax=None)#
Plot anisotropy metric pseudo-section (station × frequency).
- Parameters:
metric (str) – Column from
analyze_anisotropy()to map to colour:"ratio_log10"(default),"swift_skew","phase_diff_deg", or"strike_deg".ratio_threshold (float)
skew_threshold (float)
cmap (str) – Colormap (default
"RdBu_r"— diverging, centred at 0).period_axis (bool) – Show period on y-axis (default) rather than frequency.
log_y (bool) – Logarithmic y-axis.
contour_zero (bool) – Draw a white contour at value = 0 (relevant for ratio_log10).
ax (matplotlib.axes.Axes or None)
figsize (tuple)
recursive (bool)
on_dup (str)
strict (bool)
verbose (int)
- Returns:
ax
- Return type:
- pycsamt.emtools.phase_features_table(sites, *, recursive=True, on_dup='replace', strict=False, verbose=0, api=None)#
- pycsamt.emtools.classify_dimensionality(sites, *, skew_th=3.0, ellipt_th=0.2, recursive=True, on_dup='replace', strict=False, verbose=0, api=None)#
- pycsamt.emtools.pre2d_inversion_assessment(sites, *, band=None, skew_th=3.0, ellipt_th=0.2, rotation_applied=False, rotation_method='consensus', groom_bailey_attempted=False, groom_bailey_applied=False, groom_bailey_reason=None, recursive=True, on_dup='replace', strict=False, verbose=0, api=None)#
Summarise dimensionality and strike checks before 2-D inversion.
The table is designed for audit trails and manuscript responses. It combines phase-tensor skew/ellipticity dimensionality labels, impedance sweep strike, phase-tensor strike, consensus strike, and frequency-dependent strike variability. It also records whether data were rotated to strike and whether Groom-Bailey decomposition was attempted/applied.
- Parameters:
- Return type:
- pycsamt.emtools.mask_by_dimensionality(sites, *, keep=(0, 1), inplace=False, recursive=True, on_dup='replace', strict=False, verbose=0)#
- pycsamt.emtools.project_to_2d(sites, *, strike=None, method='swift', antisym=True, inplace=False, recursive=True, on_dup='replace', strict=False, verbose=0)#
- pycsamt.emtools.learn_dim_dictionary(sites, *, n_atoms=6, lam=0.05, n_iter=40, code_iter=50, recursive=True, on_dup='replace', strict=False, verbose=0)#
- pycsamt.emtools.encode_dimensionality(sites, model, *, lam=0.05, code_iter=50, recursive=True, on_dup='replace', strict=False, verbose=0, api=None)#
- pycsamt.emtools.mask_by_dictionary(sites, model, *, keep=(0, 1), lam=0.05, code_iter=50, inplace=False, recursive=True, on_dup='replace', strict=False, verbose=0)#
- pycsamt.emtools.plot_atom_psection(sites, model, *, energy='l2', figsize=(9.0, 4.8), recursive=True, on_dup='replace', strict=False, verbose=0, ax=None)#
- pycsamt.emtools.plot_dim_confidence_grid(sites, *, skew_th=3.0, ellipt_th=0.2, figsize=(8.8, 4.2), recursive=True, on_dup='replace', strict=False, verbose=0, ax=None)#
- pycsamt.emtools.plot_dim_occupancy_area(sites, *, skew_th=3.0, ellipt_th=0.2, n_bands=24, figsize=(8.8, 3.6), recursive=True, on_dup='replace', strict=False, verbose=0, ax=None)#
- pycsamt.emtools.plot_dim_map(sites, *, period=10.0, skew_th=3.0, ellipt_th=0.2, figsize=(8.0, 6.0), recursive=True, on_dup='replace', strict=False, verbose=0, ax=None)#
- class pycsamt.emtools.GroomBaileyResult(sites, table, applied, method)#
Bases:
objectContainer returned by
groom_bailey_decomposition().
- pycsamt.emtools.apply_groom_bailey(sites, table=None, *, band=None, rotate_deg=None, min_freq=4, max_iter=30, tol=1e-06, robust=True, inplace=False, recursive=True, on_dup='replace', strict=False, verbose=0)#
Remove fitted Groom-Bailey galvanic distortion from impedance tensors.
- pycsamt.emtools.groom_bailey_decomposition(sites, *, apply=False, band=None, rotate_deg=None, min_freq=4, max_iter=30, tol=1e-06, robust=True, inplace=False, recursive=True, on_dup='replace', strict=False, verbose=0)#
Estimate, and optionally apply, Groom-Bailey distortion correction.
- pycsamt.emtools.groom_bailey_table(sites, *, band=None, rotate_deg=None, min_freq=4, max_iter=30, tol=1e-06, robust=True, recursive=True, on_dup='replace', strict=False, verbose=0, api=None)#
Estimate Groom-Bailey-style galvanic distortion parameters.
The fitted model is
\[Z_\mathrm{obs}(f) \approx D\,Z_{2D}(f),\]where
Dis a real, frequency-independent 2x2 distortion matrix andZ_2Dis anti-diagonal at each frequency. The fitted matrix is decomposed into gain, twist, shear, and anisotropy-style parameters.
- pycsamt.emtools.estimate_strike_sweep(sites, *, angles=array([-90., -89., -88., -87., -86., -85., -84., -83., -82., -81., -80., -79., -78., -77., -76., -75., -74., -73., -72., -71., -70., -69., -68., -67., -66., -65., -64., -63., -62., -61., -60., -59., -58., -57., -56., -55., -54., -53., -52., -51., -50., -49., -48., -47., -46., -45., -44., -43., -42., -41., -40., -39., -38., -37., -36., -35., -34., -33., -32., -31., -30., -29., -28., -27., -26., -25., -24., -23., -22., -21., -20., -19., -18., -17., -16., -15., -14., -13., -12., -11., -10., -9., -8., -7., -6., -5., -4., -3., -2., -1., 0., 1., 2., 3., 4., 5., 6., 7., 8., 9., 10., 11., 12., 13., 14., 15., 16., 17., 18., 19., 20., 21., 22., 23., 24., 25., 26., 27., 28., 29., 30., 31., 32., 33., 34., 35., 36., 37., 38., 39., 40., 41., 42., 43., 44., 45., 46., 47., 48., 49., 50., 51., 52., 53., 54., 55., 56., 57., 58., 59., 60., 61., 62., 63., 64., 65., 66., 67., 68., 69., 70., 71., 72., 73., 74., 75., 76., 77., 78., 79., 80., 81., 82., 83., 84., 85., 86., 87., 88., 89., 90.]), metric='diag_ratio', band=None, recursive=True, on_dup='replace', strict=False, verbose=0)#
- pycsamt.emtools.estimate_strike_phase_tensor(sites, *, band=None, robust=True, recursive=True, on_dup='replace', strict=False, verbose=0)#
- pycsamt.emtools.estimate_strike_consensus(sites, *, band=None, w_sweep=0.5, w_pt=0.5, angles=array([-90., -89., -88., -87., -86., -85., -84., -83., -82., -81., -80., -79., -78., -77., -76., -75., -74., -73., -72., -71., -70., -69., -68., -67., -66., -65., -64., -63., -62., -61., -60., -59., -58., -57., -56., -55., -54., -53., -52., -51., -50., -49., -48., -47., -46., -45., -44., -43., -42., -41., -40., -39., -38., -37., -36., -35., -34., -33., -32., -31., -30., -29., -28., -27., -26., -25., -24., -23., -22., -21., -20., -19., -18., -17., -16., -15., -14., -13., -12., -11., -10., -9., -8., -7., -6., -5., -4., -3., -2., -1., 0., 1., 2., 3., 4., 5., 6., 7., 8., 9., 10., 11., 12., 13., 14., 15., 16., 17., 18., 19., 20., 21., 22., 23., 24., 25., 26., 27., 28., 29., 30., 31., 32., 33., 34., 35., 36., 37., 38., 39., 40., 41., 42., 43., 44., 45., 46., 47., 48., 49., 50., 51., 52., 53., 54., 55., 56., 57., 58., 59., 60., 61., 62., 63., 64., 65., 66., 67., 68., 69., 70., 71., 72., 73., 74., 75., 76., 77., 78., 79., 80., 81., 82., 83., 84., 85., 86., 87., 88., 89., 90.]), metric='diag_ratio', recursive=True, on_dup='replace', strict=False, verbose=0)#
- pycsamt.emtools.rotate_to_strike(sites, *, method='consensus', band=None, angles=array([-90., -89., -88., -87., -86., -85., -84., -83., -82., -81., -80., -79., -78., -77., -76., -75., -74., -73., -72., -71., -70., -69., -68., -67., -66., -65., -64., -63., -62., -61., -60., -59., -58., -57., -56., -55., -54., -53., -52., -51., -50., -49., -48., -47., -46., -45., -44., -43., -42., -41., -40., -39., -38., -37., -36., -35., -34., -33., -32., -31., -30., -29., -28., -27., -26., -25., -24., -23., -22., -21., -20., -19., -18., -17., -16., -15., -14., -13., -12., -11., -10., -9., -8., -7., -6., -5., -4., -3., -2., -1., 0., 1., 2., 3., 4., 5., 6., 7., 8., 9., 10., 11., 12., 13., 14., 15., 16., 17., 18., 19., 20., 21., 22., 23., 24., 25., 26., 27., 28., 29., 30., 31., 32., 33., 34., 35., 36., 37., 38., 39., 40., 41., 42., 43., 44., 45., 46., 47., 48., 49., 50., 51., 52., 53., 54., 55., 56., 57., 58., 59., 60., 61., 62., 63., 64., 65., 66., 67., 68., 69., 70., 71., 72., 73., 74., 75., 76., 77., 78., 79., 80., 81., 82., 83., 84., 85., 86., 87., 88., 89., 90.]), metric='diag_ratio', inplace=False, recursive=True, on_dup='replace', strict=False, verbose=0)#
- pycsamt.emtools.strike_curve_sweep(sites, *, angles=array([-90., -89., -88., -87., -86., -85., -84., -83., -82., -81., -80., -79., -78., -77., -76., -75., -74., -73., -72., -71., -70., -69., -68., -67., -66., -65., -64., -63., -62., -61., -60., -59., -58., -57., -56., -55., -54., -53., -52., -51., -50., -49., -48., -47., -46., -45., -44., -43., -42., -41., -40., -39., -38., -37., -36., -35., -34., -33., -32., -31., -30., -29., -28., -27., -26., -25., -24., -23., -22., -21., -20., -19., -18., -17., -16., -15., -14., -13., -12., -11., -10., -9., -8., -7., -6., -5., -4., -3., -2., -1., 0., 1., 2., 3., 4., 5., 6., 7., 8., 9., 10., 11., 12., 13., 14., 15., 16., 17., 18., 19., 20., 21., 22., 23., 24., 25., 26., 27., 28., 29., 30., 31., 32., 33., 34., 35., 36., 37., 38., 39., 40., 41., 42., 43., 44., 45., 46., 47., 48., 49., 50., 51., 52., 53., 54., 55., 56., 57., 58., 59., 60., 61., 62., 63., 64., 65., 66., 67., 68., 69., 70., 71., 72., 73., 74., 75., 76., 77., 78., 79., 80., 81., 82., 83., 84., 85., 86., 87., 88., 89., 90.]), metric='diag_ratio', smooth=5, recursive=True, on_dup='replace', strict=False, verbose=0)#
- pycsamt.emtools.plot_strike_rose(sites, *, style='pycsamt', groups=None, group_key=None, band=None, freq_bands=None, band_labels=None, band_colors=None, method='consensus', bins=36, weight='inv_iqr', bar_style=<object object>, bar_color=<object object>, bar_alpha=<object object>, bar_edgecolor=<object object>, bar_edgelw=<object object>, cmap=<object object>, outer_ring_lw=<object object>, outer_ring_color=<object object>, n_rings=<object object>, ring_color=<object object>, ring_ls=<object object>, ring_lw=<object object>, ring_labels=<object object>, ring_label_angle=<object object>, ring_label_fontsize=<object object>, ring_label_color=<object object>, ring_label_fmt=<object object>, spoke_every=<object object>, spoke_color=<object object>, spoke_ls=<object object>, spoke_lw=<object object>, compass_labels=<object object>, compass_fontsize=<object object>, compass_color=<object object>, compass_fontweight=<object object>, show_mean=<object object>, mean_color=<object object>, mean_lw=<object object>, mean_ls=<object object>, show_secondary=<object object>, secondary_color=<object object>, secondary_ls=<object object>, secondary_lw=<object object>, show_annotation=<object object>, annotation_pos=<object object>, annotation_fontsize=<object object>, annotation_bg=<object object>, annotation_ec=<object object>, show_n_stations=<object object>, subplot_size=3.2, n_cols=None, axes=None, figsize=None, suptitle='', suptitle_fontsize=10.0, tight_layout=True, recursive=True, on_dup='replace', strict=False, verbose=0)#
Publication-quality rose diagram of geoelectric strike direction.
Each subplot shows the angular distribution of the estimated MT geoelectric strike for one station group (profile line). Bars are drawn with axial symmetry — 0–180° mirrored to 180–360° — to reflect the inherent 180° ambiguity of geoelectric strike.
- Parameters:
sites (any) – EDI paths, EDI objects, or SitesCollection accepted by
ensure_sites().groups (dict[str, list[str]], optional) – Explicit map
{group_label: [station_name, ...], ...}. If None, stations are auto-grouped by profile prefix (e.g."E1S01"→ group"E1").group_key (str, optional) – EDI attribute to read as group label when groups is None.
band ((float, float), optional) – Period band
(lo_s, hi_s)in seconds for strike estimation. None uses all available frequencies.freq_bands (list of (float, float), optional) – Period sub-bands used for
bar_style="bands". Each tuple is(lo_s, hi_s); one histogram per band is stacked.band_labels (list[str], optional) – Legend labels matching freq_bands (one per band).
band_colors (list, optional) – Bar colours for each freq_bands entry (any matplotlib colour spec). Defaults to
tab10palette.method ({"consensus", "sweep", "pt"}) – Strike estimation method — see
estimate_strike_consensus().bins (int) – Number of histogram bins over 0–180°, mirrored to 360°.
weight ({"inv_iqr", "uniform"}) – Weighting scheme.
"inv_iqr"down-weights unstable sites.bar_style ({"gradient", "bands", "solid"}) –
"gradient"— bars coloured by height via cmap (paper style);"bands"— stacked per-band bars with distinct colours;"solid"— uniform bar_color.bar_color (str) – Bar fill colour for
bar_style="solid".bar_edgecolor (str) – Bar edge colour (
"none"→ no edge).bar_edgelw (float) – Bar edge line-width.
cmap (str) – Colormap name for
bar_style="gradient".outer_ring_lw (float) – Line-width of the bold outer circle.
outer_ring_color (str) – Colour of the outer circle.
n_rings (int) – Number of concentric reference rings inside the plot.
ring_color (str) – Colour of grid rings and radial spokes.
ring_ls (str) – Line-style of grid rings and radial spokes.
spoke_every (float) – Angular spacing (degrees) of radial spokes / tick marks.
compass_labels ({"NESW", "degrees", "none"}) – Labels around the polar perimeter.
"NESW"shows cardinal directions;"degrees"shows degree values;"none"suppresses all labels.compass_fontsize (float) – Font size for compass / degree labels.
compass_color (str) – Colour of compass labels.
mean_color (str) – Colour of the mean-direction line.
mean_lw (float) – Line-width of the mean-direction line.
mean_ls (str) – Line-style of the mean-direction line.
show_secondary (bool) – Draw the 180°-conjugate mean line (axial symmetry axis).
secondary_color (str, optional) – Colour for the conjugate line; defaults to mean_color.
secondary_ls (str) – Line-style for the conjugate line.
secondary_lw (float, optional) – Line-width for the conjugate line; defaults to mean_lw.
annotation_pos ((float, float)) – Axes-fraction
(x, y)of the strike angle annotation box.annotation_fontsize (float) – Font size of the annotation text.
annotation_bg (str) – Background colour of the annotation box.
annotation_ec (str) – Edge colour of the annotation box.
show_n_stations (bool) – Append the station count
n = Nto the annotation text.subplot_size (float) – Side length (inches) of each polar subplot.
n_cols (int, optional) – Number of subplot columns. Defaults to
len(groups).figsize ((float, float), optional) – Override the auto-computed figure size.
suptitle (str) – Figure-level super-title.
suptitle_fontsize (float) – Font size of the super-title.
tight_layout (bool) – Call
fig.tight_layout()before returning.recursive (bool) – Passed to
ensure_sites().on_dup (str) – Duplicate-handling strategy for
ensure_sites().strict (bool) – Strict mode for
ensure_sites().verbose (int) – Verbosity level.
- Returns:
Figure with one polar axes per station group.
- Return type:
Examples
Basic usage — one rose per profile line, gradient style:
>>> from pycsamt.emtools import plot_strike_rose >>> fig = plot_strike_rose("path/to/edis/")
Frequency-band decomposition (short / long period stacked):
>>> fig = plot_strike_rose( ... sites, ... bar_style="bands", ... freq_bands=[(0.001, 0.1), (0.1, 100.0)], ... band_labels=["Short period", "Long period"], ... )
- pycsamt.emtools.plot_strike_rose_by_line(sites, *, groups=None, group_key=None, band=None, method='consensus', bins=36, weight='inv_iqr', axes=None, figsize=(8.6, 4.6), recursive=True, on_dup='replace', strict=False, verbose=0)#
- pycsamt.emtools.plot_strike_ribbon(sites, *, method='sweep', win=5, show_colorbar=True, cbar_ticks=None, figsize=(9.0, 4.2), recursive=True, on_dup='replace', strict=False, verbose=0, ax=None)#
- pycsamt.emtools.plot_strike_profile(sites, *, method='consensus', band=None, sort_by='auto', figsize=(8.6, 3.8), recursive=True, on_dup='replace', strict=False, verbose=0, ax=None)#
- pycsamt.emtools.plot_strike_mapsticks(sites, *, method='consensus', band=None, len_deg=0.02, alpha_scale=0.9, figsize=(7.8, 6.0), recursive=True, on_dup='replace', strict=False, verbose=0, ax=None)#
- pycsamt.emtools.plot_strike_analysis(sites, *, style='pycsamt', band=None, bins=36, method='sweep', cmap_z=None, cmap_pt=None, cmap_tipper=None, title_fc_z='#ffe0e0', title_fc_pt='#ffffd0', title_fc_tipper='#d5e8ff', title_ec='0.35', axes=None, figsize=None, subplot_size=3.8, suptitle='', tight_layout=True, recursive=True, on_dup='replace', strict=False, verbose=0)#
Three-panel rose diagram: Strike (Z), PT Azimuth, and Tipper Strike.
Produces a publication-quality figure with one polar rose per analysis type, analogous to the MTPy
StrikeAnalysisplot. All three panels share the sameRoseStyleso colours remain visually consistent. Each panel carries a coloured title box to distinguish the three quantities at a glance.- Parameters:
sites (any) – EDI paths, EDI objects, or
SitesCollectionaccepted byensure_sites().style (str, RoseStyle, or None) – Named style preset or
RoseStyleinstance. Default"pycsamt"uses the YlOrRd-gradient, crimson-mean-line paper style.band ((lo_s, hi_s) or None) – Period window in seconds applied to all three panels.
Noneuses all available periods / frequencies.bins (int) – Number of histogram bins over 0–180°, mirrored to 0–360°. Default 36 → 5° bins.
method ({"sweep", "pt", "consensus"}) –
Strike estimation algorithm for the Strike (Z) panel.
"sweep"— impedance-tensor rotation sweep (callsestimate_strike_sweep());"pt"— phase-tensor θ median per station (callsestimate_strike_phase_tensor());"consensus"— weighted blend of sweep and PT (callsestimate_strike_consensus()).cmap_z (str or None) – Colormap name for each panel when
bar_style="gradient".Nonefalls back to the colormap in style.cmap_pt (str or None) – Colormap name for each panel when
bar_style="gradient".Nonefalls back to the colormap in style.cmap_tipper (str or None) – Colormap name for each panel when
bar_style="gradient".Nonefalls back to the colormap in style.title_fc_z (str) – Facecolour of the title annotation box for each panel.
title_fc_pt (str) – Facecolour of the title annotation box for each panel.
title_fc_tipper (str) – Facecolour of the title annotation box for each panel.
title_ec (str) – Edge colour shared by all title boxes.
figsize ((float, float) or None) – Figure size. Auto-derived from subplot_size when
None.subplot_size (float) – Side length (inches) of each polar panel when figsize is auto.
suptitle (str) – Figure-level super-title.
tight_layout (bool) – Call
fig.tight_layout()before returning.recursive (bool) – Passed to
ensure_sites().on_dup (str) – Passed to
ensure_sites().strict (bool) – Passed to
ensure_sites().verbose (int) – Passed to
ensure_sites().
- Returns:
Figure with three polar axes: Strike (Z), PT Azimuth, Tipper Strike.
- Return type:
Examples
Default style, all periods:
>>> from pycsamt.emtools import plot_strike_analysis >>> fig = plot_strike_analysis("path/to/edis/") >>> fig.savefig("strike_analysis.png", dpi=150, bbox_inches="tight")
Short-period band, publication style:
>>> fig = plot_strike_analysis( ... sites, ... band=(0.01, 1.0), ... style="publication", ... suptitle="WILLY AMT — short-period band", ... )
- pycsamt.emtools.rotate_z_to_strike(sites, *, method='swift', inplace=False, recursive=True, on_dup='replace', strict=False, verbose=0)#
- pycsamt.emtools.rotate(sites, angle, *, inplace=False, recursive=True, on_dup='replace', strict=False, verbose=0)#
- pycsamt.emtools.rotate_by_map(sites, angle_by_station, *, inplace=False, recursive=True, on_dup='replace', strict=False, verbose=0)#
- pycsamt.emtools.antisymmetrize(sites, *, how='rms', inplace=False, recursive=True, on_dup='replace', strict=False, verbose=0)#
- pycsamt.emtools.invert(sites, *, inplace=False, recursive=True, on_dup='replace', strict=False, verbose=0)#
- pycsamt.emtools.orient_from_sensors(sites, ex, ey, bx, by, *, degrees=True, inplace=False, recursive=True, on_dup='replace', strict=False, verbose=0)#
- pycsamt.emtools.sigma_clip_z(sites, *, sigma=3.0, inplace=False, recursive=True, on_dup='replace', strict=False, verbose=0)#
- pycsamt.emtools.balance_offdiag(sites, *, mode='avgabs', inplace=False, recursive=True, on_dup='replace', strict=False, verbose=0)#
- pycsamt.emtools.build_phase_tensor_table(sites, *, recursive=True, on_dup='replace', strict=False, verbose=0)#
- pycsamt.emtools.plot_phase_tensor_psection(sites, *, stations=None, period_range=None, axis_y='logperiod', period_up=True, scale=<object object>, normalise_by=<object object>, s1_ref=None, min_aspect=<object object>, c_by=<object object>, cmap=<object object>, clim=None, clim_pct=<object object>, symmetric_clim=<object object>, edgecolor=<object object>, linewidth=<object object>, alpha=<object object>, skew_threshold=<object object>, mark_3d=<object object>, ref_ellipse=<object object>, legend_fontsize=8.0, title='', xlabel='', ylabel='', tick_label_rotation=45.0, figsize=(10.0, 5.5), recursive=True, on_dup='replace', strict=False, verbose=0, ax=None)#
Phase-tensor ellipse pseudo-section (Caldwell et al. 2004 style).
Each cell
(station × period)is drawn as an ellipse whose shape, orientation, and fill colour encode phase-tensor invariants:major axis ∝ φ_max (s1 eigenvalue — mean phase level)
minor axis ∝ φ_min (s2 eigenvalue — minimum phase)
aspect ratio = φ_min / φ_max (1 = isotropic / 1-D)
rotation = θ (geoelectric strike, CCW from E)
fill colour controlled by c_by (default: skew angle β)
- Parameters:
sites (any) – EDI paths, glob pattern,
Sites, or any input accepted byensure_sites().stations (list of str or None) – Restrict to a subset of station names.
period_range ((T_min, T_max) in seconds or None) – Restrict the period range plotted.
axis_y (
"logperiod"|"logfreq") – Y-axis quantity."logperiod"is the MT convention.period_up (bool, default
True) – WhenTrue(MT convention) long period (low frequency) is at the top of the figure;Falseflips the y-axis.scale (float, default
0.85) – Fraction of each cell occupied by the reference ellipse (the one with s1 = s1_ref). Values above 1 cause overlap.normalise_by (
"cell"|"unity"|"abs") –Sizing strategy:
"cell"Sizes are normalised so the 90th-percentile s1 fills scale of its cell. Preserves relative size information.
"unity"s1_ref = 1.0 (tan 45° = 1-D half-space reference). A 45° phase tensor is drawn as a circle filling scale of its cell.
"abs"Raw: width = scale × s1, height = scale × s2 in data units (legacy behaviour).
s1_ref (float or None) – Override the reference s1 for
"cell"and"unity"modes.min_aspect (float, default
0.18) – Minimum height/width ratio enforced on every ellipse so that near-degenerate (φ_min ≈ 0) cells stay visible as ovals instead of collapsing into an invisible hairline. Set to0to plot raw ellipticity.c_by (str, default
"skew") – Column name or derived quantity to map to fill colour. Supported values:"skew","beta","alpha","theta","ellipt","s1","s2","|skew|","|beta|","|theta|","phi_mean","phi_max","phi_min".cmap (str, default
"RdBu_r") – Matplotlib colormap name.clim ((vmin, vmax) or None) – Explicit colour limits. When
None, derived from clim_pct.clim_pct ((lo, hi), default
(5.0, 95.0)) – Percentile limits used when clim isNone.symmetric_clim (bool, default
True) – Enforce vmin = −vmax for skew-like quantities.edgecolor (str, default
"k") – Ellipse border colour. Set to"none"to suppress borders.linewidth (float, default
0.2) – Ellipse border width (pts). 3-D cells receive 3 × this width when mark_3d isTrue.alpha (float, default
0.92) – Ellipse fill opacity.skew_threshold (float or None, default
3.0) – |β| threshold (degrees) separating 1-D/2-D from 3-D structure. Used to cap clim symmetrically when clim isNone, and to highlight 3-D cells with a thicker border when mark_3d isTrue.mark_3d (bool, default
True) – Draw a thicker border on ellipses where |β| > skew_threshold.ref_ellipse (bool, default
True) – Draw a labelled reference circle (φ_max = φ_min = s1_ref, β = 0°) in a reserved legend strip above the data, as a scale indicator.legend_fontsize (float, default
8.0) – Font size for the reference-circle label and the 1-D/2-D vs 3-D annotation shown when ref_ellipse / skew_threshold are active.title (str) – Axes title and axis labels. Sensible defaults are used when empty.
xlabel (str) – Axes title and axis labels. Sensible defaults are used when empty.
ylabel (str) – Axes title and axis labels. Sensible defaults are used when empty.
tick_label_rotation (float, default
45.0) – Station-name tick rotation in degrees.figsize ((width, height), default
(10.0, 5.5)) – Figure size (ignored when ax is provided).recursive (see
ensure_sites().)on_dup (see
ensure_sites().)strict (see
ensure_sites().)verbose (see
ensure_sites().)ax (
Axesor None) – If provided, draw into this axes and return it; otherwise create a new figure.
- Returns:
ax
- Return type:
See also
build_phase_tensor_tableUnderlying data computation.
plot_phase_tensor_summary3-panel figure combining ellipses, dimensionality, and skew distribution.
plot_phase_tensor_mapGeographic map view.
- pycsamt.emtools.plot_phase_tensor_skewmap(sites, *, axis_y='logperiod', agg='median', figsize=(9.0, 4.0), recursive=True, on_dup='replace', strict=False, verbose=0, ax=None)#
- pycsamt.emtools.plot_theta_vs_period(sites, *, figsize=(8.0, 4.0), recursive=True, on_dup='replace', strict=False, verbose=0, ax=None)#
- pycsamt.emtools.plot_strike_director_field(sites, *, color_by='skew', length_by='ellipt', streamlines=True, skew_max=6.0, cmap=None, period_subsample=None, bar_scale=26.0, show_legend=True, title=None, figsize=(12.0, 5.2), recursive=True, on_dup='replace', strict=False, verbose=0, ax=None)#
Geoelectric-strike director field over station and period.
A supplement to
plot_theta_vs_period(). Because the phase-tensor strikethetais an axial angle (defined mod 180 deg), the correct glyph is not a point on a linear axis but a head-less bar pointing along the strike. This draws one director per(station, period)cell on a station x log-period grid, encoding three more channels at once:orientation – the strike
theta;length –
length_by(default ellipticity), the 2-D strength: near-1-D cells get a short bar because there strike is ill-defined;colour –
color_by(default|skew|), the departure from 2-D: green = low-skew / reliable, red = high-skew / 3-D or galvanic distortion where the strike should not be trusted.
An optional smoothed streamline overlay (
streamlines=True) integrates the director field into a strike “flow”, so lateral and vertical coherence read at a glance.Interpretation#
long, aligned, green bars flowing in a laminar bundle -> robust, depth-consistent 2-D strike; read the azimuth with confidence;
bars rotating smoothly with depth -> strike varies with depth (dipping structure or layered anisotropy);
short and/or red, swirling bars -> 1-D, 3-D, or noise: do not over-interpret the direction there.
- param sites:
Anything accepted by
build_phase_tensor_table().- type sites:
path, EDI object, APISurvey, Sites, or iterable of sites
- param color_by:
Table column mapped to bar colour (its absolute value is used).
- type color_by:
{‘skew’, ‘ellipt’, ‘s1’, ‘s2’, …}, default ‘skew’
- param length_by:
Table column mapped to bar length (absolute value, 95th-percentile normalised).
Nonedraws uniform-length bars.- type length_by:
str or None, default ‘ellipt’
- param streamlines:
Overlay smoothed strike streamlines (needs SciPy).
- type streamlines:
bool, default True
- param skew_max:
Upper clip of the
|skew|colour scale, in degrees (only used whencolor_by='skew'). Skew above a few degrees already flags 3-D behaviour.- type skew_max:
float, default 6.0
- param cmap:
Override the colour map (default
'RdYlGn_r'for skew, else'viridis').- type cmap:
str, optional
- param period_subsample:
Keep at most this many periods (evenly along the log axis) to thin a very dense grid.
- type period_subsample:
int, optional
- param bar_scale:
Matplotlib quiver
scale– larger makes shorter bars.- type bar_scale:
float, default 26.0
- param show_legend:
Draw the director / streamline legend.
- type show_legend:
bool, default True
- param title:
Axes title.
- type title:
str, optional
- param figsize:
Standard
emtoolsplotting arguments.- param recursive:
Standard
emtoolsplotting arguments.- param on_dup:
Standard
emtoolsplotting arguments.- param strict:
Standard
emtoolsplotting arguments.- param verbose:
Standard
emtoolsplotting arguments.- param ax:
Standard
emtoolsplotting arguments.- rtype:
matplotlib.axes.Axes
See also
plot_theta_vs_periodthe linear scatter this supplements.
plot_phase_tensor_psectionper-cell ellipses (magnitudes as well).
- Parameters:
- Return type:
- pycsamt.emtools.plot_ellipticity_psection(sites, *, figsize=(8.5, 4.0), agg='median', recursive=True, on_dup='replace', strict=False, verbose=0, ax=None)#
- pycsamt.emtools.plot_dimensionality_psection(sites, *, skew_th=3.0, ellipt_th=0.2, figsize=(8.5, 4.0), recursive=True, on_dup='replace', strict=False, verbose=0, ax=None)#
- pycsamt.emtools.plot_phase_tensor_rose(sites, *, style='pycsamt', band=None, freq_bands=None, band_labels=None, band_colors=None, bins=36, bar_style=<object object>, bar_color=<object object>, bar_alpha=<object object>, bar_edgecolor=<object object>, bar_edgelw=<object object>, cmap=<object object>, outer_ring_lw=<object object>, outer_ring_color=<object object>, n_rings=<object object>, ring_color=<object object>, ring_ls=<object object>, ring_lw=<object object>, ring_labels=<object object>, ring_label_angle=<object object>, ring_label_fontsize=<object object>, ring_label_color=<object object>, ring_label_fmt=<object object>, spoke_every=<object object>, spoke_color=<object object>, spoke_ls=<object object>, spoke_lw=<object object>, compass_labels=<object object>, compass_fontsize=<object object>, compass_color=<object object>, compass_fontweight=<object object>, show_mean=<object object>, mean_color=<object object>, mean_lw=<object object>, mean_ls=<object object>, show_secondary=<object object>, secondary_ls=<object object>, secondary_lw=<object object>, secondary_color=<object object>, show_annotation=<object object>, annotation_pos=<object object>, annotation_fontsize=<object object>, annotation_bg=<object object>, annotation_ec=<object object>, show_n=<object object>, figsize=(5.5, 5.5), title='', title_fontsize=10.0, recursive=True, on_dup='replace', strict=False, verbose=0, ax=None)#
Publication-quality phase-tensor θ rose diagram.
Bars are drawn with axial symmetry — 0–180° mirrored to 180–360° — reflecting the inherent 180° ambiguity of the phase-tensor principal axis direction.
- Parameters:
sites (any) – EDI paths, objects, or collection accepted by
ensure_sites().band ((lo_s, hi_s) or None) – Period window in seconds.
Noneuses all available periods.freq_bands (list of (lo_s, hi_s), optional) – Sub-bands for
bar_style="bands"— one stacked bar colour per band.band_labels (list[str], optional) – Legend labels for each entry in freq_bands.
band_colors (list, optional) – Colours for each freq_bands entry. Defaults to
tab10.bins (int) – Number of bins over 0–180°, mirrored to 360°. Default
36gives 5° bins.bar_style ({"gradient", "solid", "bands"}) –
"gradient"colours bars by height via cmap;"solid"uses bar_color uniformly;"bands"stacks one colour per period sub-band.bar_color (str) – Bar fill colour for
bar_style="solid".bar_edgecolor (str) – Bar edge colour (
"none"→ no edge).bar_edgelw (float) – Bar edge line-width.
bar_alpha (float) – Bar opacity (0–1).
cmap (str) – Colormap for
bar_style="gradient".outer_ring_lw (float) – Line-width of the bold outer bounding circle.
outer_ring_color (str) – Colour of the outer circle.
n_rings (int) – Number of concentric reference rings.
ring_color (str) – Colour of concentric rings and spokes.
ring_ls (str) – Line-style of concentric rings.
ring_lw (float) – Line-width of concentric rings.
ring_labels (list[float], optional) – Explicit count values to annotate on the rings (e.g.
[25, 50, 75, 100]).None→ evenly spaced fromrmax / n_ringstormax.ring_label_angle (float) – Clockwise angle from North (degrees) at which ring count labels are placed. Default
22.5.ring_label_fontsize (float) – Font size for ring count labels.
ring_label_color (str) – Colour for ring count labels.
ring_label_fmt (str) – Format string for ring labels, e.g.
"{:.0f}".spoke_every (float) – Angular spacing (degrees) between radial spokes.
spoke_color (str) – Colour of radial spokes.
spoke_ls (str) – Line-style of radial spokes.
spoke_lw (float) – Line-width of radial spokes.
compass_labels ({"NESW", "degrees", "none"}) – Perimeter labels.
"NESW"shows cardinal directions;"degrees"shows degree values;"none"hides all.compass_fontsize (float) – Font size for perimeter labels.
compass_color (str) – Colour for perimeter labels.
compass_fontweight (str) – Font weight for perimeter labels.
show_mean (bool) – Draw the axial mean direction as a line through the centre.
mean_color (str) – Colour of the mean-direction line.
mean_lw (float) – Line-width of the mean-direction line.
mean_ls (str) – Line-style of the mean-direction line.
show_secondary (bool) – Draw the 180°-conjugate mean line.
secondary_ls (str) – Line-style of the conjugate line.
secondary_lw (float, optional) – Line-width of the conjugate line; defaults to mean_lw.
secondary_color (str, optional) – Colour of the conjugate line; defaults to mean_color.
show_annotation (bool) – Show a text box with the mean θ and station/pair count.
annotation_pos ((float, float)) – Axes-fraction
(x, y)for the annotation box.annotation_fontsize (float) – Font size of the annotation text.
annotation_bg (str) – Background colour of the annotation box.
annotation_ec (str) – Edge colour of the annotation box.
show_n (bool) – Append
n = Nto the annotation text.title (str) – Axes title (set via
ax.set_title).title_fontsize (float) – Font size for title.
recursive (bool) – Passed to
ensure_sites().on_dup (str) – Passed to
ensure_sites().strict (bool) – Passed to
ensure_sites().verbose (int) – Passed to
ensure_sites().ax (matplotlib.axes.Axes, optional) – Pre-existing polar axes to draw into. Created when
None.
- Returns:
The polar axes containing the rose diagram.
- Return type:
Examples
Default gradient style, all periods:
>>> from pycsamt.emtools import plot_phase_tensor_rose >>> ax = plot_phase_tensor_rose("path/to/edis/", figsize=(6, 6))
Frequency-band decomposition (stacked):
>>> ax = plot_phase_tensor_rose( ... sites, ... bar_style="bands", ... freq_bands=[(1e-4, 1e-2), (1e-2, 1e0)], ... band_labels=["Short period", "Long period"], ... )
Custom ring count labels:
>>> ax = plot_phase_tensor_rose( ... sites, ... ring_labels=[25, 50, 75, 100], ... ring_label_angle=15.0, ... )
- pycsamt.emtools.plot_phase_tensor_map(sites, *, period=10.0, scale=<object object>, normalise_by=<object object>, s1_ref=<object object>, min_aspect=<object object>, c_by=<object object>, cmap=<object object>, clim=None, clim_pct=<object object>, symmetric_clim=<object object>, alpha=<object object>, edgecolor=<object object>, linewidth=<object object>, skew_threshold=<object object>, mark_3d=<object object>, lw_3d_factor=<object object>, ref_ellipse=<object object>, show_tipper=True, tipper_convention='parkinson', tipper_component='real', tipper_scale=None, tipper_color='k', tipper_lw=1.4, bg_grid=None, station_labels=True, station_marker='v', station_ms=5.0, station_color='k', label_fontsize=7.0, title='', colorbar_label=None, coords=None, figsize=(9.0, 7.0), recursive=True, on_dup='replace', strict=False, verbose=0, ax=None)#
Publication-quality phase-tensor map at a single period.
Renders each station as a phase-tensor ellipse positioned at its geographic coordinates. The ellipse shape encodes the phase-tensor principal values (φ_max, φ_min) and orientation (θ); fill colour encodes an additional scalar (default: skewness β). Induction arrows can be overlaid when tipper data are available, and an optional background field (gravity, resistivity, …) may be drawn behind the ellipses.
- Parameters:
sites (any) – EDI paths, objects, or collection accepted by
ensure_sites().period (float) – Target period (s). The nearest available period in the data is used for each station independently.
scale (float or _UNSET) – Maximum ellipse semi-axis in geographic units (degrees).
_UNSET→ auto-derived from the median inter-station spacing.normalise_by (
"cell"|"unity"|"abs") – Size normalisation strategy, seePhaseTensorEllipseStyle.s1_ref (float or None) – Manual reference s1 used with
normalise_by="cell"/"unity".min_aspect (float or _UNSET) – Minimum height/width ratio enforced on every ellipse; see
plot_phase_tensor_psection(). Default:PYCSAMT_STYLE.pt_ellipse.min_aspect.c_by (str) – Scalar quantity to map to fill colour. Recognised values:
"skew","beta","|skew|","theta","ellipt","phi_mean","phi_max","phi_min","s1","s2","alpha". Defaults toPYCSAMT_STYLE.pt_ellipse.c_by.cmap (str or None) – Matplotlib colormap. Auto-selected from c_by when
None(same logic asPhaseTensorEllipseStyle).clim ((vmin, vmax) or None) – Explicit colour limits.
None→ derived from clim_pct.clim_pct ((lo, hi)) – Percentile window for automatic colour limits.
symmetric_clim (bool) – Force vmin = −vmax (useful for diverging quantities).
alpha (float) – Ellipse fill opacity.
edgecolor (str) – Ellipse border colour.
linewidth (float) – Normal ellipse border width (pts).
skew_threshold (float or None) – |β| above which a cell is flagged as 3-D.
mark_3d (bool) – Draw thicker borders on 3-D flagged ellipses.
lw_3d_factor (float) – Line-width multiplier for 3-D flagged borders.
ref_ellipse (bool) – Draw a reference circle in the lower-left corner as a scale bar.
show_tipper (bool) – Overlay induction arrows when tipper data are found.
tipper_convention (
"parkinson"|"wiese") – Parkinson: arrow toward anomaly (negated real T); Wiese: arrow along real T.tipper_component (
"real"|"imag"|"both") – Which tipper component to draw.tipper_scale (float or None) – Arrow length = tipper_scale × scale. Auto-derived when
None.tipper_color (str) – Arrow colour (real component). Imaginary component uses
mcolors.to_rgba(tipper_color, 0.55).tipper_lw (float) – Arrow line-width.
bg_grid (dict or None) –
Optional background field drawn behind ellipses:
bg_grid = dict( lons = 1D or 2D longitude array, lats = 1D or 2D latitude array, values = 2D array (shape matches meshgrid of lons × lats), cmap = "RdYlGn", # colormap clim = (vmin, vmax), # or None → auto alpha = 0.55, # opacity label = "Gravity (gu)", )station_labels (bool) – Annotate each station position with its name.
station_marker (str) – Marker style for station positions (default
"v").station_ms (float) – Marker size.
station_color (str) – Marker and label colour.
label_fontsize (float) – Font size for station labels.
title (str) – Axes title.
colorbar_label (str or None) – Override the automatic colorbar label derived from c_by.
coords (dict[str, (lat, lon)] or None) – Explicit station coordinates. When
Nonethe function readsed.coords(a(lat, lon, elev)tuple) from each Site object.recursive (bool) – Passed to
ensure_sites().on_dup (str) – Passed to
ensure_sites().strict (bool) – Passed to
ensure_sites().verbose (int) – Passed to
ensure_sites().ax (matplotlib.axes.Axes or None) – Pre-existing axes to draw into.
- Return type:
Examples
Default skew-coloured map:
>>> from pycsamt.emtools import plot_phase_tensor_map >>> ax = plot_phase_tensor_map(sites, period=10.0)
Ellipticity coloured, with tipper arrows:
>>> ax = plot_phase_tensor_map( ... sites, period=10.0, ... c_by="ellipt", cmap="viridis", ... show_tipper=True, tipper_convention="parkinson", ... )
With a gravity background:
>>> ax = plot_phase_tensor_map( ... sites, period=10.0, ... bg_grid=dict(lons=g_lon, lats=g_lat, values=g_bouguer, ... cmap="RdYlGn", alpha=0.45, label="Gravity (gu)"), ... )
- pycsamt.emtools.phase_tensor_legend(*, size=1.0, figsize=(2.5, 2.5), ax=None)#
- pycsamt.emtools.plot_dimensionality_grid(sites, *, skew_th=3.0, ellipt_th=0.2, figsize=(8.5, 4.0), recursive=True, on_dup='replace', strict=False, verbose=0, ax=None)#
- pycsamt.emtools.plot_theta_stability_stripe(sites, *, win=5, figsize=(9.0, 4.0), recursive=True, on_dup='replace', strict=False, verbose=0, ax=None)#
- pycsamt.emtools.plot_skew_ellipt_density(sites, *, band=None, gridsize=40, figsize=(6.5, 5.5), recursive=True, on_dup='replace', strict=False, verbose=0, ax=None)#
- pycsamt.emtools.plot_theta_rose_grid(sites, *, n_bands=6, axes=None, figsize=(13.0, 3.8), bins=24, style='pycsamt', panel_title_fontsize=7.5, recursive=True, on_dup='replace', strict=False, verbose=0)#
Phase-tensor θ rose grid — one pycsamt-styled rose per frequency decade.
Draws n_bands polar rose diagrams side by side, each covering one equal-log-width period band. Each rose applies axial symmetry (0–180° mirrored to 180–360°) and is rendered using the active
RoseStyle.- Parameters:
sites (any) – Input accepted by
ensure_sites().n_bands (int, default
6) – Number of equal-log-width period bands.figsize ((width, height), default
(13.0, 3.8)) – Figure size in inches. Constrained layout is used internally so an externalfig.suptitlefits without producing blank space.bins (int, default
24) – Number of bins over 0–180° (mirrored to 360°).style (str,
RoseStyle, or None) – Rose visual style. Strings resolved viaresolve_rose_style().panel_title_fontsize (float, default
7.5) – Font size for the period-band label above each panel.recursive (bool) – Passed to
ensure_sites().on_dup (str) – Passed to
ensure_sites().strict (bool) – Passed to
ensure_sites().verbose (int) – Passed to
ensure_sites().
- Return type:
- pycsamt.emtools.plot_phase_tensor_strip(sites, *, station=None, period_range=None, scale=<object object>, normalise_by=<object object>, s1_ref=None, min_aspect=<object object>, cells_per_decade=7.0, c_by=<object object>, cmap=<object object>, clim=None, clim_pct=<object object>, symmetric_clim=<object object>, edgecolor=<object object>, linewidth=<object object>, alpha=<object object>, skew_threshold=<object object>, mark_3d=<object object>, phase_ticks=(0.0, 45.0, 90.0), ylabel='', station_label=True, station_label_fontsize=8.0, title='', xlabel='', figsize=(6.0, 1.4), show_colorbar=True, colorbar_label=None, recursive=True, on_dup='replace', strict=False, verbose=0, ax=None)#
Single-station phase-tensor ellipse strip vs period.
Draws one horizontal row of phase-tensor ellipses for a single station, one ellipse per period, in the classic “ellipse timeseries” style used e.g. by WinGLink / mtpy single-station PT plots. The y-axis carries no per-point data — it is a schematic 0°–90° phase scale (phase_ticks) used only to calibrate ellipse size by eye; the physical information (φ_max, φ_min, θ, fill colour) is identical to
plot_phase_tensor_psection().Combine several stations/profiles into the small-multiples layout (columns = profiles, rows = stations, one shared colorbar) with
plot_phase_tensor_strip_grid().- Parameters:
sites (any) – EDI paths, glob pattern,
Sites, any input accepted byensure_sites(), or a pre-builtpandas.DataFramefrombuild_phase_tensor_table()(already filtered to one station — in that case station may be leftNone).station (str or None) – Station to plot. Required when sites resolves to more than one station.
period_range ((T_min, T_max) in seconds or None) – Restrict the period range plotted.
scale (see) –
plot_phase_tensor_psection(). Sizing controls;normalise_by="unity"(φ=45° 1-D reference fills scale of the row) makes phase_ticks an exact scale. min_aspect floors the ellipse height so near-degenerate cells stay visible.normalise_by (see) –
plot_phase_tensor_psection(). Sizing controls;normalise_by="unity"(φ=45° 1-D reference fills scale of the row) makes phase_ticks an exact scale. min_aspect floors the ellipse height so near-degenerate cells stay visible.s1_ref (see) –
plot_phase_tensor_psection(). Sizing controls;normalise_by="unity"(φ=45° 1-D reference fills scale of the row) makes phase_ticks an exact scale. min_aspect floors the ellipse height so near-degenerate cells stay visible.min_aspect (see) –
plot_phase_tensor_psection(). Sizing controls;normalise_by="unity"(φ=45° 1-D reference fills scale of the row) makes phase_ticks an exact scale. min_aspect floors the ellipse height so near-degenerate cells stay visible.cells_per_decade (float, default
7.0) – Visual ellipse pitch along the period axis, expressed as the number of ellipse-widths per log10 decade. Unlikeplot_phase_tensor_psection()(one ellipse per station column), a period axis is typically sampled far more densely than is useful for ellipse width — sizing each ellipse to its local sample spacing would shrink it to an invisible sliver wherever sampling is dense. This value is independent of how many periods were actually measured, so ellipses overlap (by design — the classic single-station “ellipse timeseries” look) rather than shrinking as sampling gets denser.c_by – Fill-colour controls; see
plot_phase_tensor_psection().cmap – Fill-colour controls; see
plot_phase_tensor_psection().clim (tuple[float, float] | None) – Fill-colour controls; see
plot_phase_tensor_psection().clim_pct – Fill-colour controls; see
plot_phase_tensor_psection().symmetric_clim – Fill-colour controls; see
plot_phase_tensor_psection().edgecolor (ellipse border/opacity controls.)
linewidth (ellipse border/opacity controls.)
alpha (ellipse border/opacity controls.)
skew_threshold (3-D cell highlighting, see) –
plot_phase_tensor_psection().mark_3d (3-D cell highlighting, see) –
plot_phase_tensor_psection().phase_ticks ((lo, mid, hi) or None, default
(0.0, 45.0, 90.0)) – Tick values drawn on the schematic y-scale.Nonehides the y-axis entirely.ylabel (str) – Y-axis label; defaults to
"Phase (°)"when phase_ticks is notNone.station_label (bool, default
True) – Annotate the station name in the upper-left corner of the axes.station_label_fontsize (float, default
8.0)title (str) – Axes title / x-label. xlabel defaults to
"Period (s)".xlabel (str) – Axes title / x-label. xlabel defaults to
"Period (s)".figsize ((float, float), default
(6.0, 1.4)) – Figure size (ignored when ax is provided).show_colorbar (bool, default
True) – Attach a right-side colorbar. SetFalsewhen composing a grid with a single shared colorbar (seeplot_phase_tensor_strip_grid()).colorbar_label (str or None) – Override the automatic colorbar label derived from c_by.
recursive (see
ensure_sites().)on_dup (see
ensure_sites().)strict (see
ensure_sites().)verbose (see
ensure_sites().)ax (
Axesor None) – If provided, draw into this axes and return it; otherwise create a new figure.
- Returns:
ax
- Return type:
Examples
>>> from pycsamt.emtools import plot_phase_tensor_strip >>> ax = plot_phase_tensor_strip(sites, station="S1")
Fixed colour scale (for visual consistency across several calls):
>>> ax = plot_phase_tensor_strip( ... sites, station="S1", c_by="skew", clim=(-9.0, 9.0), ... )
See also
plot_phase_tensor_strip_gridMulti-station / multi-profile facet grid.
plot_phase_tensor_psectionStation × period pseudo-section.
- pycsamt.emtools.plot_phase_tensor_strip_grid(sites, profiles, *, period_range=None, scale=<object object>, normalise_by=<object object>, s1_ref=None, min_aspect=<object object>, cells_per_decade=7.0, c_by=<object object>, cmap=<object object>, clim=None, clim_pct=<object object>, symmetric_clim=<object object>, edgecolor=<object object>, linewidth=<object object>, alpha=<object object>, skew_threshold=<object object>, mark_3d=<object object>, phase_ticks=(0.0, 45.0, 90.0), col_titles=None, xlabel='Period (s)', suptitle='', colorbar_label=None, panel_size=(4.4, 1.05), recursive=True, on_dup='replace', strict=False, verbose=0, axes=None)#
Phase-tensor ellipse strips for several stations, grouped by profile.
Reproduces the classic multi-panel figure — one
plot_phase_tensor_strip()row per selected station, tiled in a grid where each column is a profile/line and each row is one of the stations picked for that profile — with a single shared colorbar for the whole figure (so fill colours are comparable across every panel).- Parameters:
sites (any) – Input accepted by
ensure_sites()covering every station referenced in profiles.profiles (dict[str, list[str]]) –
Mapping
{profile_label: [station, ...]}, e.g.:{"Profile L1": ["S1", "S15", "S30", "S45"], "Profile L3": ["S1", "S4", "S7", "S11"]}
Each list becomes one column; rows are aligned by position, not by station identity (profiles may list a different number of stations — empty cells are left blank).
period_range ((T_min, T_max) or None) – Restrict the period range plotted in every panel.
scale
normalise_by
s1_ref (float | None)
min_aspect
cells_per_decade (float)
c_by
cmap
xlabel (str)
suptitle (str)
colorbar_label (str | None)
recursive (bool)
on_dup (str)
strict (bool)
verbose (int)
axes (Any | None)
- Return type:
:param : :param clim: :param clim_pct: :param symmetric_clim: :param edgecolor: :param linewidth: :param alpha: :param : :param skew_threshold: Forwarded to every
plot_phase_tensor_strip()call; see itsdocstring (and
plot_phase_tensor_psection()) for details. When clim isNoneit is derived once from the pooled data of every listed station, so all panels share one colour scale.- Parameters:
mark_3d – Forwarded to every
plot_phase_tensor_strip()call; see its docstring (andplot_phase_tensor_psection()) for details. When clim isNoneit is derived once from the pooled data of every listed station, so all panels share one colour scale.phase_ticks ((lo, mid, hi) or None, default
(0.0, 45.0, 90.0)) – Schematic y-scale ticks drawn on every panel.col_titles (dict[str, str] or None) – Override the column title text; defaults to the profiles keys.
xlabel (str, default
"Period (s)") – Shown once under the bottom-most panel of each column.suptitle (str) – Figure-level title.
colorbar_label (str or None) – Override the automatic colorbar label derived from c_by.
panel_size ((width, height), default
(4.4, 1.05)) – Per-panel size in inches; the figure size is(n_cols * width, n_rows * height).recursive (see
ensure_sites().)on_dup (see
ensure_sites().)strict (see
ensure_sites().)verbose (see
ensure_sites().)axes (2-D array of
Axesor None) – Pre-existing(n_rows, n_cols)axes grid to draw into (n_rows = max(len(v) for v in profiles.values()),n_cols = len(profiles)). A new figure is created whenNone.sites (Any)
s1_ref (float | None)
cells_per_decade (float)
- Returns:
fig
- Return type:
Examples
>>> from pycsamt.emtools import plot_phase_tensor_strip_grid >>> fig = plot_phase_tensor_strip_grid( ... sites, ... profiles={ ... "Profile L1": ["S1", "S15", "S30", "S45"], ... "Profile L3": ["S1", "S4", "S7", "S11"], ... }, ... c_by="skew", cmap="RdBu_r", ... )
See also
plot_phase_tensor_stripSingle-station ellipse strip (one panel).
plot_phase_tensor_psectionStation × period pseudo-section.
- pycsamt.emtools.plot_phasor_wheel(sites, *, station=None, components=('xy', 'yx'), pband=None, radius='abs', cmap='viridis', colors=None, marker='o', ms=3.0, lw=1.0, connect=True, figsize=(4.8, 4.8), recursive=True, on_dup='replace', strict=False, verbose=0, ax=None)#
- pycsamt.emtools.plot_offdiag_antisym_residual(sites, *, vlim=None, cmap='magma', figsize=(9.0, 4.8), recursive=True, on_dup='replace', strict=False, verbose=0, ax=None)#
- pycsamt.emtools.plot_determinant_track(sites, *, station=None, pband=None, pcts=(10.0, 50.0, 90.0), n_draws=200, height_ratio=(2, 1), axes=None, figsize=(6.4, 3.8), color_mag='C0', color_phase='C3', fill_alpha=0.2, recursive=True, on_dup='replace', strict=False, verbose=0)#
- pycsamt.emtools.rho_spatial_gradient(sites, spacing_m=200.0, *, comp='det', recursive=True, on_dup='replace', strict=False, verbose=0)#
Transverse (along-line) apparent resistivity gradient.
Computes the first-order finite difference of \(\rho_a\) between adjacent stations at each frequency (eq. 11 of zhang2021):
\[\Delta\rho_a^x(j,\,f) \approx \rho_a(j,\,f) - \rho_a(j-1,\,f)\]where station indices are ordered by their position along the survey line. The result is assigned to the spatial midpoint between the two stations.
- Parameters:
sites (Sites | list) – EDI-like objects or a
Sitescontainer.spacing_m (float, default 200) – Fall-back inter-station spacing [m] used when no coordinate metadata is available.
comp ({"det", "xy", "yx"}, default "det") – Impedance component for \(\rho_a\).
"det"uses the geometric-mean determinant.recursive (bool) – Forwarded to
ensure_sites().on_dup (str) – Forwarded to
ensure_sites().strict (bool) – Forwarded to
ensure_sites().verbose (int) – Forwarded to
ensure_sites().
- Returns:
One row per (station-pair, frequency). Columns:
station_a,station_bNames of the left and right stations of each pair.
x_mMidpoint position along the survey line [m].
dx_mSpacing between the two stations [m].
freq_hz,period_sFrequency [Hz] and period [s].
depth_mSkin depth \(\delta = 503\,\sqrt{\rho_a/f}\) [m] at the midpoint \(\rho_a\) and the given frequency.
rho_a_ohmmMean \(\rho_a\) of the pair at that frequency [Ω·m].
delta_rho_x\(\Delta\rho_a^x\) [Ω·m].
- Return type:
References
Zhang et al. (2021), eq. (11).
- pycsamt.emtools.rho_frequency_gradient(sites, *, comp='det', spacing_m=200.0, recursive=True, on_dup='replace', strict=False, verbose=0)#
Vertical (log-frequency) apparent resistivity gradient.
Computes the first-order finite difference of \(\rho_a\) between adjacent frequencies at each station (eq. 12 of zhang2021):
\[\Delta\rho_a^z(j,\,f_k) \approx \rho_a(j,\,f_k) - \rho_a(j,\,f_{k-1})\]where \(f_k > f_{k-1}\) (ascending frequency, ascending skin-depth index). Because different frequencies probe different depths, \(\Delta\rho_a^z\) is associated with vertical changes in the subsurface.
- Parameters:
sites (Sites | list) – EDI-like objects or a
Sitescontainer.comp ({"det", "xy", "yx"}, default "det") – Impedance component for \(\rho_a\).
spacing_m (float, default 200) – Fall-back inter-station spacing [m].
recursive (bool) – Forwarded to
ensure_sites().on_dup (str) – Forwarded to
ensure_sites().strict (bool) – Forwarded to
ensure_sites().verbose (int) – Forwarded to
ensure_sites().
- Returns:
One row per (station, adjacent-frequency pair). Columns:
stationStation name.
x_mStation position along the survey line [m].
freq_hz,period_sHigher frequency \(f_k\) of the pair [Hz] and its corresponding period [s].
depth_mSkin depth at the mean \(\rho_a\) of the pair [m].
rho_a_ohmmMean \(\rho_a\) of the two adjacent frequencies [Ω·m].
delta_rho_z\(\Delta\rho_a^z\) [Ω·m].
- Return type:
References
Zhang et al. (2021), eq. (12).
- pycsamt.emtools.rho_joint_gradient(sites, spacing_m=200.0, *, comp='det', recursive=True, on_dup='replace', strict=False, verbose=0)#
Joint vertical-transverse apparent resistivity gradient.
Computes the frequency difference of the spatial gradient (eq. 13 of zhang2021), which simultaneously resolves lateral and vertical boundaries while suppressing the spurious background interference in the spatial gradient over homogeneous regions:
\[\Delta\rho_a^{zx}(j,\,f_k) = \Delta\rho_a^x(j,\,f_k) - \Delta\rho_a^x(j,\,f_{k-1})\]Expanding in terms of \(\rho_a\):
\[\Delta\rho_a^{zx}(j,\,f_k) = \bigl[\rho_a(j,\,f_k) - \rho_a(j-1,\,f_k)\bigr] - \bigl[\rho_a(j,\,f_{k-1}) - \rho_a(j-1,\,f_{k-1})\bigr]\]where station j is to the right of station j-1 along the survey line and \(f_k > f_{k-1}\).
The result is non-zero only where \(\rho_a\) changes in both the lateral and vertical directions simultaneously, making it a sensitive indicator of target boundaries.
- Parameters:
sites (Sites | list) – EDI-like objects or a
Sitescontainer.spacing_m (float, default 200) – Fall-back inter-station spacing [m].
comp ({"det", "xy", "yx"}, default "det") – Impedance component for \(\rho_a\).
recursive (bool) – Forwarded to
ensure_sites().on_dup (str) – Forwarded to
ensure_sites().strict (bool) – Forwarded to
ensure_sites().verbose (int) – Forwarded to
ensure_sites().
- Returns:
One row per (station-pair, adjacent-frequency pair). Columns:
station_a,station_bNames of the left and right stations of each pair.
x_mMidpoint position along the survey line [m].
dx_mSpacing between the two stations [m].
freq_hz,period_sUpper frequency \(f_k\) of the pair [Hz] and period [s].
depth_mSkin depth [m] estimated from the median \(\rho_a\) of the four surrounding cells and the frequency \(f_k\).
delta_rho_zx\(\Delta\rho_a^{zx}\) [Ω·m].
- Return type:
References
Zhang et al. (2021), eqs. (1), (11), (13).
- pycsamt.emtools.plot_gradient_section(sites, quantity='joint', spacing_m=200.0, *, comp='det', period_axis=True, log_y=True, figsize=(10.0, 5.0), cmap='RdBu_r', vlim=None, recursive=True, on_dup='replace', strict=False, verbose=0, ax=None)#
Pseudo-section of a gradient apparent resistivity quantity.
Produces a colour-coded pseudo-section (position × period or frequency) for one of three gradient quantities from zhang2021:
"spatial"(\(\Delta\rho_a^x\)) — lateral boundaries."frequency"(\(\Delta\rho_a^z\)) — vertical boundaries."joint"(\(\Delta\rho_a^{zx}\)) — combined; best boundary delineation and suppressed background interference (default).
A diverging colour-map centred at zero is used so that positive and negative gradient values (resistivity increase vs. decrease) can be distinguished at a glance.
- Parameters:
sites (Sites | list) – EDI-like objects or a
Sitescontainer.quantity ({"joint", "spatial", "frequency"}, default "joint") – Which gradient pseudo-section to plot.
spacing_m (float, default 200) – Fall-back inter-station spacing [m].
comp ({"det", "xy", "yx"}, default "det") – Impedance component for \(\rho_a\).
period_axis (bool, default True) – Show period [s] on the y-axis when True; frequency [Hz] when False.
log_y (bool, default True) – Use a logarithmic y-axis.
cmap (str, default
"RdBu_r") – Diverging Matplotlib colour-map.vlim ((vmin, vmax) or None) – Colour-scale limits [Ω·m]. If None, a symmetric range centred at zero is derived from the data.
ax (matplotlib.axes.Axes or None) – Draw on existing axes; a new figure is created if None.
recursive (bool) – Forwarded to
ensure_sites().on_dup (str) – Forwarded to
ensure_sites().strict (bool) – Forwarded to
ensure_sites().verbose (int) – Forwarded to
ensure_sites().
- Return type:
References
Zhang et al. (2021), Geophysical Prospecting, doi:10.1111/1365-2478.13059.
- pycsamt.emtools.sites_summary(sites, *, fields=('station', 'n_freq', 'has_tipper', 'period_min', 'period_max', 'lat', 'lon'), recursive=True, on_dup='replace', strict=False, verbose=0, api=None)#
- pycsamt.emtools.list_missing_sections(sites, *, require=('mt', 'tipper'), recursive=True, on_dup='replace', strict=False, verbose=0)#
- pycsamt.emtools.frequency_coverage(sites, *, mode='per-site', recursive=True, on_dup='replace', strict=False, verbose=0)#
- pycsamt.emtools.plot_coverage(sites, *, axis='period', show_mask=True, figsize=(7.0, 4.0), recursive=True, on_dup='replace', strict=False, verbose=0, ax=None)#
- pycsamt.emtools.plot_rhoa_phi(sites, *, components=('xy', 'yx'), axis='period', errorbar=True, figsize=(7.5, 6.0), recursive=True, on_dup='replace', strict=False, verbose=0, ax_r=None, ax_p=None)#
- pycsamt.emtools.plot_tipper_components(sites, *, kind=('real', 'imag'), axis='period', figsize=(7.5, 4.5), recursive=True, on_dup='replace', strict=False, verbose=0, ax=None)#
- pycsamt.emtools.pseudosection(sites, *, quantity='rho_xy', axis_x='station', axis_y='period', period_range=None, vmin=None, vmax=None, figsize=(7.5, 4.5), recursive=True, on_dup='replace', strict=False, verbose=0, ax=None, topo=None, dark=True)#
Draw a period-vs-station pseudosection.
- Parameters:
sites (Sites or compatible) – Station data.
quantity (str) – Column name to plot (e.g.
"rho_xy","phi_xy").topo (bool or None) – Override the global
PYCSAMT_TOPOsetting.None(default) reads the global singleton.dark (bool) – Use dark-palette styling for the topo strip.
axis_x (str)
axis_y (str)
vmin (float | None)
vmax (float | None)
recursive (bool)
on_dup (str)
strict (bool)
verbose (int)
ax (Axes | None)
- Return type:
- pycsamt.emtools.plot_station_response(sites, *, station=None, sites_model=None, components=('xx', 'xy', 'yx', 'yy'), period_range=None, rho_lim=None, phase_lim=None, tipper_lim=(-0.5, 0.5), show_tipper=True, show_error_bars=True, show_rms=True, title='', axes=None, figsize=None, recursive=True, on_dup='replace', strict=False, verbose=0)#
Full four-component impedance tensor + tipper response for one station.
Renders a 3-row × N-column figure (N = len(components)) following the
MTComponentStylecolour scheme:Row 0 — apparent resistivity ρa (Ω·m) vs period, log–log.
Row 1 — phase φ (°) vs period, semilog-x.
Row 2 — tipper magnitude: Re(Tx), Im(Tx), Re(Ty), Im(Ty) vs period, semilog-x. Hidden when show_tipper is
Falseor when no tipper data are found.
Each column corresponds to one impedance tensor component (Zxx, Zxy, Zyx, Zyy). The tipper row always uses four fixed sub-panels regardless of which Z components are selected.
An optional sites_model argument overlays a second (model/forward) dataset on the same axes as dotted lines. When both observed and model data are present, a per-component RMS is computed in log10(ρa) space and appended to each column header.
- Parameters:
sites (any) – Observed EDI data — path,
SitesCollection, or anything accepted byensure_sites().station (str or None) – Name of the station to plot.
Nonepicks the first available.sites_model (any or None) – Optional forward-model or inversion-response EDI data (same API as sites). When provided, overlay dashed lines and show RMS.
components (tuple of {"xx","xy","yx","yy"}, default all four) – Z-tensor components to display. Order determines column order.
period_range ((T_min, T_max) or None) – Clip period axis to this window (seconds).
rho_lim ((vmin, vmax) or None) – ρa y-axis limits.
None→ matplotlib auto.phase_lim ((lo, hi) or None) – Phase y-axis limits in degrees.
None→ auto.tipper_lim ((lo, hi), default (-0.5, 0.5)) – Tipper y-axis limits.
show_tipper (bool, default True) – Whether to add the tipper row.
show_error_bars (bool, default True) – Draw error bars on observed data.
show_rms (bool, default True) – Append per-component RMS to column titles when sites_model is set.
title (str) – Override the auto-derived figure title.
figsize ((float, float) or None) – Figure size. Auto-computed when
None.recursive (bool) – Passed to
ensure_sites().on_dup (str) – Passed to
ensure_sites().strict (bool) – Passed to
ensure_sites().verbose (int) – Passed to
ensure_sites().
- Return type:
Examples
Observed data only:
>>> from pycsamt.emtools import plot_station_response >>> fig = plot_station_response("path/to/edis/", station="S07")
With model overlay:
>>> fig = plot_station_response( ... obs_edis, station="HBH03_IMP", ... sites_model=model_edis, ... period_range=(1e-4, 1.0), ... )
- pycsamt.emtools.plot_tipper_hodograms(sites, *, station=None, bands=None, n_bands=3, normalize=False, colors=None, marker='o', ms=3.0, lw=1.0, ls='-', unit_circle=True, axes=None, figsize=(6.4, 3.2), recursive=True, on_dup='replace', strict=False, verbose=0)#
- Parameters:
- pycsamt.emtools.plot_induction_arrows(sites, *, periods=(1.0,), convention='park', scale=1.0, normalize=True, strike_ticks=True, tick_len=0.25, figsize=(7.2, 3.4), recursive=True, on_dup='replace', strict=False, verbose=0, ax=None)#
- pycsamt.emtools.plot_induction_map(sites, *, period=1.0, convention='park', show_real=True, show_imag=True, scale=<object object>, cmap='plasma', clim=None, show_colorbar=True, reference_arrow=0.1, station_labels=True, title='', figsize=(8, 7), recursive=True, on_dup='replace', strict=False, verbose=0, ax=None)#
Map-view induction arrows at one period.
Real (solid) and imaginary (dashed) Parkinson arrows at every station, coloured by |T| magnitude.
- Parameters:
sites (Sites-like)
period (float) – Target period in seconds.
convention ({'park', 'wiese', 'real', 'imag'})
show_real (bool)
show_imag (bool)
scale (float or _UNSET) – Arrow scale factor (auto from station spacing).
cmap (str)
clim ((vmin, vmax) or None)
show_colorbar (bool)
reference_arrow (float) – Length of the scale-bar reference arrow.
station_labels (bool)
title (str)
figsize (standard)
ax (standard)
recursive (bool)
on_dup (str)
strict (bool)
verbose (int)
- Returns:
ax
- Return type:
Axes
- pycsamt.emtools.plot_induction_section(sites, *, component='abs', n_periods=20, cmap='RdBu_r', clim=None, section='pseudosection', title='', figsize=None, recursive=True, on_dup='replace', strict=False, verbose=0, ax=None)#
Period × station pseudo-section coloured by |T| magnitude.
- pycsamt.emtools.plot_induction_convention(sites, *, period=1.0, scale=<object object>, station_labels=True, title='', axes=None, figsize=(11, 10), recursive=True, on_dup='replace', strict=False, verbose=0)#
2×2 panel: Parkinson/Wiese × Real/Imaginary conventions.
Parkinson — Real
Parkinson — Imaginary
Wiese — Real
Wiese — Imaginary
- pycsamt.emtools.plot_tipper_polar(sites, *, station=None, component='real', cmap=<object object>, lw=<object object>, alpha=<object object>, title='', figsize=(5.5, 5.5), recursive=True, on_dup='replace', strict=False, verbose=0, ax=None)#
Polar view: tipper azimuth (angle) and magnitude (radius) vs period.
Each frequency is one scatter point; colour encodes log₁₀(period). North (0°) = up, clockwise positive, following geomagnetic convention.
- pycsamt.emtools.plot_induction_rose(sites, *, component='real', pband=None, nbins=36, style=<object object>, title='', figsize=(5, 5), recursive=True, on_dup='replace', strict=False, verbose=0, ax=None)#
Rose diagram of induction arrow azimuths (all stations & periods).
- Parameters:
- Returns:
ax
- Return type:
polar Axes
- pycsamt.emtools.plot_induction_map_from_spectra(sp_input, *, period=1.0, coords=None, show_real=True, show_imag=True, scale=<object object>, cmap='plasma', station_labels=True, title='', figsize=(8, 5), ax=None)#
Map-view induction arrows from
Spectra.- Parameters:
- Returns:
ax
- Return type:
Axes
- pycsamt.emtools.plot_tipper_polar_from_spectra(sp, *, component='real', cmap='viridis', title='', ax=None, figsize=(5.5, 5.5))#
Polar tipper from a
Spectraobject.- Parameters:
sp (Spectra)
component ({'real', 'imag', 'abs'})
cmap (standard)
title (standard)
figsize (standard)
- Returns:
ax
- Return type:
polar Axes
- pycsamt.emtools.plot_induction_rose_from_spectra(sp_input, *, component='real', pband=None, nbins=36, style=<object object>, title='', figsize=(5, 5), ax=None)#
Rose diagram of induction arrow directions from Spectra objects.
- pycsamt.emtools.plot_induction_multiperiod_map(sites, *, periods=(1.0, 10.0, 100.0, 1000.0), tipper_data=None, convention='park', panel_labels=None, background=None, background_extent=None, background_cmap='terrain', background_alpha=0.75, background_clim=None, bg_colorbar_label='Elevation (m)', bg_colorbar_side='right', show_background_cbar=True, arrow_color='black', arrow_scale=<object object>, arrow_lw=1.6, reference_arrow=0.1, reference_panel=0, reference_label=<object object>, show_stations=True, station_labels=False, annotations=None, annotation_color='navy', annotation_fontsize=8.0, title='', axes=None, figsize=<object object>, panel_height=3.0, panel_width=8.5, recursive=True, on_dup='replace', strict=False, verbose=0)#
Stacked multi-period induction vector map.
Produces one panel per period (stacked vertically), each showing the real Parkinson induction vectors on a background colour map (elevation, resistivity, or any 2-D raster). The layout mimics the style of Boukhalfa et al. (2020, GJI) Fig. 7 and the paper figure described in the session above.
Visual conventions#
Arrows: black solid vectors using the Parkinson (1962) convention (real component pointing toward the conductor).
Background: smooth terrain coloured with background_cmap; a synthetic gradient is auto-generated when background is
None.Reference vector: drawn in the first (or reference_panel) sub-plot; labelled with its normalised length.
Shared colorbar: placed on the right edge of the figure, spanning all panels.
Panel labels:
"(A) 1 s","(B) 10 s", … placed in the lower-left corner of each panel.Geological / site annotations: optional blue text labels supplied via annotations.
- param sites:
EDI collection accepted by
ensure_sites(). When the EDIs carry no tipper (Tipper.tipperall-zero), pass synthetic tipper via tipper_data.- type sites:
Sites-like
- param periods:
Target periods in seconds, one panel each.
- type periods:
sequence of float
- param tipper_data:
Explicit tipper override. Keys must match periods (nearest match is used). Column 0 = T_x, column 1 = T_y. When absent, the tipper is read from the EDIs.
- type tipper_data:
dict {period → (n_sites, 2) complex array}, optional
- param convention:
Arrow convention.
"park"(Parkinson real) is the only one used in published induction-vector maps;"wiese"is supported.- type convention:
str
- param panel_labels:
Panel corner labels (e.g.
["(A) 1 s", "(B) 10 s", ...]). Auto-generated whenNone.- type panel_labels:
sequence of str, optional
- param background:
Pre-computed background raster. A smooth synthetic terrain is generated when
None.- type background:
ndarray (ny, nx), optional
- param background_extent:
Geographic extent for the background raster. Auto-inferred from station positions when
None.- type background_extent:
(x_left, x_right, y_bottom, y_top), optional
- param background_cmap:
Colormap for the background. Default
"terrain"(green→brown elevation appearance).- type background_cmap:
str
- param background_alpha:
Background opacity (0–1).
- type background_alpha:
float
- param background_clim:
- type background_clim:
(vmin, vmax) or None
- param bg_colorbar_label:
Label for the shared colorbar.
- type bg_colorbar_label:
str
- param bg_colorbar_side:
Side of the figure where the shared background colorbar is placed. Right-side placement is the package default because it keeps section axes and station labels visually grouped.
- type bg_colorbar_side:
{‘right’, ‘left’, ‘top’, ‘bottom’}, default ‘right’
- param show_background_cbar:
- type show_background_cbar:
bool
- param arrow_color:
Single colour for all induction vectors. Default
"black".- type arrow_color:
str
- param arrow_scale:
Multiplier for arrow length in data units. Auto-computed from the typical station spacing when
_UNSET.- type arrow_scale:
float or _UNSET
- param arrow_lw:
Arrow shaft line width in points.
- type arrow_lw:
float
- param reference_arrow:
Normalised length of the scale-reference arrow drawn in the reference_panel sub-plot. Default 0.1.
- type reference_arrow:
float
- param reference_panel:
Sub-plot index (0-based) where the reference arrow appears.
- type reference_panel:
int
- param reference_label:
Text for the reference arrow. Defaults to
"Vector length {reference_arrow}".- type reference_label:
str or _UNSET
- param show_stations:
Draw a small marker (
▼) at each station position.- type show_stations:
bool
- param station_labels:
Annotate station names next to markers.
- type station_labels:
bool
- param annotations:
Geological or site annotations drawn in annotation_color on every panel.
- type annotations:
dict {label: (x, y)} or {label: (x, y, {fontsize, color, …})}, optional
- param annotation_color:
Default colour for annotations.
- type annotation_color:
str
- param annotation_fontsize:
- type annotation_fontsize:
float
- param title:
Figure suptitle.
- type title:
str
- param figsize:
Auto-computed from panel_height, panel_width, and number of panels when omitted.
- type figsize:
(float, float) or _UNSET
- param panel_height:
Per-panel size in inches used for auto figsize.
- type panel_height:
float
- param panel_width:
Per-panel size in inches used for auto figsize.
- type panel_width:
float
- param recursive:
- type recursive:
standard ensure_sites kwargs.
- param on_dup:
- type on_dup:
standard ensure_sites kwargs.
- param strict:
- type strict:
standard ensure_sites kwargs.
- param verbose:
- type verbose:
standard ensure_sites kwargs.
- returns:
fig (Figure)
axes (ndarray of Axes, shape (n_periods,))
Examples
Real data with tipper:
fig, axs = plot_induction_multiperiod_map( "site.edi", periods=[1, 10, 100, 1000], )
Synthetic tipper supplied explicitly:
tipper = {1.0: st_tips_1s, 10.0: st_tips_10s, 100.0: st_tips_100s} fig, axs = plot_induction_multiperiod_map( "profile/*.edi", periods=[1, 10, 100], tipper_data=tipper, )
- Parameters:
sites (Any)
convention (str)
background (ndarray | None)
background_extent (tuple[float, float, float, float] | None)
background_cmap (str)
background_alpha (float)
bg_colorbar_label (str)
bg_colorbar_side (str)
show_background_cbar (bool)
arrow_color (str)
arrow_scale (float)
arrow_lw (float)
reference_arrow (float)
reference_panel (int)
reference_label (str)
show_stations (bool)
station_labels (bool)
annotation_color (str)
annotation_fontsize (float)
title (str)
figsize (Any)
panel_height (float)
panel_width (float)
recursive (bool)
on_dup (str)
strict (bool)
verbose (int)
- Return type:
- pycsamt.emtools.plot_response_tipper(sites, *, stations=None, components=('xy', 'yx'), tipper_components=('tx', 'ty'), raw=False, force_style=False, control=None, phase_range=None, ncols_groups=3, comp_wspace=0.12, group_hspace=0.32, height_ratios=(2.2, 1.1, 0.75, 0.75), axes=None, figsize_scale=(4.8, 4.6), colors=None, tipper_span_group=False, line_style=None, tipper_line_style=':', label_component_x=True, label_tipper_x=True, title_group_fmt='{station}', title_comp_fmt='Z{component}', shared_group_labels=True, shared_x_label_pad=0.074, x_tick_rotation=0.0, tick_fontsize=7, show_error_bars=True, show_tipper_error_bars=False, show_component_legend=True, show_tipper_legend=True, ylim_rhoa=None, ylim_phase=None, ylim_tipper=(-0.6, 0.6), grid=True, preserve_duplicates=False, recursive=True, on_dup='replace', strict=False, verbose=0)#
Plot impedance response panels with station-level tipper rows.
The figure is designed for MT/AMT quality control where impedance and tipper behaviour must be inspected together. Each station is a group: apparent resistivity and phase are shown per impedance component, while compact \(T_x\) and \(T_y\) panels span the full group width.
- Parameters:
sites (Sites-like) – EDI path, collection of EDI files,
Sitesobject, or iterable accepted bypycsamt.emtools._core.ensure_sites().stations (list of str, optional) – Station names to display. When omitted, all stations are used.
components (tuple of str, default ("xy", "yx")) – Impedance tensor components plotted in the resistivity and phase rows. Values are component keys such as
"xy","yx","xx", or"yy".tipper_components (tuple of str, default ("tx", "ty")) – Tipper components to draw. The usual complete diagnostic uses both
"tx"and"ty".raw (bool, default False) – If
True, impedance response curves use the package raw-data style unlessforce_styleis also true. Tipper real/imaginary curves keep distinct package component colours for readability.force_style (bool, default False) – Use component colours even when
raw=True.control (object, optional) – Plot view control. Defaults to
pycsamt.api.control.PYCSAMT_CONTROL.phase_range (tuple of float or None, optional) – Explicit phase display range. If omitted, the active
control.phasepolicy is used.ncols_groups (int, default 3) – Number of station groups per figure row.
comp_wspace (float) – Spacing inside and between station groups.
group_hspace (float) – Spacing inside and between station groups.
height_ratios (tuple of float, default (2.2, 1.1, 0.75, 0.75)) – Relative heights for rho, phase, Tx, and Ty rows. If only one tipper component is requested, the unused extra ratio is ignored.
figsize_scale (tuple of float, default (4.8, 4.6)) – Width and height multiplier for each station group row/column.
colors (dict, optional) – Optional impedance component colour overrides.
tipper_span_group (bool, default False) – If
True, each tipper row spans all impedance component columns in a station group. IfFalse, Tx and Ty are repeated under each component column, giving a compact grid such asrho/phase/Tx/Tyfor every component.line_style (str or None, optional) – Optional line style override for impedance curves.
tipper_line_style (str or None, default ":") – Optional line style override for real and imaginary tipper curves.
label_component_x (bool, default True) – Put the active x-axis label under each impedance-component stack and under the bottom tipper row.
label_tipper_x (bool, default True) – Put the active x-axis label under each impedance-component stack and under the bottom tipper row.
title_group_fmt (str) – Format strings for station and component labels.
title_comp_fmt (str) – Format strings for station and component labels.
shared_group_labels (bool, default True) – Use group-level rho/phase/tipper labels instead of repeating labels on every small axis.
shared_x_label_pad (float, default 0.074) – Figure-coordinate padding used for shared x labels.
x_tick_rotation (float, default 0) – Rotation for bottom x tick labels.
tick_fontsize (int, default 7) – Tick-label size for compact panels.
show_error_bars (bool) – Toggle impedance and tipper error bars independently.
show_tipper_error_bars (bool) – Toggle impedance and tipper error bars independently.
show_component_legend (bool) – Toggle global legends.
show_tipper_legend (bool) – Toggle global legends.
ylim_rhoa (tuple or None) – Optional y-axis limits.
ylim_phase (tuple or None) – Optional y-axis limits.
ylim_tipper (tuple or None) – Optional y-axis limits.
grid (bool, default True) – Draw light panel grids.
preserve_duplicates (bool, default False) – Preserve repeated in-memory EDI objects instead of normalizing them through
ensure_sites(). This is useful for diagnostic demos or before/after comparisons where two display stations intentionally share the same source station name.recursive (bool) – Forwarded to
pycsamt.emtools._core.ensure_sites().on_dup (str) – Forwarded to
pycsamt.emtools._core.ensure_sites().strict (bool) – Forwarded to
pycsamt.emtools._core.ensure_sites().verbose (int) – Forwarded to
pycsamt.emtools._core.ensure_sites().
- Returns:
The assembled response/tipper figure.
- Return type:
Examples
>>> from pycsamt.emtools.plot import plot_response_tipper >>> fig = plot_response_tipper( ... "data/AMT/TIPPER/HBH03_IMP.edi", ... components=("xy", "yx"), ... )
- pycsamt.emtools.plot_raw_sites_1d(sites, *, stations=None, components=('xx', 'xy', 'yx', 'yy'), raw=True, force_style=False, control=None, phase_range=None, ncols_groups=3, comp_wspace=0.12, group_hspace=0.25, height_ratio=(2, 1), axes=None, figsize_scale=(4.2, 3.1), colors=None, title_group_fmt='{station}', title_comp_fmt='Z{component}', shared_group_labels=True, label_mode=None, shared_x_label_pad=0.078, x_tick_rotation=None, tick_fontsize=7, show_error_bars=True, show_component_legend=True, ylim_rhoa=None, ylim_phase=None, grid=True, recursive=True, on_dup='replace', strict=False, verbose=0)#
Plot raw or processed 1-D rho/phase panels by station.
The layout mirrors diagnostic raw-data figures used in AMT/MT workflows: every selected station is a group, every component is a column, and each component column contains apparent resistivity above phase. When
raw=Truethe raw-data style frompycsamt.api.style.PYCSAMT_STYLE.rawis used automatically, producing black diagnostic traces unlessforce_style=Trueor explicitcolorsare provided.- Parameters:
sites (Any)
raw (bool)
force_style (bool)
control (Any | None)
ncols_groups (int)
comp_wspace (float)
group_hspace (float)
title_group_fmt (str)
title_comp_fmt (str)
shared_group_labels (bool)
label_mode (str | None)
shared_x_label_pad (float)
x_tick_rotation (float | None)
tick_fontsize (int)
show_error_bars (bool)
show_component_legend (bool)
grid (bool)
recursive (bool)
on_dup (str)
strict (bool)
verbose (int)
- pycsamt.emtools.plot_sites_panels(sites, *, components=('xy', 'yx'), quantity='rhoa', x_axis='period', phase_range=(-90.0, 90.0), stations=None, ncols=6, wspace=0.2, hspace=0.08, height_ratio=(2, 1), axes=None, figsize_scale=(2.6, 2.6), colors=None, marker=<object object>, ms=<object object>, lw=<object object>, ls=<object object>, show_error_bars=True, show_legend=False, title_fmt='{station}', ylim_rhoa=None, ylim_phase=None, grid=True, preserve_duplicates=False, recursive=True, on_dup='replace', strict=False, verbose=0)#
- pycsamt.emtools.plot_sites_compare(sites, new_sites=None, *, components=('xy', 'yx'), quantity='rhoa', x_axis='period', phase_range=(-90.0, 90.0), stations=None, ncols_groups=3, group_gap=0.35, pair_wspace=0.06, hspace=0.06, height_ratio=(2, 1), axes=None, figsize_scale=(3.0, 3.0), colors=None, marker=<object object>, ms=<object object>, lw=<object object>, ls=<object object>, show_error_bars=True, labels=('raw', 'after'), title_group_fmt='{station}', title_col_fmt='{tag}', show_legend=False, ylim_rhoa=None, ylim_phase=None, grid=True, recursive=True, on_dup='replace', strict=False, verbose=0)#
- Parameters:
- pycsamt.emtools.plot_sites_fit_grid(sites, pred_sites, *, components=('xx', 'xy', 'yx', 'yy'), quantity='rhoa', x_axis='period', phase_range=(-180.0, 180.0), stations=None, ncols_groups=2, comp_wspace=0.1, group_hspace=0.18, height_ratio=(2, 1), axes=None, figsize_scale=(4.0, 3.0), colors_meas=None, color_fit_te=<object object>, color_fit_tm=<object object>, marker=<object object>, ms=<object object>, lw=<object object>, ls_meas=<object object>, lw_fit=2.0, ls_fit='-', show_error_bars=True, show_mode_legend=True, title_group_fmt='{station}', ylim_rhoa=None, ylim_phase=None, grid=True, recursive=True, on_dup='replace', strict=False, verbose=0)#
- pycsamt.emtools.lcurve_table(misfit, rough, lam=None, *, sort='auto', method='curvature', smooth=3, skip=1, return_dict=False)#
- pycsamt.emtools.plot_lcurve(misfit, rough, lam=None, *, labels=None, colors=None, cmap='viridis', marker='o', ms=3.0, lw=1.4, alpha=0.9, show_points=True, show_path=True, arrow_every=0, method='curvature', smooth=3, skip=1, show_corner=True, corner_style=None, show_inset=True, inset_loc=(0.62, 0.12, 0.32, 0.32), figsize=(6.0, 4.6), ax=None)#
- pycsamt.emtools.coherence_matrix(sp)#
Compute the inter-channel coherence matrix.
The squared coherence between channels i and j is:
\[\gamma^2_{ij}(f) = \frac{|S_{ij}(f)|^2}{S_{ii}(f)\,S_{jj}(f)}\]- Parameters:
sp (Spectra) – Cross-spectra container.
- Returns:
coh – Real-valued coherence matrix per frequency, values in [0, 1].
- Return type:
ndarray, shape
(n_freq, n_chan, n_chan)
- pycsamt.emtools.psd_table(sp_input, *, normalize=False, api=None)#
Power spectral density per channel as a tidy DataFrame.
- pycsamt.emtools.coherence_table(sp_input, *, pairs=None, api=None)#
Inter-channel squared coherence as a tidy DataFrame.
- pycsamt.emtools.snr_table(sp_input, *, pairs=None, api=None)#
Signal-to-noise ratio estimated from squared coherence.
Uses the coherence-based estimator:
\[\text{SNR} = \frac{\gamma^2}{1 - \gamma^2}\]with the dB version
SNR_dB = 10 log₁₀(SNR).
- pycsamt.emtools.band_select(sp, f_min, f_max)#
Return a new
Spectrarestricted to the frequency band[f_min, f_max]Hz.
- pycsamt.emtools.mask_low_coherence(sp, *, pairs=None, threshold=0.5, require_all=False)#
Boolean mask of frequencies with sufficient coherence.
- Parameters:
- Returns:
mask –
Truewhere coherence is acceptable.- Return type:
ndarray of bool, shape
(n_freq,)
- pycsamt.emtools.spectra_summary(sp, *, api=None)#
Compact per-frequency summary table.
Columns:
freq,period,bw,avgt,rotspec, plus the diagonal PSD and mean off-diagonal coherence for each channel.
- pycsamt.emtools.plot_psd(sp, *, channels=None, log_psd=True, lw=<object object>, alpha=<object object>, title='', figsize=(9, 5), ax=None)#
Plot the power spectral density per channel.
The x-axis follows
PYCSAMT_CONTROL(log₁₀ period by default). Colours come frommultiline.- Parameters:
sp (Spectra)
channels (sequence of int, optional) – Channel indices to plot. Default = all channels.
log_psd (bool) – Display log₁₀(PSD) when
True.lw (float) – Line width. Default:
PYCSAMT_STYLE.multiline.lw.alpha (float) – Line alpha. Default:
PYCSAMT_STYLE.multiline.alpha.title (str)
ax (Axes or None)
- Returns:
ax
- Return type:
Axes
- pycsamt.emtools.plot_coherence(sp, *, pairs=None, threshold=0.5, show_threshold=True, lw=<object object>, alpha=<object object>, title='', axes=None, figsize=None)#
Plot squared coherence for selected channel pairs.
Each pair gets its own sub-axis arranged in a grid. A dashed horizontal line marks threshold.
- Parameters:
sp (Spectra)
pairs (list of (i, j), optional) – Default = all upper-triangle pairs.
threshold (float) – Quality threshold drawn as a dashed line. Default 0.5.
show_threshold (bool)
lw (float) – Style defaults from
PYCSAMT_STYLE.multiline.alpha (float) – Style defaults from
PYCSAMT_STYLE.multiline.title (str)
figsize ((float, float) or None) – Auto-computed from the number of pairs when
None.
- Returns:
axes
- Return type:
ndarray of Axes, shape
(n_pairs,)
- pycsamt.emtools.plot_spectra_matrix(sp, *, freq_idx=0, quantity='abs', cmap=<object object>, log_scale=True, title='', ax=None, figsize=(7, 6))#
Visualise the full cross-spectral density matrix at one frequency.
The matrix is drawn as a colour image. The diagonal shows auto- spectra (PSD); the upper and lower triangles show the magnitude (or real/imaginary part) of the cross-spectra.
- Parameters:
sp (Spectra)
freq_idx (int) – Index into
sp.freqfor the frequency slice.quantity ({'abs', 'real', 'imag', 'phase'}) – Quantity to colour.
cmap (str or _UNSET) – Colour map. Defaults to
"viridis"(abs) or"RdBu_r"(real/imag/phase).log_scale (bool) – Apply log₁₀ to the absolute value when
quantity="abs".title (str)
- Returns:
fig
- Return type:
Figure
- pycsamt.emtools.plot_z_from_spectra(sp, *, e_labels=('EX', 'EY'), h_labels=('HX', 'HY'), ridge=None, estimate_error=False, show_error=True, title='', axes=None, figsize=(10, 5))#
Plot apparent resistivity and phase recovered from spectra.
Calls
to_Z()internally and renders the result with the standard MT component styling fromPYCSAMT_STYLE.- Parameters:
sp (Spectra)
e_labels (tuple of str) – Channel type labels used for the E and H blocks in
to_Z().h_labels (tuple of str) – Channel type labels used for the E and H blocks in
to_Z().ridge (float or None) – Tikhonov regularization for S_HH inversion.
estimate_error (bool) – Estimate 1-σ errors in
to_Z(). DefaultFalse(avoids DoF warnings when metadata is incomplete).show_error (bool) – Shade error envelope when errors are available.
title (str)
- Returns:
fig
- Return type:
Figure
- pycsamt.emtools.plot_tipper_from_spectra(sp, *, h_labels=('HX', 'HY'), ridge=None, estimate_error=False, show_error=True, title='', axes=None, figsize=(10, 5))#
Plot the induction tipper magnitude and phase from spectra.
Displays the real and imaginary parts of T_x and T_y as well as their magnitudes on a two-panel figure (amplitude | phase).
- pycsamt.emtools.plot_psd_section(sp_input, *, channel=0, log_psd=True, cmap='viridis', vmin=None, vmax=None, section='pseudosection', title='', figsize=None, ax=None)#
Pseudo-section of PSD across stations (station × period).
Interpolates all Spectra objects to a common log-spaced frequency grid before assembling the 2-D colour map.
- pycsamt.emtools.plot_coherence_section(sp_input, *, pair=None, threshold=0.5, show_threshold=True, cmap='RdYlGn', section='pseudosection', title='', figsize=None, ax=None)#
Pseudo-section of coherence across stations (station × period).
- Parameters:
pair ((int, int) or None) – Single channel pair
(i, j)to display. WhenNonethe mean over all upper-triangle pairs is shown.threshold (float) – Value shown by the shared colorbar. Default 0.5.
show_threshold (bool) – Add a contour at threshold when
True.cmap (str) – Default
"RdYlGn"— red=low, green=high coherence.section (str or SectionStyle)
title (str)
ax (Axes or None)
- Returns:
ax
- Return type:
Axes
- pycsamt.emtools.check_em_kind(objs, /)#
Validate a collection of EM objects and return their common kind.
Ensures that all elements in
objsare instances of eitherEdiorZ, but not a mix of both. Returns the common kind as"EDI"or"Z". Raises if the collection is empty, contains non-EM objects, or mixes kinds.- Parameters:
objs (iterable) – Iterable of objects expected to be all
Edior allZ. Strings and bytes are not valid inputs.- Returns:
"EDI"if all objects are EDI instances, else"Z"if all are Z instances.- Return type:
- Raises:
TypeError – If
objsis not an iterable, or is a string/bytes, or is empty.EMError – If an element is neither
EdinorZ, or if the iterable mixesEdiandZ.
Notes
Uses
is_instance_extended()to be robust to class reloads and alternate import paths.Examples
>>> from pycsamt.utils.em import check_em_kind >>> # assuming `eds` is a list of Edi instances >>> check_em_kind(eds) 'EDI' >>> # assuming `zs` is a list of Z instances >>> check_em_kind(zs) 'Z'
- pycsamt.emtools.extract_z_list(objs, /)#
Return a list of
Zobjects from EDI or Z inputs.If
objsis a collection ofEdi, each element’s.Zattribute is extracted. Ifobjsis already a collection ofZ, it is returned as a plain list.- Parameters:
objs (iterable) – Iterable of EM objects. Must be all
Edior allZ. Seecheck_em_kind().- Returns:
List of
Zinstances.- Return type:
- Raises:
TypeError – If
objsis not an iterable or is empty.EMError – If
objsmixesEdiandZ, contains non-EM objects, or anEdiis missing the.Zattribute.
Notes
Uses
check_em_kind()to validate homogeneity.Examples
>>> zs = extract_z_list(list_of_edi) # from EDI inputs >>> zs = extract_z_list(list_of_z) # already Z
- pycsamt.emtools.parse_tensor(out='resxy', *, tensor=None, component=None, kind='complex', **kws)#
Parse and validate a tensor request, returning name and component.
This helper normalizes shorthand like
'resxy'or explicit pairs liketensor='z', component='xy'and validates the desired numeric kind (e.g., complex, real, imag, modulus).- Parameters:
out (str, default='resxy') – Compact token specifying the tensor and component, e.g.
'resxy','zxy','phaseyx', or a frequency request such as'freq'. When bothtensorandcomponentare given, they overrideout.tensor (str, optional) – Tensor name or alias. Accepted values include:
'z','tensor','res','rho','rhoa','phase','phs','freq','frequency'.component (str, optional) – EM component among
'xx','xy','yx','yy'. Required for'z','resistivity'and'phase'.kind ({'complex','real','imag','modulus'}, default='complex') – Numeric form of the tensor to be later extracted. Aliases are accepted, e.g.
're'``→’real’, ``'im'``→’imag’, ``'abs'/'mod'``→’modulus’, ``'reel'``→’real’``.**kws – Extra keywords ignored here. Kept for API symmetry with callers that also manage frequency expansion.
- Returns:
(name, comp) – Normalized tensor name and its component. The
nameis one of'z','resistivity','phase','_freq', or includes the'_err'suffix for error arrays where applicable.compis'xx','xy','yx','yy'orNonefor frequency.- Return type:
- Raises:
EMError – If only one of
tensororcomponentis provided.ValueError – If the parsed tokens are invalid, the component is missing for a tensor that requires it, or
kindis unknown.
Examples
>>> parse_tensor('zxy') ('z', 'xy') >>> parse_tensor(tensor='res', component='yx') ('resistivity', 'yx') >>> parse_tensor('freq') ('_freq', None) >>> parse_tensor('resx') Traceback (most recent call last): ... ValueError: 'Resistivity' component is missing...
- pycsamt.emtools.compute_qc(z_or_edis_obj_list, /, tol=0.5, *, interpolate_freq=False, return_freq=False, tensor='res', return_data=False, to_log10=False, return_qco=False)#
Assess data quality across a collection of EDI/Z objects.
Computes a global completeness ratio \(1 - \#NaN / (N_{freq} N_{sta})\) from a 2-D tensor (freq × station). Frequencies whose per-row missing-data fraction exceeds
tolare dropped. Optionally interpolate the retained frequencies and/or return a structured summary object.- Parameters:
z_or_edis_obj_list (list of Edi or Z) – Homogeneous collection of EM objects.
tol (float, default=0.5) – Tolerance threshold in
[0, 1]. A frequency row is considered invalid and dropped when its fraction of missing values exceedstol.interpolate_freq (bool, default=False) – If
True, interpolate the retained frequencies on a log-spaced grid spanning[min, max]with the same count.return_freq (bool, default=False) – If
True, return the retained (possibly interpolated) frequency vector.tensor ({'z','res','rho','rhoa','phase','phs'}, default='res') – Tensor family used for QC. The function first attempts the TE component (
'xy'); on failure, it falls back to TM ('yx').return_data (bool, default=False) – If
True, also return the subset of the tensor data corresponding to retained frequencies.to_log10 (bool, default=False) – If
True, returnlog10of the retained frequencies. Applied after interpolation ifinterpolate_freqis set.return_qco (bool, default=False) –
If
True, return aBunchwith the following attributes:rate_: global completeness ratiocomponent_: selected component,'xy'or'yx'mode_: EM mode,'TE'or'TM'freqs_: retained (optionally interpolated) frequenciesinvalid_freqs_: frequencies dropped by the QCdata_: tensor data at retained frequencies
Setting this flag forces
return_freq=Trueandreturn_data=True.
- Returns:
Depending on the flags.
rateis in[0, 1].- Return type:
Notes
The 2-D tensor has shape
(n_freq, n_stations). The per-row missing-data fraction isnan_count / n_stations.Examples
>>> rate, = compute_qc(data) >>> rate, freqs = compute_qc(data, return_freq=True) >>> rep = compute_qc(data, return_qco=True) >>> rep.rate_, rep.component_, rep.freqs_.shape (0.75, 'xy', (56,))
- pycsamt.emtools.full_freq(z_or_edis_obj_list, /, to_log10=False)#
Return the reference (clean) frequency grid for a collection.
The full frequency grid is taken from the site that contains the largest number of frequency samples (i.e., the most complete set). This is commonly used as the survey reference grid to which per-site tensors are aligned.
- Parameters:
- Returns:
The reference frequency vector.
- Return type:
- Raises:
TypeError – If the input is empty or not iterable.
EMError – If the collection mixes Edi and Z, or any element is missing a frequency attribute.
ValueError – If
to_log10=Trueand any frequency is non-positive.
Notes
For each object, the function looks for
.Z._freq/.Z.freq(whenEdi) or._freq/.freq(whenZ), using the first available attribute.Examples
>>> f = full_freq(edi_data) >>> f.shape (56,) >>> flog = full_freq(edi_data, to_log10=True)
- pycsamt.emtools.tensor2d(z_or_edis_obj_list, /, tensor='z', component='xy', kind='modulus', return_freqs=False, freqs=None, **kws)#
Build a 2-D matrix (freq × station) from a tensor collection.
Converts a collection of
EdiorZobjects into a 2-D array where rows are frequencies and columns are stations. Missing per-site frequencies are filled withNaN(no interpolation).- Parameters:
z_or_edis_obj_list (list of Edi or Z) – Collection of EM objects. All items must be
Edior allZ.tensor (str, default='z') – Tensor name or alias. Examples include
'z','res'/'rho'/'rhoa','phase'/'phs'. Error arrays are supported (e.g.,'resistivity_err').component ({'xx','xy','yx','yy'}, default='xy') – Component to extract for non-frequency tensors.
kind ({'modulus','real','imag','complex'}, default='modulus') – Numeric form for complex
Ztensors. Ignored for real arrays (e.g., resistivity/phase).return_freqs (bool, default=False) – If
True, also return the reference frequency vector.freqs (array-like, optional) – Precomputed reference frequency grid. If given, it is used directly and
get_full_frequencyis not called. The grid should include (or supersede) each site’s frequencies; any missing values will be filled withNaNduring alignment.**kws – Extra keywords forwarded to the frequency extractor when
freqsis not provided.
- Returns:
mat2d (ndarray (n_freq, n_stations)) – 2-D matrix of the requested tensor component.
(mat2d, freqs) (tuple) – If
return_freqs=True, also returns the frequency vector.
- Raises:
EMError – If inputs are missing, mixed, or illegal (e.g., frequency requested as the primary tensor here).
ValueError – If an unknown
kindis used for complexZ.
Notes
Each item in the input provides a 3-D tensor of shape
(n_freq, 2, 2). Index positions map as:xx -> (0, 0) xy -> (0, 1) yx -> (1, 0) yy -> (1, 1)
Examples
>>> phase_yx = tensor2d(data, tensor='phase', component='yx') >>> phase_yx.shape (56, 7)
- pycsamt.emtools.align_tensor(ref_freq, site_freq, z, fill_value=nan)#
Align a tensor component to a reference frequency grid.
The reference frequency grid (
ref_freq) is assumed to be the complete set of clean frequencies for the survey. Site-level measurements (site_freq) may be missing some frequencies due to interferences. This function maps the provided tensor values (z) to the reference grid and fills gaps withfill_value.- Parameters:
ref_freq (ArrayLike) – Reference frequency grid collected in the field. It should contain all survey frequencies.
site_freq (ArrayLike) – Frequencies measured at a site. All values must be present in
ref_freq(i.e., no out-of-grid frequencies).z (ndarray of complex) – Tensor component values measured at
site_freq. Typically the real or imaginary part for one ofxx,xy,yx, oryy. Length must matchsite_freq.fill_value (float, default=nan) – Value used to fill positions in the reference grid where the site tensor is missing.
- Returns:
Array aligned to
ref_freqwith gaps filled byfill_value. The dtype matchesz.dtype.- Return type:
ndarray of complex
- Raises:
EMError – If the number of mappable positions inferred from
site_freqdoes not match the length ofz.ValueError – If input shapes are inconsistent.
Notes
Internally uses
ismissing()to identify positions inref_freqthat correspond tosite_freq. The function does not interpolate values; it only aligns and fills gaps.Examples
>>> ref_freq = np.linspace(7e7, 1.0, 20) >>> site_freq = np.hstack([ref_freq[:7], ref_freq[12:]]) >>> z = np.random.randn(len(site_freq)) + 1j*np.random.randn( ... len(site_freq) ... ) >>> z_aligned = align_tensor(ref_freq, site_freq, z) >>> np.isnan(z_aligned).sum() # gaps inserted 5
- pycsamt.emtools.export_edis(edi_objs, new_z, savepath=None, **kws)#
Export new EDI files from a batch of EDI objects and Z tensors.
Applies updated impedance tensors to each input EDI object and writes new EDI files. This is typically used after applying corrections or replacements to the impedance tensor.
- Parameters:
edi_objs (list of
Edi) – Collection of EDI objects. All elements must be instances ofEdi(noZobjects are allowed here).new_z (list of ndarray (nfreq, 2, 2), complex) – Collection of impedance tensors matching
edi_objsone-to- one. Each tensor is a 3-D complex array with shape(n_freq, 2, 2).savepath (str, optional) – Directory to write the new EDI files. If
None, the EDI writer decides (often the current directory).**kws – Extra arguments forwarded to the EDI writer method
Edi.write_new_edifile(e.g., naming options).
- Returns:
Files are written as a side effect. The underlying writer may return paths, but this function does not collect them.
- Return type:
None
- Raises:
EdIDataError – If
edi_objsdoes not contain exclusively EDI objects.ValueError – If the lengths of
edi_objsandnew_zdiffer.
See also
exportediHelper for exporting a single EDI (if available in the API).
Examples
>>> # edi_objs: list[Edi], z_list: list[np.ndarray] >>> export_edis(edi_objs, z_list, savepath="out/")
- pycsamt.emtools.plot_confidence(z_or_edis_obj_list, /, tensor='res', view='1d', drop_outliers=True, distance=None, c_line=False, view_ci=True, figsize=(6.0, 2.0), fontsize=4.0, dpi=300, top_label='Stations', rotate_xlabel=90.0, fbtw=True, savefig=None, **plot_kws)#
Plot confidence diagnostics from tensor errors for EM data.
The default tensor for confidence evaluation is the resistivity error at TE mode (
'xy'). This plot helps decide which frequencies (and stations) are reliable, recoverable, or should be discarded before further processing.Three confidence levels are highlighted:
High: \(conf \ge 0.95\)
Soft: \(0.5 \le conf < 0.95\) (often recoverable)
Bad: \(conf < 0.5\) (usually discard)
- Parameters:
z_or_edis_obj_list (list of EDI or Z) – Collection of
EdiorZobjects.tensor (str, default='res') – Tensor selector. Accepted aliases include resistivity (
'res','rho','rhoa'), phase ('phase','phs'), or'z'. Error arrays are used for resistivity/phase automatically.view ({'1d', '2d'}, default='1d') – Plot as a 1-D profile (by station) or as a 2-D map (frequency vs. station).
drop_outliers (bool, default=True) – If
True, suppress outliers in the error tensor before plotting (filled withnan).distance (float, optional) – Inter-station distance. Used to scale the x-axis in 1-D view. If
None, a unit spacing of 1 is used.c_line (bool, default=False) – If
Trueandview='2d', overlay the confidence line.view_ci (bool, default=True) – If
True, show markers indicating confidence classes.figsize (tuple of float, default=(6.0, 2.0)) – Matplotlib figure size.
fontsize (float, default=4.0) – Base font size used for labels and ticks.
dpi (int, default=300) – Figure resolution in dots per inch.
top_label (str, default='Stations') – Title used for the top x-axis (station labels).
rotate_xlabel (float, default=90.0) – Rotation angle for station labels on the top x-axis.
fbtw (bool, default=True) – In 1-D view, fill between the curve and confidence bands.
savefig (str, optional) – Path to save the figure. If
None, the figure is shown.plot_kws (Any)
- Returns:
The Matplotlib Axes with the plotted content.
- Return type:
Notes
Internally, the function computes an error tensor for the chosen
tensor(resistivity/phase use error arrays). Confidence is aggregated across stations and displayed either as a 1-D line or a 2-D image with categorical markers.Examples
>>> ax = plot_confidence( ... emobj.ediObjs_, distance=20, view='2d', ... figsize=(6, 2) ... ) >>> ax = plot_confidence( ... emobj.ediObjs_, distance=20, view='1d', ... figsize=(6, 3), fontsize=5 ... )
- pycsamt.emtools.plot_strike(list_of_edis, /, kind=2, period_tolerance=0.05, text_pad=1.65, rot_z=0.0, **kws)#
Plot strike angles from invariants and phase tensor as rose/polar diagrams.
Accepts a single .edi file path, a directory containing .edi files, or an iterable of .edi paths. Files are validated before plotting. Output is produced by
mtpy.imaging.plotstrike. PlotStrike, with console output muted.- Parameters:
list_of_edis (str or iterable of str) – Path to an .edi file, a directory of .edi files, or a list/tuple of .edi file paths.
kind ({1, 2}, default=2) –
Plot style for
PlotStrike: -1: plot individual decades in one plot. -2: plot all period ranges in a polar diagram for eachstrike estimate.
period_tolerance (float, default=0.05) – Tolerance to match periods across different EDI files.
text_pad (float, default=1.65) – Padding of the angle label at the bottom of each polar diagram.
rot_z (float, default=0.0) – Clockwise rotation (degrees) applied to the tensor.
**kws – Extra keyword arguments forwarded to
PlotStrike(e.g.,plot_range,plot_tipper,fold,plot_orientation, color settings, etc.).
- Returns:
Plots are created as a side effect.
- Return type:
None
Notes
Files are validated with
IsEdi._assert_edi.Third-party output is muted via
nullify_output().
Examples
>>> plot_strike("/path/to/edis_dir") >>> plot_strike("/path/to/site.edi", kind=1) >>> plot_strike(["a.edi", "b.edi"], rot_z=10.0)
- pycsamt.emtools.plot_tensors(z_or_edis_obj_list, /, station='S00', zplot=False, show_error_bars=False, **kwargs)#
Plot tensors for one station (compat wrapper).
This is a compatibility wrapper around
plot_station_tensors(). It preserves the legacy API—station,zplot(for impedance vs. app. resistivity/ phase), andshow_error_bars—and forwards any additional styling options to the underlying plotter.- Parameters:
z_or_edis_obj_list (list of Edi or Z) – Collection of EM objects containing either
Edi(from which the embeddedZis extracted) orZdirectly.station (int or str, default='S00') – Target station index or label. Strings such as
'S00'are parsed to 0-based indices.zplot (bool, default=False) – If
True, plot real/imag parts of the impedance tensor (Z). IfFalse, plot apparent resistivity and phase.show_error_bars (bool, default=False) – Whether to display error bars.
**kwargs – Additional plotting options passed to
plot_station_tensors()(e.g.,color_mode, markers, line widths, legend style, font sizes, etc.).
- Returns:
The
Zobject for the selected station.- Return type:
Notes
The heavy lifting (layout, filtering, styling) is handled by
plot_station_tensors(). This wrapper exists to maintain source compatibility with v1.x code.Examples
>>> z = plot_tensors(edi_list, station='S03', zplot=True) >>> z = plot_tensors(edi_list, station=0, show_error_bars=True)
- pycsamt.emtools.plot_station_tensors(z_or_edis_obj_list, /, station='S00', *, plot_z=False, show_error_bars=True, **kwargs)#
Plot tensors for one station: resistivity/phase or Z real/imag.
By default, the function plots apparent resistivity and phase panels for the four components (xx, xy, yx, yy). If
plot_zisTrue, it plots the real and imaginary parts of the impedance tensor instead. Error bars can be displayed or hidden.- Parameters:
z_or_edis_obj_list (list of Edi or Z) – Collection containing either
EdiorZobjects. When EDI objects are provided, the embeddedZis extracted.station (int or str, default='S00') – Target station index or label. Strings like
'S00'are parsed to an integer index (0-based).plot_z (bool, default=False) – If
True, plot real/imag parts ofZ. Otherwise, plot apparent resistivity and phase.show_error_bars (bool, default=True) – Whether to include error bars on each panel.
**kwargs –
Plot customization such as: -
fig_size(tuple, default=(6, 6)) -dpi(int, default=300) -subplot_wspace(float, default=0.3) -phase_limits(tuple[min,max] in deg) -freq_limits(tuple[min,max] in Hz) -period_limits(tuple[min,max] in s) -mod_base(int, default=360) - style:color_mode(‘color’|’bw’), markers, line widths,legend style, font sizes, etc.
- Returns:
The
Zobject for the selected station.- Return type:
Notes
The function expects the station’s
Zobject to provide (or compute on demand) the following arrays with shape(n_freq, 2, 2): -resistivity,resistivity_err-phase,phase_err-z(complex),z_err(complex) and a frequency vector_freq(Hz).Examples
>>> z = plot_station_tensors(edi_list, station=3) >>> z = plot_station_tensors( ... edi_list, station="S00", plot_z=True, ... show_error_bars=False, color_mode="bw" ... )
- pycsamt.emtools.wrap_phase(phase, value_range=None, mod_base=360)#
Wrap phase values to a target range with a given periodic base.
By default, phases are wrapped into the interval
[0, mod_base). Whenvalue_rangeis provided, the wrapped phases are linearly remapped from[0, mod_base)to the desired interval.- Parameters:
phase (array-like) – Phase values (any shape), possibly negative or outside the target range.
value_range ({None, scalar, (min, max)}, optional) – Target interval for the output. -
None: return values in[0, mod_base). - scalar: treated as(0, scalar). - tuple/list(min, max): custom interval;min < max.mod_base ({90, 180, 270, 360}, default=360) – Periodicity used for wrapping (degrees).
- Returns:
Wrapped (and optionally remapped) phase values with the same shape as the input and dtype float.
- Return type:
np.ndarray
Notes
For the common symmetric range
(-180, 180], setvalue_range=(-180, 180)withmod_base=360.The remapping is affine: values in
[0, mod_base)are scaled to[min, max).
Examples
>>> x = np.array([-540, -180, 0, 180, 360, 540]) >>> # Default: [0, 360) >>> wrap_phase(x, mod_base=360) array([180., 180., 0., 180., 0., 180.])
>>> # Symmetric range (-180, 180) >>> wrap_phase(x, value_range=(-180, 180), mod_base=360) array([-180., -180., 0., 180., 0., 180.])
>>> # Custom half-range [0, 180) >>> wrap_phase(x, value_range=180, mod_base=360) array([90., 90., 0., 90., 0., 90.])
Processing and QC#
Quality-control confidence ratios for EM transfer functions. |
|
Polar uncertainty diagnostics for CSAMT impedance data. |
|
pycsamt.emtools.spectra |
|
Tensor and Dimensionality#
3-D axial anisotropy analysis for CSAMT impedance tensor data. |
|
Gradient-based apparent resistivity pseudo-sections for CSAMT. |
Source and Field-Zone Effects#
Phased-array (PAS) transmitter design and radiation pattern analysis for CSAMT. |
|
CSAMT source overprint and shadow effect analysis. |
|
Controlled-source ultra-audio MT depth and survey-planning tools. |
|
Public QC Plot Functions#
- pycsamt.emtools.qc.overlay_noise_cone(ax, period, lo, hi, *, color='0.6', alpha=0.18)[source]#
Overlay lower and upper noise envelopes on an existing period axis.
- pycsamt.emtools.qc.overlay_spectral_holes(ax, sites, *, thresh_dec=0.3, recursive=True, on_dup='replace', strict=False, verbose=0)[source]#
Highlight gaps in spectral coverage on an existing QC plot.
- pycsamt.emtools.qc.plot_consistency_fan(sites, *, station=None, other=None, comps=('xy', 'yx'), pcts=(10.0, 50.0, 90.0), n_draws=200, figsize=(8.6, 4.2), recursive=True, on_dup='replace', strict=False, verbose=0, ax=None)[source]#
Plot cross-station response consistency as a fan diagram.
- pycsamt.emtools.qc.plot_coverage_psection(sites, *, metric='presence', alpha_by='none', section='dynamic', figsize=None, recursive=True, on_dup='replace', strict=False, verbose=0, ax=None)[source]#
Plot frequency coverage and data availability as a pseudosection.
- pycsamt.emtools.qc.plot_qc_quicklook(sites, *, axes=None, figsize=(10.0, 8.0), recursive=True, on_dup='replace', strict=False, verbose=0)[source]#
Create a compact multi-panel quality-control summary for a survey.
- pycsamt.emtools.qc.plot_snr_hist(sites, *, bins=40, figsize=(7.2, 3.6), recursive=True, on_dup='replace', strict=False, verbose=0, ax=None)[source]#
Plot the distribution of signal-to-noise ratios across survey data.