Occam2D#
pycsamt.models.occam2d provides the pyCSAMT interface to an
Occam2DMT-style smooth 2-D inversion workflow. It prepares native input files,
can run the external Fortran executable, loads iteration and response outputs,
and provides plotting helpers for models, responses, pseudosections, misfit
curves, station-level residuals, and 1-D station extracts.
Occam inversion is deliberately conservative. It seeks the smoothest model that fits the data to an acceptable normalized RMS misfit. For a residual vector \(r\), the usual RMS diagnostic is
Occam iteration files store model parameters as log10 resistivity,
\(m = \log_{10}(\rho)\). pyCSAMT preserves that convention in
InversionResult.rho_2d and converts to physical resistivity only when a
plot or downstream calculation needs it.
When To Use Occam2D#
Occam2D is a good first production engine when:
stations follow a profile and can be represented by 2-D geometry;
the dominant resistivity structure is expected to be approximately 2-D;
a smooth model is scientifically appropriate;
TE/TM apparent resistivity and phase are the primary inversion data;
the deliverable must include native Occam data, mesh, model, startup, iteration, response, and log files;
the interpreter wants a reproducible external-code workflow rather than only an in-memory inversion result.
Occam2D is not a blocky geology engine. A sharp geological contact may appear as a smooth gradient because the regularization intentionally spreads structure unless the data require a sharper transition.
Package Map#
The Occam2D package is organized around the native inversion lifecycle.
Area |
Main objects |
Purpose |
|---|---|---|
Configuration |
|
Stores data selection, error floors, mesh options, startup controls, file names, and binary discovery name. |
Input construction |
|
Builds data, mesh, model, and startup files from a survey source. |
Native data |
|
Reads/writes Occam data files and builds data rows from EDI/Sites-like sources. |
Mesh and model |
|
Build/read finite-element mesh geometry and the model-parameter mapping. |
Startup and iterations |
|
Represent the iteration-zero startup file and non-zero iteration output files. |
Execution |
|
Finds or compiles the executable, patches startup controls when requested, launches the solver, and captures stdout/stderr logs. |
Results |
|
Scans a completed run, loads mesh/model/data/log/iteration/response
files, reconstructs |
Diagnostics |
|
Parse response residuals and convergence history. |
Plotting |
|
Visual QC and interpretation views. |
Validation |
|
Recognize data, mesh, model, startup, iteration, response, and log files. |
Configuration#
OccamConfig is the source-of-truth object for a native Occam2D run. It can
be created in Python, written to a template, edited, and loaded again.
1from pycsamt.models.occam2d import OccamConfig
2
3cfg = OccamConfig(
4 modes=["TE", "TM"],
5 error_floor_rho=0.05,
6 error_floor_phase=0.5,
7 freq_min=0.1,
8 freq_max=1000.0,
9 n_layers=36,
10 n_airlayers=5,
11 cell_size_horizontal=75.0,
12 cell_size_vertical_top=10.0,
13 depth_scale=1.18,
14 n_padding_x=8,
15 max_iterations=80,
16 target_misfit=1.0,
17 initial_rho=100.0,
18 data_file="OccamDataFile.dat",
19 mesh_file="Occam2DMesh",
20 model_file="Occam2DModel",
21 startup_file="Startup",
22 binary_name="Occam2D",
23)
24
25cfg.to_template("runs/profile_a/occam2d.yml")
26loaded = OccamConfig.from_file("runs/profile_a/occam2d.yml")
The configuration groups four concerns.
Concern |
Fields |
Meaning |
|---|---|---|
Data selection |
|
Which data rows are written and how minimum uncertainties are enforced. |
Mesh geometry |
|
Finite-element discretization near stations, at depth, and near lateral boundaries. |
Startup controls |
|
Values written to the startup control file. |
Files and binary |
|
Native file names inside the run directory and executable name used by the runner. |
Use strict loading for project work. Unknown keys usually mean spelling mistakes or stale configuration files.
1from pycsamt.models.occam2d import OccamConfig
2
3cfg = OccamConfig.from_file("runs/profile_a/occam2d.yml")
4
5# Migration only: ignore retired or unknown keys while cleaning old files.
6migrated = OccamConfig.from_file(
7 "runs/profile_a/old_occam2d.yml",
8 strict=False,
9)
Native Files#
Occam2D projects should be archived as native file sets. The final image alone is not enough to reproduce the inversion.
File |
Object |
Role |
|---|---|---|
|
|
Observed data rows, station names, offsets, frequencies, Occam type codes, datum values, and uncertainties. |
|
|
Finite-element mesh geometry, including air layers, earth layers, horizontal cells, and padding cells. |
|
|
Mapping from mesh cells to inversion parameters. |
|
|
Iteration-zero control file passed to the executable: data/model/mesh names, target misfit, iteration controls, starting parameters. |
|
|
Non-zero iteration output files containing log10-resistivity parameter values and iteration diagnostics. |
|
|
Modeled responses and residual information for an iteration. |
|
|
Convergence history and run-level diagnostic messages. |
|
|
Captured process streams from pyCSAMT-launched runs. |
The validation helpers can classify files when scanning a run directory.
1from pycsamt.models.occam2d.validation import detect_file_type
2
3for path in [
4 "runs/profile_a/native/OccamDataFile.dat",
5 "runs/profile_a/native/Occam2DMesh",
6 "runs/profile_a/native/Startup",
7]:
8 print(path, detect_file_type(path))
Build Input Files#
InputBuilder constructs the four files required before an external Occam2D
run can start.
1from pycsamt.models.occam2d import InputBuilder, OccamConfig
2from pycsamt.site import Sites
3
4sites = Sites.from_dir("data/edi/profile_a")
5
6cfg = OccamConfig(
7 modes=["TE", "TM"],
8 freq_min=0.1,
9 freq_max=1000.0,
10 error_floor_rho=0.05,
11 error_floor_phase=0.5,
12 n_layers=32,
13 target_misfit=1.0,
14)
15
16builder = InputBuilder(
17 sites,
18 workdir="runs/profile_a/native",
19 config=cfg,
20 verbose=1,
21)
22builder.build(title="Profile A Occam2D inversion")
23
24print(builder.summary())
The build chain is fixed:
OccamData.from_ediconverts the survey source into Occam data rows.OccamMesh.from_databuilds a mesh from station offsets and mesh options.OccamModel.from_meshmaps mesh cells to inversion parameters.OccamStartup.from_modelwrites the initial parameter vector and run controls.
One-shot overrides passed to build update the stored configuration before
files are written.
1builder.build(
2 modes=["TM"],
3 n_layers=40,
4 cell_size=50.0,
5 error_floor_rho=0.07,
6 freq_min=0.2,
7 freq_max=500.0,
8 title="Profile A TM-only sensitivity run",
9)
Because these overrides persist on builder.config, write the resulting
configuration to the run directory if the build is retained.
Data Rows And Type Codes#
Occam2D data files written by pyCSAMT use TE/TM apparent resistivity and phase rows. In the configuration:
"TE"selects the \(Z_{xy}\) component;"TM"selects the \(Z_{yx}\) component;apparent resistivity is stored as \(\log_{10}(\rho_a)\);
phase is stored in degrees;
error_floor_rhois relative, for example0.05for five percent;error_floor_phaseis absolute, in degrees.
Inspect the data object before running.
1from pycsamt.models.occam2d import OccamData
2
3data = OccamData.read("runs/profile_a/native/OccamDataFile.dat")
4
5print(data.n_sites)
6print(data.n_frequencies)
7print(data.n_data)
8print(data.site_names)
9print(data.offsets)
Station order and offsets matter because the mesh and pseudosections are built around that profile geometry.
Mesh And Model Review#
The mesh and model determine what kind of smoothness the inversion can express. Before launching a long external run, inspect:
horizontal cell width near stations;
number of padding cells on each side;
top cell thickness and depth growth factor;
number of air layers and earth layers;
total number of model parameters;
whether the mesh is far wider and deeper than the interpreted target.
1from pycsamt.models.occam2d import OccamMesh, OccamModel
2
3mesh = OccamMesh.read("runs/profile_a/native/Occam2DMesh")
4model = OccamModel.read("runs/profile_a/native/Occam2DModel")
5
6print(mesh.n_xcells, mesh.n_zcells)
7print(model.n_params)
Fine cells can improve near-surface representation, but they also increase runtime and may exaggerate the apparent resolution of poorly constrained structure. Padding moves boundaries away from the profile but also increases mesh size.
Run Occam2D#
OccamRunner executes a prepared native directory. It does not build input
files; use InputBuilder first when starting from EDI data.
1from pycsamt.models.occam2d import OccamRunner
2
3runner = OccamRunner(
4 workdir="runs/profile_a/native",
5 binary_path="/usr/local/bin/Occam2D",
6 startup_file="Startup",
7 verbose=1,
8)
9
10runner.discover_binary(auto_compile=False)
11
12# Run only when the executable and native files are ready.
13# exit_code = runner.run(max_iter=80, target_misfit=1.0)
Binary discovery follows this order:
explicit
binary_path;Occam2DorOccam2D.exein the run directory;executable on
PATH;bundled
_sourcedirectory, ifauto_compile=True.
Automatic compilation uses the bundled Fortran source and a compiler such as
gfortran through make. For reproducible production work, prefer an
explicit binary path and record compiler provenance separately.
run can patch the startup file in place when max_iter or
target_misfit is supplied. Archive the startup file that was actually run,
not only the template that created it.
Asynchronous execution is available for scripts that need to poll an external process.
1runner = OccamRunner("runs/profile_a/native")
2
3# process = runner.run_async(auto_compile=False)
4# while runner.is_running:
5# ...
6# exit_code = runner.wait()
For HPC usage, build and validate the native directory locally, then submit the
equivalent Occam2D Startup command through the scheduler. Load the completed
directory afterward with InversionResult.
Backend-Neutral Occam2D Runs#
The backend-neutral inversion API can drive the same native workflow through
backend="occam2d".
1from pycsamt.inversion import InversionConfig, run_inversion
2
3cfg = InversionConfig(
4 method="mt",
5 dimension="2d",
6 backend="occam2d",
7 data="data/edi/profile_a",
8 workdir="runs/profile_a",
9 run_external=False,
10 backend_options={
11 "occam_config": {
12 "modes": ["TE", "TM"],
13 "n_layers": 32,
14 "target_misfit": 1.0,
15 },
16 },
17)
18
19result = run_inversion(cfg)
With run_external=False, pyCSAMT prepares or validates the native directory
without requiring the external binary to run. This is useful for documentation,
cluster workflows, and dry-run checks.
Load Results#
InversionResult scans a completed run directory. If iteration is not
specified, it loads the highest numbered .iter file. It tries to match the
corresponding .resp file and reconstructs a log10-resistivity grid from the
mesh, model, and iteration vector.
1from pycsamt.models.occam2d import InversionResult
2
3result = InversionResult("runs/profile_a/native")
4
5print(result.summary())
6print(result.final_rms)
7print(result.n_iterations)
8print(result.rho_2d.shape if result.rho_2d is not None else None)
9
10selected = InversionResult("runs/profile_a/native", iteration=12)
11selected.iter2dat("runs/profile_a/exports/profile_a_iter12.dat")
The loader is tolerant of missing optional files. Missing logs or response
files leave the corresponding attributes as None. A missing run directory
raises NotADirectoryError.
Response And Misfit Diagnostics#
OccamResponse reads modeled responses and residuals. Use it to understand
which sites, frequencies, or components are controlling the misfit.
1from pycsamt.models.occam2d import OccamResponse
2
3response = OccamResponse.read("runs/profile_a/native/RESP12.resp")
4
5print(response.rms)
6print(response.misfit_per_site())
7print(response.misfit_per_frequency())
Weighted residuals depend on the error column in the Occam data file. If error floors are too small, the inversion may chase noise. If they are too large, the model may stop before fitting reliable signal.
Log And Convergence#
OccamLog parses convergence history from Occam log files. Use the log
together with the selected iteration file; do not judge a run only by the final
model image.
1from pycsamt.models.occam2d import OccamLog
2
3log = OccamLog.read("runs/profile_a/native/LogFile")
4
5print(log.converged)
6print(log.rms[-1] if log.rms.size else None)
7print(log.n_iter)
Review:
starting RMS and final RMS;
whether the run reached target misfit;
whether roughness changes stabilize;
whether the best-looking model corresponds to a sensible iteration;
whether response residuals improve where the data are trustworthy.
Plotting And QC#
The plotting helpers are designed to replace common Occam2DMT MATLAB post-processing views.
Plot helper |
Use |
|---|---|
|
Plot the reconstructed 2-D resistivity section. |
|
Compare observed and modeled responses. |
|
Plot observed-data pseudosections. |
|
Plot RMS/convergence metrics by iteration. |
|
Extract station-centered 1-D profiles from the 2-D model. |
|
Plot per-site residual diagnostics. |
|
Inspect response behavior across sites/frequencies. |
|
Review station-level 1-D fit style diagnostics. |
Example:
1from pycsamt.models.occam2d import InversionResult
2
3result = InversionResult("runs/profile_a/native")
4
5result.plot_misfit()
6result.plot_pseudo(mode="TE", data_type="rho")
7result.plot_response(site=0)
8result.plot_model()
Before interpretation, compare the section against residual plots. A coherent anomaly with poor response fit is not reliable geological evidence.
Recommended Run Layout#
Keep one run directory per scientific experiment.
1runs/
2 profile_a_occam2d_v01/
3 occam2d.yml
4 provenance.yml
5 native/
6 OccamDataFile.dat
7 Occam2DMesh
8 Occam2DModel
9 Startup
10 ITER12.iter
11 RESP12.resp
12 LogFile
13 occam_stdout.log
14 occam_stderr.log
15 qc/
16 pseudosection.png
17 convergence.png
18 response_fit_site_014.png
19 section_iter12.png
20 exports/
21 profile_a_iter12.dat
22 run_snapshot.zip
Avoid launching new experiments in an old output directory. Occam-style native
files often have simple names, and stale .iter or .resp files can be
mistaken for fresh output.
Pre-Run Checklist#
Before launching:
load
OccamConfigfrom the edited template;confirm station order and profile offsets;
inspect selected modes and frequency band;
confirm apparent-resistivity and phase error floors;
inspect mesh dimensions, padding, and depth growth;
inspect model parameter count;
confirm startup file references the intended data, mesh, and model files;
confirm the executable path and compiler provenance;
move old iteration, response, and log files out of the native directory;
record the exact command and runtime environment.
Post-Run Checklist#
After completion:
read stdout/stderr logs if pyCSAMT launched the run;
load the run with
InversionResult;confirm the selected iteration number and final RMS;
inspect convergence history;
inspect per-site and per-frequency misfit;
compare observed and modeled responses;
plot the pseudosection and final model together;
export
iter2datonly after confirming the selected iteration;archive the native input and output files with the configuration.
Common Mistakes#
- Interpreting smooth gradients too literally
Occam regularization intentionally smooths structure. A gradient may represent a sharper geological contact that is not resolved by the data.
- Ignoring station geometry
The data, mesh, pseudosection, and model section all depend on station ordering and offsets. Check them before running.
- Using unrealistic error floors
Too-small floors can force the inversion to fit noise; too-large floors can underfit useful signal.
- Mixing old and new output files
If a run fails, old
.iteror.respfiles can remain. Check timestamps and log files before loading.- Forgetting startup patching
Passing
max_iterortarget_misfittoOccamRunner.runmodifiesStartupin place. Archive the modified file.
Next Steps#
Configuration And File I/O for source-of-truth configuration and native file archive practice.
Choosing A Model Backend for deciding when Occam2D is the right model integration.
Prepare an Occam2D Inversion for a practical Occam2D workflow.
Inversion Concepts for Occam-style objective functions and regularization.
pycsamt.models for generated API details.