ModEM#
pycsamt.models.modem is the ModEM integration layer in pyCSAMT v2. It
does not reimplement the ModEM solver. Instead, it gives Python objects for
preparing ModEM input files, checking native file types, launching an external
ModEM executable, loading completed runs, and plotting the results.
ModEM projects are file-oriented. A reproducible run is normally defined by an observed data file, an initial model file, an inversion-control file, and, for 3-D inversions, a covariance file. The solver then produces iteration models, predicted responses, and logs. pyCSAMT keeps those files explicit so a project can move between Python, command-line ModEM, and archived native run folders without losing provenance.
When To Use ModEM#
Use ModEM when the survey, interpretation target, or legacy project requires a native ModEM workflow.
Good ModEM candidates include:
MT or AMT surveys with stations distributed over an area, not only one line;
geological settings where off-profile structure is expected to affect the response;
projects that need 3-D inversion, covariance masks, or region-specific smoothing;
existing ModEM projects that pyCSAMT should validate, plot, or archive;
forward-response checks against a known ModEM model;
backend-neutral pyCSAMT inversion workflows where the numerical solver is an external ModEM executable.
For simple 2-D CSAMT profile inversions, Occam2D may be more direct. For large 3-D MT or AMT modelling, ModEM is usually the more appropriate native backend.
Dimensionality#
ModEM supports both 2-D and 3-D modes in pyCSAMT. The mode controls the model class, file set, executable name, and whether a covariance file is expected.
Mode |
Main model object |
Native model file |
Typical use |
|---|---|---|---|
|
|
Profile inversion, TE/TM or selected impedance components. |
|
|
|
Area surveys, full impedance tensors, and 3-D covariance control. |
Mode |
Default executable |
Covariance file |
Default builder output |
|---|---|---|---|
|
|
Not created by |
|
|
|
Created from the 3-D model grid |
|
Package Map#
The public ModEM API is grouped around native file roles.
Object or function group |
Role |
|---|---|
Stores data settings, dimensionality, grid controls, covariance controls, inversion-control values, file names, executable names, and MPI settings. |
|
Builds a consistent ModEM input directory from EDI-like station objects
or a populated |
|
Reads, writes, and builds ModEM observed or predicted data files. |
|
Reads, writes, and creates 2-D ModEM resistivity models. |
|
Reads, writes, and creates 3-D ModEM resistivity models. |
|
Represents 3-D smoothing coefficients, region masks, and smoothing exceptions. |
|
Reads and writes the |
|
Assembles and runs external ModEM inversion or forward commands. |
|
Scans a run directory and loads models, responses, covariance, control, and logs. |
|
Parses iteration number, RMS, objective value, model norm, lambda, and alpha from a ModEM log. |
|
|
Matplotlib diagnostics for convergence, model inspection, station responses, and pseudo-sections. |
|
Identify ModEM data, model, covariance, control, and log files before reading or routing them. |
|
Convert older impedance, Mackie, MeshTools, and interpolation-oriented file formats. |
Configuration#
Most workflows start with ModEmConfig. The same
configuration object is passed to the builder, runner, control file, model
factory, covariance factory, and result loader.
1from pycsamt.models.modem import ModEmConfig
2
3cfg = ModEmConfig(
4 mode="3d",
5 component_type="Full_Impedance",
6 error_floor_z=0.05,
7 initial_rho=100.0,
8 nx=24,
9 ny=24,
10 nz=36,
11 cell_size_h=500.0,
12 cell_size_v_top=10.0,
13 depth_scale=1.18,
14 n_padding_xy=8,
15 smooth_x=0.2,
16 smooth_y=0.2,
17 smooth_z=0.1,
18 n_smooth_iter=2,
19 max_iterations=80,
20 target_rms=1.05,
21 binary_3d="Mod3DMT",
22 use_mpi=True,
23 n_procs=16,
24)
25
26cfg.write_template("modem_config.ini")
The template can be edited and loaded again:
1from pycsamt.models.modem import ModEmConfig
2
3cfg = ModEmConfig.from_file("modem_config.ini", strict=True)
4print(cfg.mode, cfg.binary_name)
Important settings are grouped as follows.
Setting group |
Main fields |
|---|---|
Dimensionality |
|
Data block |
|
2-D grid |
|
3-D grid |
|
Regularization |
|
Nonlinear inversion |
|
File names |
|
Execution |
|
Native Files#
A ModEM run folder should be understandable without Python. pyCSAMT therefore writes and reads the same native file roles that the executable uses.
File role |
Python object |
Notes |
|---|---|---|
Observed data |
|
ASCII data blocks grouped by component type. Built from EDI-like station objects or read from existing ModEM files. |
2-D model |
|
|
3-D model |
|
|
Covariance |
|
3-D earth-only smoothing file. It stores smoothing arrays, exceptions, and integer masks. |
Control |
|
|
Log |
|
Parsed convergence history from ModEM text logs. |
Predicted data |
|
Loaded by |
Validate files before routing them into a workflow:
1from pathlib import Path
2
3from pycsamt.models.modem import detect_file_type
4
5for path in Path("runs/modem_3d").iterdir():
6 kind = detect_file_type(path)
7 if kind.value != "unknown":
8 print(path.name, kind.value)
Data Files And Components#
ModEmData stores observed or predicted response
rows. pyCSAMT can read existing ModEM data files or build new ones from
station objects.
When building from EDI-like objects, each station should provide:
a station name;
station coordinates or latitude/longitude/elevation metadata;
positive frequencies;
complex impedance values shaped like
(n_frequency, 2, 2);impedance errors or uncertainty estimates.
The configuration selects which response components are written. Supported
component_type values include:
|
Components written |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1from pycsamt.models.modem import ModEmConfig, ModEmData
2
3cfg = ModEmConfig(
4 mode="3d",
5 component_type="Off_Diagonal_Impedance",
6 error_floor_z=0.05,
7 freq_min=1e-3,
8 freq_max=1e3,
9)
10
11data = ModEmData.from_edi(stations, config=cfg)
12data.write("runs/modem_3d/ModEMData.dat")
13
14print(data.n_sites)
15print(data.n_periods)
16print(data.component_types)
Important
ModEM data files depend on consistent local coordinates. The pyCSAMT writer
stores local x, y, and z coordinates used by the model builder.
Confirm projection, station order, and units before inversion. Do not mix
metres, kilometres, and geographic degrees inside the same run folder.
Build A 3-D Input Set#
InputBuilder creates the standard 3-D input
set: observed data, starting model, covariance, and control file.
1from pathlib import Path
2
3from pycsamt.models.modem import InputBuilder, ModEmConfig
4
5workdir = Path("runs/modem_3d/native")
6cfg = ModEmConfig(
7 mode="3d",
8 component_type="Full_Impedance",
9 initial_rho=100.0,
10 nx=28,
11 ny=28,
12 nz=38,
13 n_airlayers=5,
14 cell_size_h=500.0,
15 cell_size_v_top=10.0,
16 depth_scale=1.18,
17 n_padding_xy=8,
18 smooth_x=0.2,
19 smooth_y=0.2,
20 smooth_z=0.1,
21 n_smooth_iter=2,
22)
23
24builder = InputBuilder(config=cfg)
25files = builder.build(
26 stations,
27 workdir=workdir,
28 data_filename=cfg.data_file,
29 model_filename="m0.ws",
30 cov_filename=cfg.covariance_file,
31 ctrl_filename=cfg.control_file,
32)
33
34for role, path in files.items():
35 print(role, path)
The returned mapping contains data, model, covariance, and
control. The builder also keeps the populated objects on
builder.data, builder.model, builder.covariance, and
builder.control for inspection.
Build A 2-D Input Set#
The 2-D builder path writes data, a 2-D half-space model, and a control file. It does not create a 3-D covariance file.
1from pycsamt.models.modem import InputBuilder, ModEmConfig
2
3cfg = ModEmConfig(
4 mode="2d",
5 component_type="TE_Impedance",
6 initial_rho=100.0,
7 nx_2d=120,
8 nz_2d=60,
9 n_airlayers_2d=5,
10 cell_size_h_2d=100.0,
11 cell_size_v_top_2d=10.0,
12 depth_scale_2d=1.18,
13 n_padding_x_2d=8,
14 max_iterations=60,
15)
16
17files = InputBuilder(config=cfg).build(
18 profile_stations,
19 workdir="runs/modem_2d/native",
20 data_filename="ModEMData.dat",
21 model_filename="m0.rho",
22 ctrl_filename="ModEM.inv",
23)
24
25assert "covariance" not in files
Build From Existing Data#
Use build_from_data when the observed data object has already been read,
filtered, or edited. In this mode the builder creates the model, covariance
when needed, and control file. The caller is responsible for writing the data
file itself.
1from pycsamt.models.modem import InputBuilder, ModEmConfig, ModEmData
2
3cfg = ModEmConfig(mode="3d")
4data = ModEmData.read("survey/ModEMData.dat")
5data.write("runs/modem_3d/native/ModEMData.dat")
6
7files = InputBuilder(config=cfg).build_from_data(
8 data,
9 workdir="runs/modem_3d/native",
10 model_filename="m0.ws",
11 cov_filename="ModEM.cov",
12 ctrl_filename="ModEM.inv",
13)
Models#
The model objects store cell widths and resistivity values. Internally,
resistivity is stored in natural-log units because ModEM solves for log
resistivity. The rho_linear property returns resistivity in linear
ohm m units for plotting and interpretation.
Create and inspect a 3-D starting model:
1from pycsamt.models.modem import ModEmData, ModEmModel3D, ModEmConfig
2
3cfg = ModEmConfig(mode="3d", initial_rho=100.0)
4data = ModEmData.read("runs/modem_3d/native/ModEMData.dat")
5
6model = ModEmModel3D.halfspace(data, config=cfg)
7model.write("runs/modem_3d/native/m0.ws")
8
9print(model.shape)
10print(model.n_air)
11print(model.rho_linear.min(), model.rho_linear.max())
Create and inspect a 2-D starting model:
1from pycsamt.models.modem import ModEmData, ModEmModel2D, ModEmConfig
2
3cfg = ModEmConfig(mode="2d", initial_rho=100.0)
4data = ModEmData.read("runs/modem_2d/native/ModEMData.dat")
5
6model = ModEmModel2D.halfspace(data, config=cfg)
7model.write("runs/modem_2d/native/m0.rho")
8
9print(model.nx, model.nz)
10print(model.x_nodes[-1], model.z_nodes[-1])
The half-space factories are intentionally conservative. They are useful for a first run, but a production inversion usually deserves a deliberate mesh review: station spacing, padding, first-layer thickness, air layers, expected skin depths, and target depth all matter.
Covariance#
The covariance file is central to 3-D ModEM interpretation. It controls the model regularization term through smoothing coefficients and integer masks. pyCSAMT creates a uniform active earth region by default, then lets advanced users edit masks and exceptions before writing the file.
1from pycsamt.models.modem import ModEmCovariance
2
3cov = ModEmCovariance.from_model(model, config=cfg)
4cov.exceptions.append((1, 2, 0.0)) # turn off smoothing across two regions
5cov.write("runs/modem_3d/native/ModEM.cov")
Mask values follow the ModEM convention:
0is reserved for air;9is reserved for ocean;1through8are user-defined earth regions.
ModEmCovariance.from_model excludes air layers from the covariance grid.
The number of covariance layers is therefore model.nz - model.n_air.
Control Files#
ModEmControl stores the nonlinear inversion
settings written to the .inv file.
1from pycsamt.models.modem import ModEmConfig, ModEmControl
2
3cfg = ModEmConfig(
4 output_stem="ModEM_out",
5 initial_lambda=10.0,
6 lambda_divisor=100.0,
7 initial_alpha=10.0,
8 rms_diff_tol=5e-4,
9 target_rms=1.05,
10 lambda_exit=1e-4,
11 max_iterations=100,
12)
13
14control = ModEmControl.from_config(cfg)
15control.write("runs/modem_3d/native/ModEM.inv")
The control file does not replace data-quality assessment. A low target RMS is meaningful only when uncertainty floors, component selection, and bad-period masking are realistic.
Run ModEM#
ModEmRunner assembles the external command and
can execute it with subprocess.run(). The executable is resolved from
PATH or from the run directory and local _source/2D or _source/3D
subdirectories.
Always inspect the command first:
1from pycsamt.models.modem import ModEmConfig, ModEmRunner
2
3cfg = ModEmConfig(
4 mode="3d",
5 binary_3d="Mod3DMT",
6 use_mpi=True,
7 n_procs=16,
8 mpi_command="mpirun",
9)
10
11runner = ModEmRunner("runs/modem_3d/native", config=cfg)
12command = runner.command(
13 "m0.ws",
14 "ModEMData.dat",
15 "ModEM.inv",
16 covariance="ModEM.cov",
17)
18print(command)
Run the inversion only after file paths, executable names, and MPI settings are correct:
1result = runner.run(
2 "m0.ws",
3 "ModEMData.dat",
4 "ModEM.inv",
5 covariance="ModEM.cov",
6 timeout=24 * 3600,
7 load_result=True,
8)
For a forward response check, call run_forward:
1runner.run_forward(
2 "m0.ws",
3 "ModEMData.dat",
4 timeout=3600,
5 load_result=False,
6)
Warning
The runner is a subprocess wrapper around an external executable. It cannot make a physically poor mesh, incorrect component selection, or inconsistent coordinate system valid. Treat the generated command as a reproducibility aid, not as a scientific approval stamp.
Load Results#
InversionResult scans a completed run directory
and loads what it finds: logs, controls, covariance, data files, and iteration
models.
1from pycsamt.models.modem import InversionResult
2
3result = InversionResult("runs/modem_3d/native", config=cfg)
4
5print(result.mode)
6print(result.n_iter)
7print(result.final_rms)
8print(result.best_rms)
9print(result.iteration_numbers)
10
11final_model = result.model_final
12observed = result.data_obs
13predicted = result.data_pred
The result loader recognizes common ModEM output naming patterns, including
numbered Modular_NLCG products. The lowest numbered response can be used
as an observed-data fallback and the highest numbered response can be used as
the predicted response when explicit filenames are not available.
Log Diagnostics#
Use ModEmLog when the convergence history is
the primary diagnostic.
1from pycsamt.models.modem import ModEmLog
2
3log = ModEmLog.read("runs/modem_3d/native/Modular_NLCG.log")
4
5print(log.n_iter)
6print(log.final_rms)
7print(log.best_iter)
8print(log.rms)
9print(log.lagrange)
Review more than the final RMS. Sudden RMS stalls, unstable lambda changes, or a best iteration far earlier than the final iteration can indicate overfitting, inconsistent errors, or a regularization setting that should be revisited.
Plotting#
The ModEM plotters operate on
InversionResult objects and return Matplotlib
figures.
1from pycsamt.models.modem import (
2 InversionResult,
3 PlotMisfit,
4 PlotModel3D,
5 PlotPseudo,
6 PlotResponse,
7)
8
9result = InversionResult("runs/modem_3d/native")
10
11fig = PlotMisfit(result=result).plot()
12fig.savefig("runs/modem_3d/figures/rms.png", dpi=200)
13
14fig = PlotModel3D(
15 result=result,
16 depths=[500, 1000, 2000, 4000],
17 rho_min=1.0,
18 rho_max=1000.0,
19).plot()
20fig.savefig("runs/modem_3d/figures/model_slices.png", dpi=200)
21
22fig = PlotResponse(
23 result=result,
24 stations=["S001", "S002"],
25 max_stations=2,
26).plot()
27fig.savefig("runs/modem_3d/figures/responses.png", dpi=200)
28
29fig = PlotPseudo(result=result, component="ZXY").plot()
30fig.savefig("runs/modem_3d/figures/pseudo_zxy.png", dpi=200)
For 2-D results, use pycsamt.models.modem.PlotModel2D:
1from pycsamt.models.modem import InversionResult, PlotModel2D
2
3result = InversionResult("runs/modem_2d/native")
4fig = PlotModel2D(result=result, depth_max=5000.0).plot()
5fig.savefig("runs/modem_2d/figures/model_section.png", dpi=200)
Conversion And Utility Tools#
The ModEM package also exposes utility functions for existing projects and format conversion.
Utility group |
Examples |
|---|---|
Impedance files |
|
Mackie formats |
|
MeshTools export |
|
Interpolation |
|
Units and transforms |
|
These helpers are most useful when a project arrives with older ModEM, Mackie, or impedance-list files and pyCSAMT is being used as a bridge into a clean v2 run directory.
Backend-Neutral Workflows#
The native objects above are the most explicit way to work with ModEM. pyCSAMT also exposes ModEM through the backend-neutral inversion interface.
1from pycsamt.inversion.config import InversionConfig
2from pycsamt.inversion.backends.modem import ModEMBackend
3
4inv_cfg = InversionConfig(
5 method="mt",
6 dimension="3d",
7 backend="modem",
8 workdir="runs/modem_3d/native",
9 run_external=False,
10 backend_options={
11 "config": {
12 "component_type": "Full_Impedance",
13 "initial_rho": 100.0,
14 "binary_3d": "Mod3DMT",
15 "use_mpi": True,
16 "n_procs": 16,
17 },
18 "files": {
19 "data": "ModEMData.dat",
20 "model": "m0.ws",
21 "control": "ModEM.inv",
22 "covariance": "ModEM.cov",
23 },
24 "runner": {
25 "timeout": 24 * 3600,
26 "load_result": True,
27 },
28 },
29)
30
31result = ModEMBackend(inv_cfg).run()
32print(result.status)
33print(result.metadata["command"])
With run_external=False, the backend prepares or validates the run folder
and reports the command that would be executed. Set run_external=True only
when the external ModEM executable is installed and the run directory has been
reviewed.
Recommended Run Layout#
A stable ModEM project layout separates native inputs, figures, and notes:
1runs/
2 modem_3d/
3 README.md
4 config/
5 modem_config.ini
6 native/
7 ModEMData.dat
8 m0.ws
9 ModEM.cov
10 ModEM.inv
11 Modular_NLCG.log
12 Modular_NLCG_001.rho
13 Modular_NLCG_001.dat
14 figures/
15 rms.png
16 model_slices.png
17 responses.png
18 exports/
19 final_model.vtk
Keep hand-edited files under version control when possible. Large model outputs and response grids can be archived separately if repository size is a concern.
Pre-Run Checklist#
Before starting a ModEM inversion, verify:
station coordinates use one local coordinate system and metre units;
periods and frequencies are in the intended range;
impedance sign convention and units match the executable expectation;
component selection is appropriate for the survey dimensionality;
error floors are realistic and bad periods have been removed or masked;
horizontal cell size reflects station spacing and target resolution;
vertical first-layer thickness and depth growth resolve shallow structure;
padding extends far enough from the station footprint;
air layers and ocean or inactive masks are correct;
covariance smoothing values and exceptions are scientifically justified;
the dry-run command points to the intended executable and files;
MPI process count matches the machine and executable build.
Post-Run Checklist#
After a run finishes, review:
RMS history and best iteration, not only final RMS;
response fits by station, period, and component;
residual concentration around specific stations or period bands;
model updates near boundaries, air layers, and padding cells;
sensitivity of interpretation to error floors and covariance settings;
consistency between final model features and known geology;
reproducibility of the run directory, configuration, command, and code version.
Common Mistakes#
The command is ready but the executable cannot be found.Check
binary_2dorbinary_3dinModEmConfig. The runner searchesPATH, the working directory, and local_sourcefolders.The 3-D run is missing a covariance file.Build the input set with
mode="3d"or createModEmCovariancefrom the 3-D model and pass it to the runner.The model loads but plotted station positions look wrong.Recheck coordinate origin, projection, and units before trusting any response or model diagnostics.
The RMS is low but the model has unrealistic detail.Inspect uncertainty floors, removed periods, smoothing values, covariance masks, and response residuals. A visually detailed model is not necessarily better constrained.
The result loader did not find a predicted response.Confirm the output stem in the control file and inspect the run directory for numbered
Modular_NLCGresponse files.
Next Steps#
Choosing A Model Backend explains when ModEM is preferable to other model backends.
Configuration And File I/O gives the shared model-backend configuration and file-layout policy.
Occam2D documents the 2-D Occam-style alternative.
Inversion Concepts introduces misfit, regularization, and inversion diagnostics.
pycsamt.models links to generated API pages for the ModEM objects.