Prepare an Occam2D Inversion#
This tutorial shows how to prepare a 2-D Occam2D inversion from a cleaned EDI survey. It follows the practical pyCSAMT v2 workflow:
load and inspect the EDI survey;
run QC and optional static-shift correction;
choose the data modes, frequency band, error floors, and mesh controls;
build native Occam2D input files;
validate the run directory before launching the external solver;
optionally run Occam2D and inspect the result.
Occam2D is an external inversion program. pyCSAMT can prepare, validate, run, load, and plot the workflow files, but an actual inversion run requires a compiled Occam2D executable.
What You Will Learn#
After this tutorial you should be able to:
decide when Occam2D is an appropriate backend;
prepare a corrected site collection for inversion;
build the native Occam files with
pycsamt.models.occam2d.InputBuilder;understand the role of
OccamDataFile.dat,Occam2DMesh,Occam2DModel, andStartup;use
pycsamt.models.occam2d.OccamConfigto control data selection, error floors, mesh geometry, and startup values;run the same preparation from the CLI;
load finished Occam2D outputs with
pycsamt.models.occam2d.InversionResult.
When To Use Occam2D#
Occam2D is a good first production backend when:
stations form a mostly 2-D profile;
the geologic target is expected to vary mainly with profile distance and depth;
the deliverable should be a smooth resistivity section;
TE and TM apparent resistivity and phase curves are available;
you want native Occam-style files that can be archived and reviewed.
Occam2D is not the best first choice when:
the survey geometry is strongly 3-D;
station coordinates are unreliable;
source effects dominate the selected frequency band;
only a few stations remain after QC;
the target is a sharp blocky body that must be represented with hard boundaries rather than smooth gradients.
For backend choice, see Choosing A Model Backend.
Prepare the Survey#
Start from the same EDI loading and QC path used in the previous tutorials.
1from pathlib import Path
2
3from pycsamt.api import read_edis
4from pycsamt.emtools.qc import station_confidence_table
5
6run_root = Path("runs")
7run_root.mkdir(exist_ok=True)
8
9survey = read_edis(
10 "data/AMT/WILLY_DATA/L18PLT",
11 recursive=False,
12 strict=False,
13 progress=False,
14)
15sites = survey.collection
16
17confidence = station_confidence_table(
18 sites,
19 method="composite",
20 api=True,
21)
22print(confidence)
Example confidence output from the bundled WILLY L18PLT line:
station confidence coverage
18-001A 0.709038 1.0
18-002U 0.774634 1.0
18-003A 0.713303 1.0
18-004A 0.732182 1.0
18-005U 0.771060 1.0
Do not prepare inversion files until station names, coordinates, frequency coverage, and obvious quality problems have been checked. Occam2D will happily invert a bad file set if the native files are syntactically valid.
Apply Optional Static-Shift Correction#
If the QC review supports a static-shift correction, apply it to a copy of the survey and keep the original data available.
1from pycsamt.emtools.ss import correct_ss_ama
2
3corrected = correct_ss_ama(
4 sites,
5 sort_by="name",
6 half_window=3,
7 weights="tri",
8 max_skew=None,
9 inplace=False,
10 recursive=False,
11 verbose=1,
12)
If static-shift evidence is weak, use sites directly for the first trial
and compare that model with a corrected run later.
The previous tutorial showed that L18PLT has high skew and large
exploratory static-shift factors. For a conservative first Occam2D input
build, it is often better to start from sites and keep the corrected copy
for a comparison run.
Choose an Occam Configuration#
OccamConfig is the source-of-truth object for a native Occam2D run. It
controls data rows, error floors, mesh geometry, startup options, filenames,
and executable discovery.
1from pycsamt.models.occam2d import OccamConfig
2
3cfg = OccamConfig(
4 modes=["TE", "TM"],
5 freq_min=0.1,
6 freq_max=1000.0,
7 error_floor_rho=0.05,
8 error_floor_phase=0.5,
9 n_layers=32,
10 n_airlayers=4,
11 cell_size_horizontal=100.0,
12 cell_size_vertical_top=25.0,
13 depth_scale=1.15,
14 target_misfit=1.0,
15 max_iterations=80,
16 initial_rho=100.0,
17 data_file="OccamDataFile.dat",
18 mesh_file="Occam2DMesh",
19 model_file="Occam2DModel",
20 startup_file="Startup",
21 binary_name="Occam2D",
22)
Important options:
modes["TE"],["TM"], or["TE", "TM"]. TE uses theZxycomponent and TM usesZyx. Each selected mode writes apparent resistivity and phase rows.freq_minandfreq_maxFrequency limits in hertz. Use these to exclude unstable short-period or long-period bands before writing Occam data.
error_floor_rhoanderror_floor_phaseMinimum data uncertainties. These are critical because Occam seeks a normalized misfit. Unrealistically small errors force the model to chase noise.
n_layersandcell_size_horizontalMain controls on mesh size and parameter count. Smaller cells and more layers increase detail but also increase runtime and non-uniqueness.
target_misfitandmax_iterationsStartup controls for the external solver.
Write the configuration next to the run so it can be reviewed later:
1workdir = run_root / "L18PLT_occam2d_native"
2workdir.mkdir(parents=True, exist_ok=True)
3
4cfg.to_template(workdir / "occam2d.yml")
Build Native Occam2D Files#
Use pycsamt.models.occam2d.InputBuilder to write the four native input
files required before the external executable can run.
1from pycsamt.models.occam2d import InputBuilder
2
3builder = InputBuilder(
4 sites,
5 workdir=workdir,
6 config=cfg,
7 verbose=1,
8)
9
10builder.build(title="L18PLT pyCSAMT v2 Occam2D preparation")
11print(builder.summary())
For the tutorial configuration, the builder writes a complete native input set:
InputBuilder summary
workdir : runs/L18PLT_occam2d_native
sites : 28
freqs : 39
data pts : 4368
mesh : 42 x 36 cells
params : 512
modes : ['TE', 'TM']
The build chain is:
OccamData.from_ediconverts the site collection into Occam data rows.OccamMesh.from_databuilds a finite-element mesh from station offsets.OccamModel.from_meshmaps mesh cells to inversion parameters.OccamStartup.from_modelwrites the iteration-zero startup vector and inversion controls.
Expected files:
runs/L18PLT_occam2d_native/
occam2d.yml
OccamDataFile.dat
Occam2DMesh
Occam2DModel
Startup
The figures in this tutorial are generated by
docs/scripts/generate_tutorial_occam2d.py. The first figure shows how the
selected TE/TM apparent-resistivity and phase rows are distributed by station.
Inspect the Built Objects#
Before running the solver, inspect the generated data, mesh, model, and startup objects.
1print("sites:", builder.data.n_sites)
2print("frequencies:", builder.data.n_frequencies)
3print("data rows:", builder.data.n_data)
4print("mesh cells:", builder.mesh.n_xcells, "x", builder.mesh.n_zcells)
5print("parameters:", builder.model.n_params)
6print("startup file:", cfg.startup_file)
Example output:
sites: 28
frequencies: 39
data rows: 4368
mesh cells: 42 x 36
parameters: 512
startup file: Startup
The selected period band and mesh skeleton provide a quick visual audit before you send the directory to an Occam2D executable:
If the number of data rows is unexpectedly small, check modes,
freq_min, freq_max, and station filtering. If the parameter count is
very large, increase cell_size_horizontal or reduce n_layers for the
first trial.
Validate File Types#
pyCSAMT can detect native Occam2D file types. Use this to catch path mistakes before sending a run to a cluster or external workstation.
1from pycsamt.models.occam2d.validation import detect_file_type
2
3for filename in (
4 cfg.data_file,
5 cfg.mesh_file,
6 cfg.model_file,
7 cfg.startup_file,
8):
9 path = workdir / filename
10 print(path.name, "->", detect_file_type(path))
Example output:
OccamDataFile.dat -> data
Occam2DMesh -> mesh
Occam2DModel -> model
Startup -> startup
The data file is much larger than the mesh, model, and startup files because it stores every selected station-frequency response row:
This does not prove the geophysics is correct, but it confirms that the native files look like the expected Occam file classes.
Build a TM-Only First Trial#
For many CSAMT and AMT profiles, a TM-only first run is easier to interpret than a joint TE/TM run. Use a separate work directory for the experiment.
1tm_workdir = run_root / "L18PLT_occam2d_tm"
2
3tm_builder = InputBuilder(
4 sites,
5 workdir=tm_workdir,
6 config=OccamConfig(),
7 verbose=1,
8)
9
10tm_builder.build(
11 modes=["TM"],
12 freq_min=0.2,
13 freq_max=500.0,
14 error_floor_rho=0.07,
15 error_floor_phase=1.0,
16 n_layers=28,
17 cell_size=150.0,
18 title="L18PLT TM-only Occam2D first trial",
19)
20
21print(tm_builder.summary())
Example output:
InputBuilder summary
workdir : runs/L18PLT_occam2d_tm
sites : 28
freqs : 35
data pts : 1960
mesh : 42 x 33 cells
params : 448
modes : ['TM']
Keep each experiment in its own directory. Names such as
L18PLT_occam2d_tm and L18PLT_occam2d_te_tm are much easier to audit
than repeatedly overwriting run01.
Run Occam2D#
When a compiled Occam2D executable is available, launch the solver with
pycsamt.models.occam2d.OccamRunner.
1from pycsamt.models.occam2d import OccamRunner
2
3runner = OccamRunner(
4 workdir=workdir,
5 binary_path="/usr/local/bin/Occam2D",
6 startup_file=cfg.startup_file,
7 verbose=1,
8)
9
10exit_code = runner.run(
11 max_iter=80,
12 target_misfit=1.0,
13 auto_compile=False,
14)
15print("Occam2D exit code:", exit_code)
OccamRunner does not build input files. It only runs a prepared directory.
If the executable is not available, stop after the build step and move the
native directory to the environment where Occam2D can run.
Load Finished Results#
After an external run completes, load the directory with
pycsamt.models.occam2d.InversionResult.
1from pycsamt.models.occam2d import InversionResult
2
3result = InversionResult(workdir)
4
5print("iteration files:", len(result.iter_files))
6print("response files:", len(result.resp_files))
7print("final RMS:", result.final_rms)
8print("model grid:", None if result.rho_2d is None else result.rho_2d.shape)
rho_2d stores log10 resistivity on the Occam mesh. Convert to ohm metres
only when you need physical resistivity values:
1import numpy as np
2
3if result.rho_2d is not None:
4 rho_ohm_m = 10.0 ** result.rho_2d
5 print(np.nanmin(rho_ohm_m), np.nanmax(rho_ohm_m))
Plot Model and Misfit#
Use the Occam plotting helpers after a completed run.
1import matplotlib.pyplot as plt
2from pycsamt.models.occam2d import PlotMisfit, PlotModel, PlotPseudo
3
4figure_dir = workdir / "figures"
5figure_dir.mkdir(exist_ok=True)
6
7fig = PlotModel(result).plot()
8fig.savefig(figure_dir / "occam2d_model.png", dpi=200, bbox_inches="tight")
9plt.close(fig)
10
11fig = PlotMisfit(result).plot()
12fig.savefig(figure_dir / "occam2d_misfit.png", dpi=200, bbox_inches="tight")
13plt.close(fig)
14
15fig = PlotPseudo(result).plot()
16fig.savefig(figure_dir / "occam2d_observed_pseudo.png", dpi=200, bbox_inches="tight")
17plt.close(fig)
If a plot helper reports that a response, iteration, or log file is missing, check that the external solver completed and that output files were written in the same directory as the input files.
Backend-Neutral Preparation#
The backend-neutral inversion API can prepare the same native files while
returning a common inversion result object. Keep run_external=False when
you only want to build and validate files.
1from pycsamt.inversion import InversionConfig, run_inversion
2
3inv_cfg = InversionConfig(
4 method="mt",
5 dimension="2d",
6 backend="occam2d",
7 data=sites,
8 workdir=run_root / "L18PLT_occam2d_workflow",
9 run_external=False,
10 error_floor=0.05,
11 phase_error=0.5,
12 backend_options={
13 "config": {
14 "modes": ["TE", "TM"],
15 "freq_min": 0.1,
16 "freq_max": 1000.0,
17 "n_layers": 32,
18 "target_misfit": 1.0,
19 "initial_rho": 100.0,
20 },
21 },
22)
23
24workflow_result = run_inversion(inv_cfg)
25print(workflow_result.status)
26print(workflow_result.files)
27print(workflow_result.warnings)
Use the native InputBuilder path when you want direct control over Occam2D
objects. Use the backend-neutral path when a larger application, agent, or
pipeline needs one interface for several inversion engines.
CLI Equivalent#
Build Occam2D inputs from the command line:
1pycsamt invert build data/AMT/WILLY_DATA/L18PLT \
2 --solver occam2d \
3 --workdir runs/L18PLT_occam2d_cli \
4 --modes TE,TM \
5 --freq 0.1:1000 \
6 --error-floor-rho 0.05 \
7 --error-floor-phase 0.5 \
8 --n-layers 32 \
9 --cell-size 100
Inspect the working directory:
1pycsamt invert status runs/L18PLT_occam2d_cli
2pycsamt invert status runs/L18PLT_occam2d_cli --format json
Run the external solver when the executable is available:
1pycsamt invert run runs/L18PLT_occam2d_cli \
2 --solver occam2d \
3 --max-iter 80 \
4 --target-misfit 1.0
Summarize and plot results:
1pycsamt invert results runs/L18PLT_occam2d_cli
2pycsamt invert plot model runs/L18PLT_occam2d_cli \
3 --save runs/L18PLT_occam2d_cli/model.png \
4 --dpi 200
5pycsamt invert plot misfit runs/L18PLT_occam2d_cli \
6 --save runs/L18PLT_occam2d_cli/misfit.png \
7 --dpi 200
Preparation Checklist#
Before launching a production Occam2D run, confirm that:
EDI files have been loaded and validated;
station order and coordinates are correct;
QC and static-shift decisions are documented;
TE/TM mode choice is intentional;
frequency limits exclude noisy or source-affected bands;
error floors are realistic;
mesh size is reasonable for the station spacing and target depth;
OccamDataFile.dat,Occam2DMesh,Occam2DModel, andStartupare present;the raw, corrected, and inversion-input directories are separate;
the external Occam2D executable can be found in the run environment.
Troubleshooting#
- The data file has too few rows
Check
modes,freq_min,freq_max, and whether the selected stations contain finite impedance values for the requested components.- The mesh is too large
Increase
cell_size_horizontal, reducen_layers, or start with a narrower frequency band for the first trial.- The runner cannot find Occam2D
Pass
binary_pathexplicitly, placeOccam2Din the run directory, or make sure the executable is onPATH.- The RMS does not approach the target
Revisit error floors, static-shift correction, bad stations, mode choice, and whether the structure is too 3-D for a 2-D smooth inversion.
- The final model is too smooth
That is often expected for Occam inversion. Compare residuals, try different error floors, and consider whether another backend is more appropriate for sharp boundaries.
See Also#
- Read an EDI Survey
Load input EDI files.
- Inspect and QC a Survey
Review data quality before inversion.
- Correct Static Shift
Review static-shift correction.
- Occam2D
Full Occam2D backend documentation.
- Choosing A Model Backend
Decide between Occam2D, ModEM, MARE2DEM, and other engines.
- Inversion API
Inversion API reference.
- Inversion Commands
Inversion CLI reference.