Forward Configuration#

Forward modelling should be reproducible. A synthetic response, training dataset, or grid experiment is only useful when another user can reconstruct the solver, model prior, sampled axis, station layout, noise model, and random seed.

The forward package provides three configuration dataclasses:

All three classes follow the same workflow:

  1. write an annotated template;

  2. edit the template as the project source of truth;

  3. load the template back into Python;

  4. validate before computation;

  5. build arrays, grids, solvers, or dataset keyword arguments from the config;

  6. archive the config next to datasets, figures, and interpretation notes.

Configuration Classes#

Class

Main purpose

Main helpers

ForwardConfig

1-D MT/CSAMT/TEM synthetic dataset generation.

freq_grid(), time_grid(), to_dataset_kwargs(), write_template(), from_file(), summary().

ForwardConfig2D

2-D MT finite-difference grid and solver setup.

freq_grid(), to_grid(), to_solver_kwargs(), write_template(), from_file(), summary().

ForwardConfig3D

Quasi-3-D MT grid and solver setup.

freq_grid(), to_grid(), to_solver_kwargs(), write_template(), from_file(), summary().

Template Files#

Forward configuration files can be written as Python, JSON, YML, or YAML. The file extension selects the format automatically. Templates include parameter comments, which makes them good review artifacts for scientific projects.

1from pycsamt.forward import ForwardConfig, ForwardConfig2D, ForwardConfig3D
2
3ForwardConfig.write_template("configs/forward_1d.yml")
4ForwardConfig2D.write_template("configs/forward_2d.yml")
5ForwardConfig3D.write_template("configs/forward_3d.yml")

Load the edited files with strict validation of keys:

1from pycsamt.forward import ForwardConfig
2
3cfg = ForwardConfig.from_file("configs/forward_1d.yml", strict=True)
4cfg.validate()
5print(cfg.summary())

strict=True is recommended for production runs because misspelled keys raise an error instead of being ignored. Use strict=False only when loading a file that intentionally contains extra metadata.

1-D Configuration#

ForwardConfig controls the 1-D solver, frequency or time grid, random layered model prior, noise model, output path, and parallel worker count.

Setting group

Fields

Solver

solver, freq_min, freq_max, n_freqs, time_min, time_max, n_times, loop_radius.

Earth model prior

n_layers_min, n_layers_max, rho_min, rho_max, depth_max, geology.

Dataset

n_samples, noise_level, noise_type, include_phase, seed, n_jobs.

Output

output_dir, output_name, verbose.

The solver controls which sampled axis is active:

solver

Active axis

Main settings

"mt1d"

Frequency

freq_min, freq_max, n_freqs, include_phase.

"csamt1d"

Frequency

freq_min, freq_max, n_freqs, include_phase.

"tem1d"

Time gates

time_min, time_max, n_times, loop_radius.

Generate a 1-D MT training dataset:

 1from pycsamt.forward import ForwardConfig, generate_dataset
 2
 3cfg = ForwardConfig(
 4    solver="mt1d",
 5    freq_min=1e-3,
 6    freq_max=1e4,
 7    n_freqs=40,
 8    n_layers_min=3,
 9    n_layers_max=7,
10    rho_min=1.0,
11    rho_max=10_000.0,
12    depth_max=3000.0,
13    n_samples=5000,
14    noise_level=0.05,
15    noise_type="field",
16    include_phase=True,
17    seed=42,
18    n_jobs=1,
19    output_dir="runs/forward",
20    output_name="mt1d_training",
21)
22
23cfg.validate()
24dataset = generate_dataset(**cfg.to_dataset_kwargs())

to_dataset_kwargs() builds the correct frequency or time array and constructs the output path. This reduces the risk of the config and the actual dataset generation call drifting apart.

Geological Priors#

ForwardConfig.geology can replace broad rho_min/rho_max and depth_max sampling with a named geological prior. This is useful when an AI dataset should represent a known target class instead of arbitrary layered-earth variation.

Common prior names include:

1sedimentary
2crystalline
3geothermal
4marine
5permafrost
 1from pycsamt.forward import ForwardConfig, generate_dataset
 2
 3cfg = ForwardConfig(
 4    solver="mt1d",
 5    geology="geothermal",
 6    n_layers_min=4,
 7    n_layers_max=7,
 8    n_samples=20_000,
 9    noise_type="field",
10    seed=12,
11    output_dir="runs/forward",
12    output_name="geothermal_mt1d",
13)
14
15cfg.validate()
16dataset = generate_dataset(**cfg.to_dataset_kwargs())

When geology is set, the geological prior should be documented in the project notes. A neural network trained on a narrow prior may not generalize outside that geological setting.

TEM Configuration#

TEM uses time gates rather than frequency samples.

 1from pycsamt.forward import ForwardConfig, generate_dataset
 2
 3cfg = ForwardConfig(
 4    solver="tem1d",
 5    time_min=1e-6,
 6    time_max=1e-2,
 7    n_times=25,
 8    loop_radius=50.0,
 9    n_layers_min=3,
10    n_layers_max=6,
11    n_samples=1000,
12    noise_type="gaussian",
13    noise_level=0.03,
14    seed=5,
15    output_dir="runs/forward",
16    output_name="tem1d_training",
17)
18
19cfg.validate()
20dataset = generate_dataset(**cfg.to_dataset_kwargs())

TEM generation can be slower than MT1D because the current implementation uses numerical integration for the step-off response. Start with a small n_samples value, inspect the response, then scale up.

2-D Configuration#

ForwardConfig2D creates a pycsamt.forward.Grid2D and solver keyword arguments for pycsamt.forward.MT2DForward.

Setting group

Fields

Solver

freq_min, freq_max, n_freqs.

Grid

nx, nz, x_max, z_max, n_pad, pad_factor.

Earth model

bg_rho, model_type, anomaly_rho, anomaly_x_lo, anomaly_x_hi, anomaly_z_lo, anomaly_z_hi.

Stations

n_stations, station_x_max.

Output

verbose.

The supported model_type values are:

model_type

Meaning

"halfspace"

Uniform background resistivity.

"anomaly"

Rectangular anomaly inside a background.

"random"

Random 2-D grid model generated by Grid2D.random.

Example 2-D anomaly run:

 1from pycsamt.forward import ForwardConfig2D, MT2DForward
 2
 3cfg = ForwardConfig2D(
 4    freq_min=1e-2,
 5    freq_max=1e3,
 6    n_freqs=25,
 7    nx=50,
 8    nz=35,
 9    x_max=10_000.0,
10    z_max=6000.0,
11    n_pad=8,
12    pad_factor=1.3,
13    bg_rho=300.0,
14    model_type="anomaly",
15    anomaly_rho=10.0,
16    anomaly_x_lo=2500.0,
17    anomaly_x_hi=6500.0,
18    anomaly_z_lo=400.0,
19    anomaly_z_hi=1800.0,
20    n_stations=16,
21    verbose=True,
22)
23
24cfg.validate()
25print(cfg.summary())
26
27grid = cfg.to_grid()
28solver = MT2DForward(grid=grid, **cfg.to_solver_kwargs())
29response = solver.run()

to_grid() accepts a seed argument for random models:

1cfg = ForwardConfig2D(model_type="random")
2cfg.validate()
3grid = cfg.to_grid(seed=42)

2-D Grid Tuning#

2-D forward runs are sensitive to grid design.

Parameter

Practical guidance

x_max

Should include all stations and enough side room that boundaries do not dominate the response.

z_max

Should extend below the expected maximum investigation depth.

nx and nz

Increase when anomalies are small or response gradients are sharp.

n_pad

Add side and bottom padding to reduce boundary artifacts.

pad_factor

Values around 1.2 to 1.5 are typical; 1.3 is a stable starting point.

n_stations

Should resolve the lateral scale of the target.

3-D Configuration#

ForwardConfig3D builds a pycsamt.forward.Grid3D and keyword arguments for pycsamt.forward.MT3DForward.

Setting group

Fields

Solver

freq_min, freq_max, n_freqs, method.

Grid

nx, ny, nz, x_max, y_max, z_max, n_pad, pad_factor.

Earth model

bg_rho, model_type, anomaly bounds, n_layers, lateral_variation, corr_length.

Stations

nx_stations, ny_stations.

Output

verbose.

The supported model_type values are:

model_type

Meaning

"halfspace"

Uniform 3-D background.

"block_anomaly"

Rectangular 3-D anomaly inside a background.

"random_layered"

Random horizontal layers with optional Gaussian-random-field lateral variation.

Example quasi-3-D block anomaly:

 1from pycsamt.forward import ForwardConfig3D, MT3DForward
 2
 3cfg = ForwardConfig3D(
 4    freq_min=1e-2,
 5    freq_max=1e3,
 6    n_freqs=15,
 7    method="quasi3d",
 8    nx=24,
 9    ny=24,
10    nz=18,
11    x_max=9000.0,
12    y_max=9000.0,
13    z_max=5000.0,
14    n_pad=8,
15    bg_rho=500.0,
16    model_type="block_anomaly",
17    anomaly_rho=20.0,
18    anomaly_x_lo=2500.0,
19    anomaly_x_hi=6500.0,
20    anomaly_y_lo=2500.0,
21    anomaly_y_hi=6500.0,
22    anomaly_z_lo=500.0,
23    anomaly_z_hi=2000.0,
24    nx_stations=6,
25    ny_stations=6,
26    verbose=True,
27)
28
29cfg.validate()
30print(cfg.summary())
31
32grid = cfg.to_grid()
33response = MT3DForward(grid=grid, **cfg.to_solver_kwargs()).run()

Random layered 3-D model:

 1cfg = ForwardConfig3D(
 2    model_type="random_layered",
 3    n_layers=5,
 4    lateral_variation=True,
 5    corr_length=2500.0,
 6    nx_stations=7,
 7    ny_stations=7,
 8)
 9
10cfg.validate()
11grid = cfg.to_grid(seed=42)

3-D Configuration Notes#

ForwardConfig3D.method currently validates to "quasi3d". The underlying solver code contains experimental hooks for fuller 3-D methods, but the documented configuration path is quasi-3-D. Treat quasi-3-D outputs as survey-scale synthetic responses, not as final production 3-D inversion results.

For production 3-D modelling or inversion, continue to:

Validation#

Call validate() before any expensive run. Validation catches basic range errors such as negative frequencies, invalid model types, impossible anomaly bounds, and invalid station counts.

1from pycsamt.forward import ForwardConfig2D
2
3cfg = ForwardConfig2D(freq_min=100.0, freq_max=10.0)
4
5try:
6    cfg.validate()
7except ValueError as exc:
8    print(f"Configuration problem: {exc}")

Validation does not prove that the model is geologically meaningful. It only checks that the parameters are internally acceptable for the builder and solver.

Summaries And Provenance#

Each config object provides summary() and repr output that can be saved in logs or reports.

1from pathlib import Path
2
3cfg = ForwardConfig2D(model_type="anomaly")
4cfg.validate()
5
6run_dir = Path("runs/forward/2d_anomaly")
7run_dir.mkdir(parents=True, exist_ok=True)
8(run_dir / "summary.txt").write_text(cfg.summary())
9cfg.to_template(run_dir / "forward_config_2d.yml")

What To Record#

For reproducibility, record:

  • config file path and format;

  • solver type and dimensionality;

  • frequency grid or time-gate grid;

  • model prior or grid constructor;

  • resistivity bounds, anomaly bounds, or geology prior;

  • station layout;

  • noise type and noise level;

  • random seed;

  • output dataset or figure paths;

  • pyCSAMT version or commit;

  • any manual edits made after grid creation.

Common Mistakes#

The generated dataset cannot be reproduced.

Record seed, configuration file, pyCSAMT version, and output path. Avoid manually changing keyword arguments outside the config.

The run validates but the response is not useful.

Validation only checks parameter ranges. Review frequency range, grid extents, anomaly size, station spacing, and plots.

TEM generation is unexpectedly slow.

Start with fewer samples or fewer time gates. TEM1D currently uses numerical integration for the step-off response.

The 2-D model has boundary artifacts.

Increase x_max, z_max, n_pad, or adjust pad_factor.

The 3-D response is treated as a production inversion result.

ForwardConfig3D documents quasi-3-D synthetic modelling. Use ModEM or MARE2DEM for production external-engine workflows.

Next Steps#