pycsamt.ai.inversion.duhi2d#

Dual-Uncertainty Hybrid Inversion preparation for Occam2D.

This module connects two uncertainty descriptions to solver-native Occam2D inputs:

  • observation reliability modifies each datum error in data space;

  • AI predictive uncertainty modifies model prejudice in parameter space.

For datum error \(\sigma_i\) and reliability \(c_i\), the effective error is

\[\sigma_{i,\mathrm{eff}} = \frac{\sigma_i} {\sqrt{\max(c_i, c_{\min})}}.\]

For AI parameter mean \(\mu_j\), standard deviation \(\sigma_j\), uncertainty floor \(\sigma_0\), and AI weight \(\lambda_{\mathrm{AI}}\), the Occam prejudice amplitude is

\[w_j = \frac{\lambda_{\mathrm{AI}}} {\sqrt{\sigma_j^2 + \sigma_0^2}}.\]

The DUHIInverter2D object prepares these inputs but does not execute the external solver. Execution remains the responsibility of pycsamt.models.occam2d.OccamRunner or the common Occam2D inversion backend.

Entry points#

apply_observation_reliability(errors, reliability)

Convert nominal errors to reliability-weighted effective errors.

map_ai_grid_to_occam(grid, model, mesh)

Map a coordinated 2-D AI grid to Occam parameter order.

DUHIInverter2D.prepare(builder, ...)

Write a completed Occam2D project with both DUHI branches.

Functions

apply_observation_reliability(errors, ...[, ...])

Return reliability-weighted effective datum errors.

Classes

DUHIInverter2D(*[, lambda_ai, ...])

Prepare an Occam2D project for DUHI physics refinement.

DUHIPreparation(workdir, prejudice_file, ...)

Summarize one completed DUHI Occam2D preparation.

class pycsamt.ai.inversion.duhi2d.DUHIPreparation(workdir, prejudice_file, data_file, model_file, startup_file, n_data, n_params, effective_error_min, effective_error_max, prejudice_weight_min, prejudice_weight_max, ai_initialized)[source]

Bases: PyCSAMTObject

Summarize one completed DUHI Occam2D preparation.

Parameters:
  • workdir (pathlib.Path) – Occam2D project directory modified by the preparation.

  • prejudice_file (pathlib.Path) – Written sparse prejudice file.

  • data_file (pathlib.Path) – Rewritten Occam data file containing effective errors.

  • model_file (pathlib.Path) – Rewritten model file referencing prejudice_file.

  • startup_file (pathlib.Path) – Rewritten startup file, optionally containing the AI mean.

  • n_data (int) – Number of Occam data rows weighted by reliability.

  • n_params (int) – Number of Occam model parameters receiving mapped AI values.

  • effective_error_min (float) – Minimum effective error after reliability weighting.

  • effective_error_max (float) – Maximum effective error after reliability weighting.

  • prejudice_weight_min (float) – Minimum mapped AI prejudice weight.

  • prejudice_weight_max (float) – Maximum mapped AI prejudice weight.

  • ai_initialized (bool) – Whether the AI mean replaced the startup parameter vector.

Variables:

files (dict of str to pathlib.Path) – Mapping of solver input roles to generated paths.

Examples

A preparation result is returned by DUHIInverter2D.prepare():

result = inverter.prepare(
    builder,
    ai_mean=mean,
    ai_std=std,
    observation_reliability=reliability,
)
result.files["prejudice"]
workdir: Path
prejudice_file: Path
data_file: Path
model_file: Path
startup_file: Path
n_data: int
n_params: int
effective_error_min: float
effective_error_max: float
prejudice_weight_min: float
prejudice_weight_max: float
ai_initialized: bool
property files: dict[str, Path][source]

Return generated solver-input paths by role.

Returns:

Keys are "data", "model", "startup", and "prejudice".

Return type:

dict of str to pathlib.Path

Examples

>>> sorted(result.files)
['data', 'model', 'prejudice', 'startup']
class pycsamt.ai.inversion.duhi2d.DUHIInverter2D(*, lambda_ai=1.0, sigma_ai_floor=0.05, reliability_floor=1e-06, prejudice_filename='DUHIPrejudice', grid_mapper=None, verbose=0)[source]

Bases: PyCSAMTObject

Prepare an Occam2D project for DUHI physics refinement.

DUHIInverter2D converts observation reliability and ensemble AI uncertainty into solver-native Occam2D inputs. The object is a preparation-stage inversion component: it does not train a network and does not execute Occam2D.

The preparation modifies a completed InputBuilder project in place. It performs four operations:

  1. replace nominal datum errors by reliability-weighted errors;

  2. map AI mean and standard-deviation grids to Occam parameters;

  3. optionally replace the startup vector with the AI mean; and

  4. write uncertainty-dependent prejudice records and reference them from the Occam model file.

Parameters:
  • lambda_ai (float, default 1.0) – Non-negative global multiplier for AI prejudice amplitudes. A value of zero writes no active prejudice records.

  • sigma_ai_floor (float, default 0.05) – Positive model-uncertainty floor in log10 resistivity. It prevents unbounded prejudice weights where ensemble spread is very small.

  • reliability_floor (float, default 1e-6) – Smallest observation reliability used in effective errors. It must lie in (0, 1].

  • prejudice_filename (str, default "DUHIPrejudice") – Solver-local prejudice filename. It must be a non-empty file name without directory components.

  • grid_mapper (callable, optional) – Function accepting (grid, model, mesh, x_coordinates, z_coordinates) and returning a one-dimensional vector of length model.n_params. If omitted, the geometry-aware map_ai_grid_to_occam() mapper is used.

  • verbose (int or bool, default 0) – Verbosity level. Positive values print a compact completion message after preparation.

Variables:
  • lambda_ai (float) – Global AI prejudice multiplier.

  • sigma_ai_floor (float) – Model-space uncertainty floor.

  • reliability_floor (float) – Data-space reliability floor.

  • prejudice_filename (str) – Native Occam prejudice filename.

  • grid_mapper (callable) – Active AI-grid to Occam-parameter mapping function.

  • verbose (int) – Integer verbosity level.

  • is_prepared (bool) – Whether prepare() completed successfully.

  • preparation (DUHIPreparation) – Most recent completed preparation result.

Notes

Instances are intentionally one-shot because the builder is modified in place. Reapplying reliability weights to an already prepared data table would compound error inflation. Create a new DUHIInverter2D and a fresh InputBuilder for each run.

When AI coordinates are omitted, the default mapper infers uniform AI cell centres spanning the complete Occam horizontal and earth domains. Explicit coordinates are preferred for archived runs.

See also

EMInverter2D

Produces learned 2-D model proposals.

EnsembleInverter

Produces ensemble mean and predictive uncertainty.

OccamPrejudice

Encodes model targets and weights for the native solver.

pycsamt.models.occam2d.OccamRunner

Executes a prepared Occam2D project.

Examples

Prepare a completed Occam2D project:

>>> from pycsamt.ai.inversion import DUHIInverter2D
>>> inverter = DUHIInverter2D(
...     lambda_ai=1.0,
...     sigma_ai_floor=0.05,
... )
>>> result = inverter.prepare(
...     builder,
...     ai_mean=ensemble_mean,
...     ai_std=ensemble_std,
...     observation_reliability=reliability,
... )
>>> result.prejudice_file
PosixPath('occam_run/DUHIPrejudice')

Supply a geometry-aware mapper:

>>> inverter = DUHIInverter2D(
...     grid_mapper=physical_mesh_mapper,
... )

References

[DUHIInverter2D-1]

deGroot-Hedlin, C., and Constable, S., “Occam’s inversion to generate smooth, two-dimensional models from magnetotelluric data”, Geophysics, 55(12), 1613-1624, 1990.

grid_mapper: Callable[[ndarray, OccamModel, OccamMesh, ndarray | None, ndarray | None], ndarray]
validate()[source]

Validate DUHI preparation hyperparameters.

Raises:
  • TypeError – Raised when grid_mapper is not callable.

  • ValueError – Raised when numerical controls are non-finite or outside their accepted ranges, or when prejudice_filename is empty or contains directory components.

Return type:

None

See also

DUHIInverter2D.prepare

Validates run-specific inputs after these controls.

Examples

>>> DUHIInverter2D(lambda_ai=0.5).validate()
prepare(builder, *, ai_mean, ai_std, observation_reliability, ai_initialize=True, ai_x=None, ai_z=None)[source]

Apply both DUHI branches to completed Occam2D inputs.

Parameters:
  • builder (InputBuilder) – Completed Occam2D input builder. builder.is_ready must be True and its data, model, startup, configuration, and work directory must be populated. Compatible builder objects exposing the same interface are also accepted.

  • ai_mean (array-like of float, shape (n_depth, n_horizontal)) – Finite ensemble-mean log10-resistivity grid.

  • ai_std (array-like of float, shape (n_depth, n_horizontal)) – Finite non-negative predictive standard-deviation grid. It must have the same shape as ai_mean.

  • observation_reliability (array-like of float, shape (n_data,)) – One reliability value in [0, 1] for each Occam data row, in exactly the same order as data_blocks.

  • ai_initialize (bool, default True) – If True, replace Startup.param_values with the mapped AI mean. If False, retain the existing startup vector while still writing the DUHI prejudice.

  • ai_x (array-like of float, optional) – Horizontal coordinates of AI grid columns in the Occam mesh coordinate system. The length must equal ai_mean.shape[1]. If omitted, uniform centres spanning the complete mesh width are inferred.

  • ai_z (array-like of float, optional) – AI grid depth coordinates, positive downward from the earth surface. The length must equal ai_mean.shape[0]. If omitted, uniform centres spanning the earth mesh depth are inferred.

Returns:

Immutable summary containing generated paths, dimensions, effective-error bounds, prejudice-weight bounds, and the initialization choice.

Return type:

DUHIPreparation

Raises:
  • RuntimeError – Raised when this one-shot object has already prepared a project.

  • TypeError – Raised when builder does not expose the expected Occam2D input interface.

  • ValueError – Raised when the builder is incomplete, AI grids are incompatible, reliability length is wrong, mapped vectors are invalid, or model/startup dimensions disagree.

Notes

The builder and its data, model, and startup objects are modified in place. The files are then rewritten sequentially using their standard Occam writers.

See also

apply_observation_reliability

Implements the data-space transformation.

OccamPrejudice.from_dense

Builds the model-space sparse constraint.

DUHIInverter2D.preparation

Returns the same result after successful preparation.

Examples

>>> result = inverter.prepare(
...     builder,
...     ai_mean=mean,
...     ai_std=std,
...     observation_reliability=reliability,
... )
>>> result.n_params
336
property is_prepared: bool[source]

Return whether Occam2D inputs were prepared successfully.

Returns:

True after prepare() completes without error.

Return type:

bool

Examples

>>> DUHIInverter2D().is_prepared
False
property preparation: DUHIPreparation[source]

Return the completed preparation summary.

Returns:

Immutable result from the successful preparation.

Return type:

DUHIPreparation

Raises:

RuntimeError – Raised when prepare() has not completed.

Examples

>>> inverter.preparation
DUHIPreparation(...)
property prejudice: OccamPrejudice[source]

Return the generated sparse Occam prejudice object.

Returns:

Generated model-space target and weight definition.

Return type:

OccamPrejudice

Raises:

RuntimeError – Raised when prepare() has not completed.

property ai_mean_parameters: ndarray[source]

Return a copy of the mapped AI mean parameter vector.

Returns:

AI mean in Occam layer-major parameter order.

Return type:

numpy.ndarray of float, shape (n_params,)

Raises:

RuntimeError – Raised when prepare() has not completed.

property ai_std_parameters: ndarray[source]

Return a copy of mapped AI standard deviations.

Returns:

Predictive standard deviations in Occam parameter order.

Return type:

numpy.ndarray of float, shape (n_params,)

Raises:

RuntimeError – Raised when prepare() has not completed.

property prejudice_weights: ndarray[source]

Return a copy of mapped uncertainty-dependent weights.

Returns:

Dense prejudice weights in Occam parameter order.

Return type:

numpy.ndarray of float, shape (n_params,)

Raises:

RuntimeError – Raised when prepare() has not completed.

summary(*, max_fields=None)[source]

Return a compact DUHI configuration and state summary.

Parameters:

max_fields (int, optional) – Accepted for compatibility with PyCSAMTObject. DUHI uses a fixed scientific summary and therefore ignores this value.

Returns:

One-line summary containing uncertainty controls and the preparation state.

Return type:

str

Examples

>>> "unprepared" in DUHIInverter2D().summary()
True
pycsamt.ai.inversion.duhi2d.apply_observation_reliability(errors, reliability, *, reliability_floor=1e-06)[source]

Return reliability-weighted effective datum errors.

Nominal errors are divided by the square root of observation reliability. Low-reliability data therefore receive larger effective errors and exert less influence on the normalized data misfit. Inputs are not modified.

Parameters:
  • errors (array-like of float) – Positive nominal standard errors. The returned array has the same shape.

  • reliability (array-like of float) – Reliability values in the closed interval [0, 1]. The values must be broadcastable to the shape of errors.

  • reliability_floor (float, default 1e-6) – Smallest reliability used in the denominator. It must lie in (0, 1] and prevents division by zero for rejected data.

Returns:

Effective errors with the same shape as errors.

Return type:

numpy.ndarray of float

Raises:

ValueError – Raised when inputs cannot be broadcast together, errors are non-finite or non-positive, reliability values are outside [0, 1], or the floor is invalid.

Notes

A reliability of one leaves the nominal error unchanged. A reliability of zero uses reliability_floor and therefore has a finite but potentially very small influence.

See also

DUHIInverter2D.prepare

Applies the transformation to an Occam data table.

Examples

>>> from pycsamt.ai.inversion.duhi2d import (
...     apply_observation_reliability,
... )
>>> apply_observation_reliability([2.0, 2.0], [1.0, 0.25]).tolist()
[2.0, 4.0]
pycsamt.ai.inversion.duhi2d.map_ai_grid_to_occam(grid, model, mesh, x_coordinates=None, z_coordinates=None)

Map a regular AI grid to Occam parameter order.

AI values are bilinearly interpolated to physical Occam cell centres. Values within each model-parameter rectangle are then averaged with finite-element cell area as weight:

\[\bar m_j = \frac{\sum_{k \in G_j} m_k A_k} {\sum_{k \in G_j} A_k},\]

where \(G_j\) is parameter group \(j\) and \(A_k\) is the area of mesh cell \(k\). Air layers are excluded, and the returned vector follows the layer-major order used by Occam startup and iteration files.

Parameters:
  • grid (array-like of float, shape (n_depth, n_horizontal)) – Finite AI values on a regular or explicitly coordinated grid. Values are typically log10 resistivity or predictive standard deviation.

  • model (OccamModel) – Populated Occam model definition. Horizontal parameter codes give the number of mesh cells spanned, and n_merge gives the number of earth rows spanned vertically.

  • mesh (OccamMesh) – Populated Occam finite-element mesh supplying physical cell widths, layer thicknesses, node positions, and air-layer count.

  • x_coordinates (array-like of float, optional) – Strictly increasing horizontal coordinates of AI grid columns, in the same coordinate system as mesh.x_nodes. The length must equal grid.shape[1]. If omitted, uniformly spaced cell centres spanning the complete Occam horizontal domain are used.

  • z_coordinates (array-like of float, optional) – Strictly increasing AI depth coordinates, positive downward from the earth surface. The length must equal grid.shape[0]. If omitted, uniformly spaced cell centres spanning the Occam earth domain are used.

Returns:

Area-weighted values in Occam layer-major parameter order.

Return type:

numpy.ndarray of float, shape (model.n_params,)

Raises:
  • TypeError – Raised when model or mesh lacks the required Occam geometry interface.

  • ValueError – Raised for invalid grids or coordinates, non-positive mesh dimensions, model groups inconsistent with the mesh, or a returned parameter count inconsistent with model.n_params.

Notes

Values outside the supplied AI coordinate range use nearest-edge extrapolation through numpy.interp(). Publication workflows should normally supply coordinates spanning the complete inversion domain so extrapolation is unnecessary.

The mapping averages log10 resistivity when grid is in log10 units. It therefore preserves the parameterization optimized by Occam rather than arithmetic resistivity.

When grid contains predictive standard deviation, the same area-weighted spatial averaging is applied. This treats uncertainty as a spatial field and does not assume independent AI pixels; it is therefore not a standard-error reduction by the number of cells.

See also

pycsamt.ai.inversion.duhi2d.DUHIInverter2D

Uses this mapper for AI means and standard deviations.

pycsamt.models.occam2d.OccamModel

Defines the parameter grouping traversed here.

pycsamt.models.occam2d.OccamMesh

Defines the physical cell areas used as weights.

Examples

Map a grid after reading an Occam project:

>>> from pycsamt.ai.inversion.mapping2d import (
...     map_ai_grid_to_occam,
... )
>>> from pycsamt.models.occam2d import OccamMesh, OccamModel
>>> mesh = OccamMesh.read("occam_run/Occam2DMesh")
>>> model = OccamModel.read("occam_run/Occam2DModel")
>>> parameters = map_ai_grid_to_occam(
...     ai_grid,
...     model,
...     mesh,
...     x_coordinates=ai_x,
...     z_coordinates=ai_z,
... )
>>> parameters.shape == (model.n_params,)
True