Multi-Station Diagnostic Panels#
pycsamt.emtools.plot is the multi-station plotting layer for
response diagnostics. It is the place to go when one station at a time
is too narrow, but a map or pseudo-section is too compressed.
The module covers five common plotting jobs:
compact apparent-resistivity and phase panels for many stations;
raw full-tensor station groups;
response plus tipper quality-control panels;
before/after comparison figures;
measured-versus-predicted fit grids with per-component RMS labels.
Full callable signatures live in the API reference. This page explains when to use each figure, which arguments matter, and how to build reproducible plotting scripts.
Where This Module Fits#
Use pycsamt.emtools.plot after loading and inspecting data. It does
not replace the first-look inventory tools in inspect or the
specialized tensor/impedance diagnostics. Instead, it gives dense
station-group figures for QC, comparison, and reporting.
Function |
Best Use |
Figure Shape |
|---|---|---|
|
Quick rho/phase panels for selected stations. |
One station per small two-row group. |
|
Full-tensor raw-data review. |
One station group, one column per component. |
|
Joint impedance and tipper QC. |
Rho/phase rows plus Tx/Ty rows. |
|
Raw vs processed or before vs after. |
Paired columns per station. |
|
Observed vs predicted inversion-response review. |
Component columns with RMS labels. |
All functions accept a path, Sites object, collection, or compatible
iterable. Internally they normalize inputs with ensure_sites unless
a function has a special duplicate-preservation mode.
Load Once, Plot Many#
Normalize the survey once, then pass the resulting object into each plot. This makes scripts faster and keeps failure points obvious.
1from pathlib import Path
2
3from pycsamt.emtools import ensure_sites
4
5survey = ensure_sites(
6 Path("data/AMT/WILLY_DATA/L18PLT"),
7 recursive=True,
8 on_dup="replace",
9 strict=True,
10 verbose=1,
11)
12
13stations = ["18-001A", "18-007U", "18-016A", "18-018A"]
Use a short station list for most figures. The functions can plot many stations, but readability falls quickly when every panel has multiple components and legends.
Quick Station Panels#
plot_sites_panels is the simplest overview. Each station gets two
stacked panels: apparent resistivity or impedance magnitude on top, and
phase below. The default components are "xy" and "yx".
1import matplotlib.pyplot as plt
2
3from pycsamt.emtools import ensure_sites, plot_sites_panels
4
5survey = ensure_sites("data/AMT/WILLY_DATA/L18PLT", strict=True)
6
7fig = plot_sites_panels(
8 survey,
9 stations=["18-001A", "18-007U", "18-016A", "18-018A"],
10 components=("xy", "yx"),
11 quantity="rhoa",
12 x_axis="period",
13 ncols=4,
14 show_error_bars=True,
15 show_legend=True,
16)
17
18fig.savefig("l18plt_station_panels.png", dpi=200)
19plt.close(fig)
Use this when you want a quick visual sweep of several stations. It is
not meant to show every tensor component or tipper row. For that, use
plot_raw_sites_1d or plot_response_tipper.
Choose Rho Or Impedance Magnitude#
Most field reports use apparent resistivity, but sometimes you need to
look directly at impedance magnitude. Set quantity="impedance" to
plot log10(abs(Z)) instead of log10(rho_a).
1from pycsamt.emtools import plot_sites_panels
2
3fig = plot_sites_panels(
4 "data/AMT/WILLY_DATA/L18PLT",
5 stations=["18-001A", "18-016A"],
6 components=("xx", "xy", "yx", "yy"),
7 quantity="impedance",
8 phase_range=(-180.0, 180.0),
9 ncols=2,
10 show_legend=True,
11)
12
13fig.savefig("impedance_magnitude_panels.png", dpi=200)
Use quantity="impedance" for tensor debugging, instrument checks,
or comparison with diagnostics that work directly with Z. Use
quantity="rhoa" for ordinary geophysical response plots.
Phase Range And X Axis#
The high-level panel function has explicit x_axis and
phase_range arguments.
1from pycsamt.emtools import plot_sites_panels
2
3fig = plot_sites_panels(
4 "data/AMT/WILLY_DATA/L18PLT",
5 stations=["18-001A", "18-007U"],
6 components=("xy", "yx"),
7 x_axis="frequency",
8 phase_range=(0.0, 360.0),
9 ylim_phase=(0.0, 360.0),
10 ncols=2,
11)
12
13fig.savefig("frequency_axis_phase_0_360.png", dpi=200)
Use phase_range=None when you want to show the raw phase values
without wrapping. Use an explicit range when comparing stations whose
phase should share a common display convention.
Raw Full-Tensor Panels#
plot_raw_sites_1d is designed for raw or nearly raw response review.
Each station is a group. Each selected component becomes a column. Each
component column contains rho above phase.
1import matplotlib.pyplot as plt
2
3from pycsamt.emtools import ensure_sites, plot_raw_sites_1d
4
5survey = ensure_sites("data/AMT/WILLY_DATA/L18PLT", strict=True)
6
7fig = plot_raw_sites_1d(
8 survey,
9 stations=["18-001A", "18-007U", "18-016A"],
10 components=("xx", "xy", "yx", "yy"),
11 raw=True,
12 force_style=False,
13 ncols_groups=3,
14 show_error_bars=True,
15 show_component_legend=True,
16)
17
18fig.savefig("raw_full_tensor_panels.png", dpi=200)
19plt.close(fig)
With raw=True the function uses the package raw-data style, which
is deliberately plain. In tests this style is black by default. That is
useful for first QC because the display does not imply interpretation
by colour.
Force Component Colours#
After the raw diagnostic pass, use force_style=True to restore the
usual component colours while keeping the same layout.
1from pycsamt.emtools import plot_raw_sites_1d
2
3fig = plot_raw_sites_1d(
4 "data/AMT/WILLY_DATA/L18PLT",
5 stations=["18-001A", "18-007U", "18-016A"],
6 components=("xy", "yx"),
7 raw=True,
8 force_style=True,
9 ncols_groups=3,
10)
11
12fig.savefig("raw_panels_component_colours.png", dpi=200)
This is a good second view: first look for obvious raw-data problems in plain black, then re-render with component colours when you want to compare modes quickly.
Use Display Control#
plot_raw_sites_1d and plot_response_tipper read display policy
from PYCSAMT_CONTROL unless you pass a specific control object. This
is how you change x-axis convention, phase wrapping, and rho display
consistently.
1import matplotlib.pyplot as plt
2
3from pycsamt.api.control import PYCSAMT_CONTROL
4from pycsamt.emtools import plot_raw_sites_1d
5
6with PYCSAMT_CONTROL.context(
7 x__view="frequency",
8 rho__view="linear",
9 phase__range=(0.0, 360.0),
10):
11 fig = plot_raw_sites_1d(
12 "data/AMT/WILLY_DATA/L18PLT",
13 stations=["18-001A"],
14 components=("xy", "yx"),
15 label_mode="axis",
16 force_style=True,
17 show_component_legend=False,
18 ncols_groups=1,
19 figsize_scale=(6.0, 4.0),
20 )
21
22fig.savefig("raw_panels_frequency_linear_rho.png", dpi=200)
23plt.close(fig)
Use this pattern when one report needs a consistent non-default display style. It is better than editing labels or axes after the fact.
Response Plus Tipper#
plot_response_tipper adds tipper rows to the impedance response
layout. Use it for MT surveys that actually contain vertical-field
data. It is not useful for AMT/CSAMT lines where all stations lack
tipper.
1import matplotlib.pyplot as plt
2
3from pycsamt.emtools import ensure_sites, plot_response_tipper
4
5survey = ensure_sites("data/MT/kap03lmt_edis", strict=True)
6
7fig = plot_response_tipper(
8 survey,
9 stations=["kap103", "kap142", "kap151"],
10 components=("xy", "yx"),
11 tipper_components=("tx", "ty"),
12 tipper_span_group=True,
13 ylim_tipper=(-2.5, 2.5),
14 ncols_groups=3,
15)
16
17fig.savefig("kap03_response_tipper.png", dpi=200)
18plt.close(fig)
When tipper_span_group=True, each tipper row spans the station
group. This is often the clearest layout when you have only two
impedance components and want tipper to read as a station-level
property.
Compact Tipper Rows#
Set tipper_span_group=False when you prefer a compact repeated
layout where the tipper rows sit under each component column.
1from pycsamt.emtools import plot_response_tipper
2
3fig = plot_response_tipper(
4 "data/MT/kap03lmt_edis",
5 stations=["kap151"],
6 components=("xx", "xy", "yx", "yy"),
7 tipper_components=("tx", "ty"),
8 tipper_span_group=False,
9 show_tipper_error_bars=False,
10 show_component_legend=False,
11 ncols_groups=1,
12 figsize_scale=(7.2, 5.6),
13 shared_x_label_pad=0.11,
14)
15
16fig.savefig("kap151_response_tipper_compact.png", dpi=200, bbox_inches="tight")
Use the compact layout for one or two stations. Use the spanning layout when comparing several stations and you want fewer small axes.
Before And After Comparison#
plot_sites_compare pairs stations from two datasets. The usual use
is raw versus processed, before versus after static-shift correction, or
original versus smoothed.
1import matplotlib.pyplot as plt
2
3from pycsamt.emtools import ensure_sites, plot_sites_compare, smooth_mavg
4
5raw = ensure_sites("data/AMT/WILLY_DATA/L18PLT", strict=True)
6smoothed = smooth_mavg(raw, k=5)
7
8fig = plot_sites_compare(
9 raw,
10 smoothed,
11 stations=["18-001A", "18-007U", "18-016A"],
12 components=("xy", "yx"),
13 labels=("raw", "smoothed k=5"),
14 quantity="rhoa",
15 x_axis="period",
16 ncols_groups=3,
17 show_legend=True,
18)
19
20fig.savefig("raw_vs_smoothed_compare.png", dpi=200)
21plt.close(fig)
The function pairs stations by station name. If the second dataset is missing a station, the corresponding after-column is blank.
Compare Impedance Instead Of Rho#
The comparison view can also use quantity="impedance". This is
helpful when a processing step changes the complex tensor directly and
you want to avoid hiding that change behind apparent-resistivity
conversion.
1from pycsamt.emtools import ensure_sites, plot_sites_compare, smooth_mavg
2
3raw = ensure_sites("data/AMT/WILLY_DATA/L18PLT", strict=True)
4processed = smooth_mavg(raw, k=3)
5
6fig = plot_sites_compare(
7 raw,
8 processed,
9 stations=["18-001A", "18-016A"],
10 components=("xx", "xy", "yx", "yy"),
11 quantity="impedance",
12 phase_range=(-180.0, 180.0),
13 labels=("raw", "processed"),
14 ncols_groups=2,
15)
16
17fig.savefig("impedance_before_after.png", dpi=200)
Use fixed ylim_rhoa and ylim_phase when the before/after
comparison needs exact visual scale matching across multiple figures.
Measured Versus Predicted Fit Grid#
plot_sites_fit_grid is built for inversion QC. It pairs measured
and predicted stations by name, aligns predicted values onto measured
frequencies, plots both curves, and writes an RMS value in each
component panel.
1import matplotlib.pyplot as plt
2
3from pycsamt.emtools import ensure_sites, plot_sites_fit_grid, smooth_mavg
4
5measured = ensure_sites("data/AMT/WILLY_DATA/L18PLT", strict=True)
6
7# Demonstration only: a smoothed copy stands in for predicted data.
8# In production, pass forward-model or inversion-response EDI files.
9predicted = smooth_mavg(measured, k=5)
10
11fig = plot_sites_fit_grid(
12 measured,
13 predicted,
14 stations=["18-001A", "18-016A"],
15 components=("xy", "yx"),
16 quantity="rhoa",
17 phase_range=(-180.0, 180.0),
18 ncols_groups=2,
19 show_mode_legend=True,
20)
21
22fig.savefig("measured_vs_predicted_fit_grid.png", dpi=200)
23plt.close(fig)
The RMS is computed from the measured-predicted residual. When measured errors are available, the residual is weighted by the display-space error estimate. That means an apparently small visual difference can still produce a high RMS if the data errors are very small.
Understand TE And TM Fit Colours#
The fit grid uses separate fit colours for TE-like and TM-like components:
xxandxyuse the TE fit colour;yxandyyuse the TM fit colour.
1from pycsamt.emtools import ensure_sites, plot_sites_fit_grid, smooth_mavg
2
3measured = ensure_sites("data/AMT/WILLY_DATA/L18PLT", strict=True)
4predicted = smooth_mavg(measured, k=5)
5
6fig = plot_sites_fit_grid(
7 measured,
8 predicted,
9 stations=["18-001A"],
10 components=("xx", "xy", "yx", "yy"),
11 color_fit_te="#2ca02c",
12 color_fit_tm="#d62728",
13 lw_fit=2.2,
14 ls_fit="-",
15 ncols_groups=1,
16 figsize_scale=(8.0, 4.0),
17 show_mode_legend=False,
18)
19
20fig.savefig("fit_grid_custom_fit_colours.png", dpi=200, bbox_inches="tight")
Use custom fit colours when assembling figures for publication or when the default colours conflict with another report convention.
Choosing The Right Figure#
Here is a practical decision path:
I only need a quick rho/phase overviewUse
plot_sites_panelswithcomponents=("xy", "yx")and a short station list.I want a raw tensor QC viewUse
plot_raw_sites_1dwith all four components andraw=True.The survey has tipper and I need to inspect it with response curvesUse
plot_response_tipper. Confirm tipper exists withlist_missing_sectionsorsites_summaryfirst.I processed the data and need before/after panelsUse
plot_sites_comparewith the original and processedSitesobjects.I have model predictions or inversion responsesUse
plot_sites_fit_gridand read the RMS labels component by component.
Common Pitfalls#
- Too many stations in one figure
Start with four to six stations. If you need every station, create multiple figures by station group.
- Comparing figures with auto-scaled axes
Use fixed
ylim_rhoaandylim_phasewhen the visual comparison matters.- Using tipper plots on AMT lines with no tipper
The figure can still render, but it will not add useful interpretation. Check missing sections first.
- Treating a smoothed dataset as a real prediction
A smoothed copy is useful for demonstrating the plotting API, but it is not a forward-model response.
- Ignoring display control
If a report switches between period and frequency axes or between linear and log rho displays, use
PYCSAMT_CONTROL.contextso all plots share the same convention.
Save A Multi-Figure Plot Bundle#
The following script writes the core report figures for one line.
1from pathlib import Path
2
3import matplotlib.pyplot as plt
4
5from pycsamt.emtools import (
6 ensure_sites,
7 plot_raw_sites_1d,
8 plot_sites_compare,
9 plot_sites_fit_grid,
10 plot_sites_panels,
11 smooth_mavg,
12)
13
14out = Path("plot_report_l18plt")
15out.mkdir(parents=True, exist_ok=True)
16
17survey = ensure_sites("data/AMT/WILLY_DATA/L18PLT", strict=True)
18stations = ["18-001A", "18-007U", "18-016A", "18-018A"]
19processed = smooth_mavg(survey, k=5)
20
21fig = plot_sites_panels(
22 survey,
23 stations=stations,
24 components=("xy", "yx"),
25 ncols=4,
26 show_legend=True,
27)
28fig.savefig(out / "station_panels.png", dpi=200)
29plt.close(fig)
30
31fig = plot_raw_sites_1d(
32 survey,
33 stations=stations[:3],
34 components=("xx", "xy", "yx", "yy"),
35 ncols_groups=3,
36)
37fig.savefig(out / "raw_full_tensor.png", dpi=200)
38plt.close(fig)
39
40fig = plot_sites_compare(
41 survey,
42 processed,
43 stations=stations[:3],
44 components=("xy", "yx"),
45 labels=("raw", "smoothed"),
46 ncols_groups=3,
47)
48fig.savefig(out / "raw_vs_smoothed.png", dpi=200)
49plt.close(fig)
50
51fig = plot_sites_fit_grid(
52 survey,
53 processed,
54 stations=stations[:2],
55 components=("xy", "yx"),
56 ncols_groups=2,
57)
58fig.savefig(out / "fit_grid_smoothed_standin.png", dpi=200)
59plt.close(fig)
Worked Example#
The gallery example demonstrates the same plotting layer on bundled AMT and MT surveys, including a tipper-capable MT line and display-control overrides.
Open the rendered gallery page here: Multi-station diagnostic panels (pycsamt.emtools.plot).