17.2. Pipeline Concepts#
The pyCSAMT processing pipeline is a reproducible engine for MT, AMT, and CSAMT site collections. A pipeline is an ordered sequence of registered operations. Each operation receives the current site collection, either transforms it or inspects it, records what happened, and passes the appropriate collection to the next operation.
At the end of a run, pyCSAMT returns a
pycsamt.pipeline.PipelineResult. When an output directory is enabled,
the same run also writes processed EDI files, quality-control figures,
reports, and the exact pipeline.yaml snapshot used for the run. The
important point is that the data product and the processing recipe travel
together.
17.2.1. Why Pipelines Exist#
Field processing often begins as a notebook: remove power-line harmonics, drop duplicate frequencies, trim to an interpretation band, align stations onto a shared frequency grid, correct static shift, inspect tensor rotation, and make quality-control plots. That is a natural way to explore, but it can become fragile when the same decisions must be repeated on another line or reviewed months later.
Pipelines make the sequence explicit. They help you:
repeat the same workflow across multiple survey lines;
save the processing recipe alongside the outputs;
inspect which step changed the data;
run the same workflow from Python and the CLI;
generate reports and QC figures in a predictable directory tree;
share processing decisions with collaborators.
Mathematically, a processing pipeline is a composition. If the loaded survey is \(S_0\) and the pipeline has step transforms \(f_1,\ldots,f_n\), then the final collection is
Diagnostic steps fit the same mental model by returning the input collection unchanged while recording plots, tables, or warnings. A failed step under a non-strict error policy also passes forward the last valid collection; that is useful for diagnosis, but the report must be read as a failed or partial run.
17.2.2. Core Objects#
The public pipeline namespace is pycsamt.pipeline:
1>>> from pycsamt.pipeline import Pipeline, Step
2>>> Pipeline.__name__, Step.__name__
3('Pipeline', 'Step')
The main objects are:
Object |
Role |
|---|---|
Ordered sequence of |
|
Configured wrapper around one registered processing operation. It
binds a registry code such as |
|
|
step registry descriptor: code, registry name, category, function path, default parameters, QC plot functions, and whether the step returns a modified site collection. |
|
Named, ordered collection of steps for common workflows such as
|
One per executed step. Records timing, input/output station counts, saved plot paths, parameters, and any stored exception. |
|
Returned by |
17.2.3. The Mental Model#
A pipeline run is left-to-right data flow:
1input Sites
2 |
3 v
4Step 1: transform or inspect
5 |
6 v
7Step 2: transform or inspect
8 |
9 v
10...
11 |
12 v
13final Sites + PipelineResult + optional files
Most steps transform the site collection and return a new or modified collection. Diagnostic-only steps run checks or plots and pass the input collection through unchanged. The pipeline report is the companion record: it says which operation ran, with which parameters, how long it took, what it saved, and whether it failed.
17.2.4. Step Registry#
Pipeline steps are not arbitrary strings. They are registered in the step registry, and each registered step has:
a short pipeline step code, for example
NR001;a registry name, for example
notch_powerline;a category, for example
noise_removalorfrequency;default parameters;
a transform function;
optional QC plot functions;
a
returns_sitesflag.
The code and registry name both identify the same operation:
1>>> from pycsamt.pipeline import Step
2>>> notch_by_code = Step("NR001", mains_hz=50.0)
3>>> notch_by_name = Step("notch_powerline", mains_hz=50.0)
4>>> notch_by_code.code, notch_by_name.code
5('NR001', 'NR001')
Use the code form in configuration files and reports because codes are compact and stable. Use registry names in exploratory Python when they make intent easier to read.
Discover available steps from Python:
1>>> from pycsamt.pipeline import Pipeline
2>>> print(Pipeline.catalogue("frequency").splitlines()[0])
3Available pipeline steps
4>>> print(Pipeline.step_info("NR001").splitlines()[0])
5NR001 notch_powerline
Discover the same information from the CLI:
1pycsamt pipe steps
2pycsamt pipe steps --category frequency
3pycsamt pipe steps --info NR001
17.2.5. Configured Steps#
A pycsamt.pipeline.Step combines a registry entry with user parameter
overrides. The registry defaults are merged with your overrides:
1>>> from pycsamt.pipeline import Step
2>>> step = Step("NR001", mains_hz=60.0)
3>>> step.code
4'NR001'
5>>> step.params["mains_hz"]
660.0
If the registry default for NR001 includes n_harm and tol_hz, the
configured step still carries those defaults at run time. You only need to
provide the values that should change for the workflow. This merge is why a
step should be inspected before editing its params: a short configuration
may still imply several default behaviours.
17.2.6. Pipeline Structure#
A pipeline stores steps as (label, Step) tuples. The label names this
occurrence in this workflow. It appears in printed summaries, output
subdirectories, reports, and CLI slicing options.
1>>> from pycsamt.pipeline import Pipeline, Step
2>>> pipe = Pipeline(
3... [
4... ("notch", Step("NR001", mains_hz=50.0)),
5... ("select_band", Step("FREQ001", band_hz=(0.001, 10000.0))),
6... ("align_grid", Step("FREQ004")),
7... ("qc_snapshot", Step("QC001")),
8... ],
9... name="first_qc",
10... )
11>>> print(pipe)
12Pipeline 'first_qc' - 4 steps
13 ( 1) notch [NR001] Power-line Harmonic Notch mains_hz=50.0 n_harm=30 tol_hz=0.08
14 ( 2) select_band [FREQ001] Frequency Band Select band_hz=(0.001, 10000.0)
15 ( 3) align_grid [FREQ004] Frequency Grid Alignment
16 ( 4) qc_snapshot [QC001] QC Quick-Look Snapshot
Labels should be short, stable, and meaningful. Prefer
select_amt_band or correct_ss over vague labels such as step1.
Changing a label changes report names and slicing handles, even when the
underlying step code is unchanged.
17.2.7. Building A Pipeline#
There are four common ways to build a pipeline.
Build directly in Python:
1>>> from pycsamt.pipeline import Pipeline, Step
2>>> pipe = Pipeline([
3... ("notch", Step("NR001")),
4... ("drop_duplicates", Step("FREQ002")),
5... ("select_band", Step("FREQ001")),
6... ("qc_snapshot", Step("QC001")),
7... ])
8>>> type(pipe).__name__
9'Pipeline'
Build from a preset:
1>>> from pycsamt.pipeline import Pipeline
2>>> pipe = Pipeline.from_preset("basic_qc")
3>>> pipe.name
4'basic_qc'
Build from a config file:
1>>> from pycsamt.pipeline import Pipeline
2>>> # pipe = Pipeline.from_yaml("config/basic_qc.yaml")
3>>> # pipe = Pipeline.from_json("config/basic_qc.json")
4>>> # pipe = Pipeline.from_py("config/basic_qc.py")
Build from the CLI:
1pycsamt pipe run data/edis --preset basic_qc
2pycsamt pipe run data/edis --config config/basic_qc.yaml
3pycsamt pipe run data/edis --steps NR001,FREQ002,FREQ001,QC001
Configuration files are documented in Pipeline Configuration Files.
17.2.8. Presets#
Pipeline presets are named pipelines for common processing intentions. They are useful when you want a known baseline without writing every step manually.
Examples include:
basic_qcMinimal denoising and frequency cleanup. Good for first-pass inspection.
noise_reductionStacked noise-removal chain for high-EMI environments.
full_processingStandard chain for noise removal, frequency cleanup, skew gate, static-shift correction, and strike rotation.
publication_readyA longer chain for publication-quality outputs.
Use a preset directly:
1>>> from pycsamt.pipeline import Pipeline
2>>> pipe = Pipeline.from_preset("publication_ready")
3>>> pipe.name
4'publication_ready'
Or generate an editable config from a preset:
1pycsamt pipe init --preset publication_ready \
2 --name line22_publication \
3 --output config/line22_publication.yaml
See Pipeline Presets for the dedicated preset guide.
17.2.9. Mutable Until Run#
A pipeline can be edited before it starts running:
1>>> from pycsamt.pipeline import Pipeline, Step
2>>> pipe = Pipeline.from_preset("full_processing")
3>>> pipe.remove("mask_skew")
4>>> pipe.append("final_qc", Step("QC001"))
5>>> pipe.replace("notch", Step("NR001", mains_hz=60.0))
During Pipeline.run, the step list is protected from mutation. This
prevents accidental changes while step results and reports are being produced.
Think of the run as an immutable transaction: once execution begins, the
pipeline must be the same object that later appears in pipeline.yaml.
17.2.10. Run Lifecycle#
Calling Pipeline.run performs these operations in order:
Resolve the runtime configuration.
Resolve the output directory.
Save a canonical
pipeline.yamlsnapshot when output is enabled.For each configured step:
count input sites;
run the step transform;
handle errors according to
on_step_error;generate and save QC plots when enabled;
optionally save intermediate EDI snapshots;
create a
pycsamt.pipeline.StepResult.
Write final processed EDI files when
save_edis=True.Write HTML and/or text reports when
save_report=True.Return a
pycsamt.pipeline.PipelineResult.
Example:
1>>> from pycsamt.emtools import ensure_sites
2>>> from pycsamt.pipeline import Pipeline
3>>> sites = ensure_sites("data/3edis", recursive=True, verbose=0)
4>>> pipe = Pipeline.from_preset("basic_qc")
5>>> # result = pipe.run(
6>>> # sites,
7>>> # outdir="results/basic_qc",
8>>> # save_plots=True,
9>>> # save_edis=True,
10>>> # save_report=True,
11>>> # )
12>>> len(sites)
133
The final run is commented here because it writes a full output directory. Use the same call in a project script when you want processed files, plots, and reports to be created.
17.2.11. Output Resolution#
The output directory is resolved in this order:
explicit
outdirpassed toPipeline.run;output_dirstored on a pipeline loaded from a config file;global
PYCSAMT_PIPE.output_root.
Passing outdir=None is an explicit opt-out: the pipeline runs in memory
and writes no output files.
1>>> # Write to the config/default output directory.
2>>> # result = pipe.run(sites)
3>>> # Override output directory for this run.
4>>> # result = pipe.run(sites, outdir="results/experiment_01")
5>>> # In-memory run: no files are written.
6>>> # result = pipe.run(sites, outdir=None)
17.2.12. Output Directory Contract#
When output is enabled, pyCSAMT writes a predictable run directory:
1results/basic_qc/
2|-- processed/
3| `-- *.edi
4|-- plots/
5| |-- 01_notch/
6| |-- 02_drop_duplicates/
7| `-- ...
8|-- pipeline.yaml
9|-- report.html
10`-- summary.txt
pipeline.yamlReproducible snapshot of the exact pipeline that was run.
processed/Final processed EDI files when
save_edis=True.plots/QC figures generated after individual steps when
save_plots=True.report.htmlandsummary.txtRun reports when
save_report=Trueand the corresponding report formats are enabled.
The output-directory details are documented in Pipeline Outputs.
17.2.13. Error Handling#
Pipeline error behavior is controlled by PYCSAMT_PIPE.on_step_error or by
the CLI --on-error option.
"raise"Re-raise the step exception immediately and stop the run.
"warn"Store the exception in the step result, warn, continue with the previous site collection, and mark the final
PipelineResultas not OK."skip"Store the exception and continue silently with the previous site collection.
Use "raise" during debugging and strict production validation. Use
"warn" for exploratory processing when you want a full report showing
which steps failed. A run that continued after a failed step is diagnostic
evidence, not a final processing product.
17.2.14. Runtime Configuration#
Pipeline runtime defaults live in pycsamt.pipeline.PYCSAMT_PIPE.
Configure them globally:
1>>> from pycsamt.pipeline import configure_pipe
2>>> configure_pipe(
3... output_root="results",
4... on_step_error="warn",
5... plot_dpi=200,
6... plot_fmt="png",
7... show_progress=True,
8... )
Or temporarily with a context manager:
1>>> from pycsamt.pipeline import PYCSAMT_PIPE
2>>> # with PYCSAMT_PIPE.context(plot_dpi=300, plot_fmt="pdf"):
3>>> # result = pipe.run(sites, outdir="results/high_resolution")
Important runtime settings include:
Setting |
Meaning |
|---|---|
|
Default output root when no explicit run output is provided. |
|
Name of the subdirectory for processed EDI files. |
|
Name of the subdirectory for QC figures. |
|
|
|
Whether to write EDI snapshots after each successful step. |
|
Whether to print progress while running. |
|
Saved figure resolution and format. |
|
Report types to write, usually |
17.2.15. PipelineResult#
Pipeline.run returns a pycsamt.pipeline.PipelineResult. Use it as
the programmatic summary of the run:
1>>> # result = pipe.run(sites, outdir="results/basic_qc")
2>>> # result.ok
3>>> # result.n_errors
4>>> # result.plots
5>>> # result.processed_paths
6>>> # print(result.summary())
result.sites_inOriginal site collection passed to
Pipeline.run.result.sites_outFinal site collection after all steps.
result.step_resultsOrdered list of step records.
result.plotsAll saved plot paths across every step.
result.processed_pathsWritten processed EDI files.
result.okTruewhen every step completed without error.
17.2.16. StepResult#
Each pycsamt.pipeline.StepResult records what happened during one
step:
1>>> # for step_result in result.step_results:
2>>> # print(step_result.summary_line())
3>>> # if not step_result.ok:
4>>> # print(step_result.error)
Useful fields include step_idx, step_name, step_code,
step_label, params, elapsed_sec, plots, n_sites_in,
n_sites_out, and error. These fields are the fine-grained audit trail
behind a pipeline run: they explain why the final result is OK, warning-only,
or failed.
17.2.17. CLI And Python Equivalence#
The CLI and Python API use the same pipeline engine.
This Python call:
1>>> from pycsamt.emtools import ensure_sites
2>>> from pycsamt.pipeline import Pipeline
3>>> sites = ensure_sites("data/3edis", recursive=True, verbose=0)
4>>> pipe = Pipeline.from_yaml("config/basic_qc.yaml")
5>>> result = pipe.run(sites, outdir="results/basic_qc")
is conceptually equivalent to:
1pycsamt pipe run data/3edis \
2 --config config/basic_qc.yaml \
3 --out results/basic_qc
Use Python when the pipeline is part of a larger analysis script. Use the CLI when the workflow should be easy to repeat from a terminal, automation script, or processing log.
17.2.18. How Concepts Connect#
The pipeline documentation is organized around these ideas:
Pipeline Configuration Files explains how to store pipelines as YAML, JSON, or Python files.
Pipeline Presets explains built-in workflows and when to start from each one.
Pipeline Steps explains registered operations, and Extending The Pipeline explains runtime plugin registration.
Caching And Resume explains
cache=and how it makes a rerun resume.Live Observability explains
on_step, live progress, and run history.Pipeline Outputs explains the generated files and reports.
Pipeline CLI explains the
pycsamt pipecommand group.
17.2.19. In Short#
A pyCSAMT pipeline is a reproducible chain of registered steps:
1>>> from pycsamt.pipeline import Pipeline, Step
2>>> pipe = Pipeline([
3... ("notch", Step("NR001")),
4... ("band", Step("FREQ001")),
5... ("qc", Step("QC001")),
6... ])
7>>> # result = pipe.run(sites, outdir="results/basic_qc")
8>>> type(pipe).__name__
9'Pipeline'
The key ideas are simple: registered step codes define what can run, labels
define how a workflow is reported, configs define reproducibility, runtime
settings define output and error behavior, and PipelineResult records what
happened.