2.26.5.21. pycsamt.agents.inv3d_agent#

pycsamt.agents.inv3d_agent#

Inv3DAgent — Graph-convolutional 3-D MT spatial inversion.

Wraps GCNInverter3D:

  • Represents the survey network as a spatial graph whose edges connect stations within a configurable radius. Spectral GCN message-passing propagates information between neighbouring stations so the resulting 3-D resistivity volume is spatially coherent — artefacts from station-by-station 1-D inversion are suppressed.

  • Trains on synthetic 3-D profiles, using one of two physics modes (see below), then predicts on the observed Sites dataset.

  • Outputs log₁₀ρ per depth layer and log₁₀h per interface for every station, giving a full layered earth model that can be gridded into a 3-D resistivity volume.

  • Optionally runs MC-dropout uncertainty (n_mc stochastic passes) to produce depth-resolved confidence maps alongside the main prediction.

Physics modes#

physics="mt1d" (default)

Tiles independent 1-D forward models across the real station positions (generate_dataset()). This is the original smoke/demo path from the AI-inversion implementation plan: nothing enforces genuine lateral/3-D coupling between the tiled 1-D columns beyond what the GCN’s spatial smoothing adds after the fact, so the plan explicitly does not call this genuine 3-D inversion. Kept unconditionally as the default and as an explicit fallback, per the plan’s requirement.

physics="mt3d"

Generates genuinely 3-D correlated geological volumes at the survey’s own real station (x, y) positions and solves them with the research-only, small-grid MT3DAdapter (generate_3d_maxwell_dataset()). Unlike Inv2DAgent(physics="mt2d"), this uses the survey’s actual station geometry rather than a synthetic uniform spacing, since the real coordinates are already needed to build the GCN adjacency graph. Each training profile’s per-station target is the true 3-D volume’s own vertical resistivity column at that station, resampled onto the agent’s display depth grid (nearest-cell; the geological grid’s own resolution is coarser than most production sections, per MT3DAdapter’s small-cell-budget research-only status). Only the TE-like zxy response is requested and used, matching the observed feature pipeline (_z_to_features(), which is [log10(rho_a_xy), phase_xy] only, zero-padded to this agent’s 4-wide feature slot — using the unused zyx/diagonal slots for synthetic data would create a train/observation distribution mismatch, not add information). When a held-out synthetic split is available, a genuine (non-fabricated) recovery check against known-truth resistivity is added to the result via recovery_report(), since the field survey has no ground truth to check against. This mode is far more expensive per training profile than "mt1d" (a real 3-D Maxwell solve vs. an independent 1-D solve per station) — lower n_train_profiles accordingly. Mesh accuracy degrades at higher frequencies (~10% error by 20-50 Hz vs ~1-2% at 1-2 Hz, at this agent’s default cells_per_skin_depth=None); passing an explicit cells_per_skin_depth (e.g. 8.0) narrows this to ~5-9% at the same cell budget but does not fully close it — see dataset3d’s module docstring for measured numbers and the cost/accuracy trade-off raising it together with max_mesh_cells buys. Prefer a freqs grid concentrated in the low-frequency range this solver’s own benchmark validates when accuracy matters more than matching a specific field acquisition band.

Requires PyTorch or TensorFlow.

Architecture#

GCNNet — spectral graph convolutional network (Kipf & Welling 2017). No external graph library is required.

References

Classes

Inv3DAgent(*[, api_key, model, ...])

3-D MT profile inversion using a graph-convolutional network (GCN).

class pycsamt.agents.inv3d_agent.Inv3DAgent(*, api_key=None, model=None, llm_provider='claude', n_layers=5, n_freqs=32, freqs=None, depth_max=None, n_train_profiles=150, epochs=30, patience=None, radius=5000.0, hidden=(256, 128, 64), dropout=0.1, n_mc=20, physics='mt1d', correlation_length_x_m=(500.0, 2000.0), correlation_length_y_m=(500.0, 2000.0), correlation_length_z_m=(100.0, 500.0), log_resistivity_mean=2.0, log_resistivity_std=0.5, mesh_safety_factor=3.0, max_mesh_cells=6000, cells_per_skin_depth=None, geology_grid_nx_ny=6, geology_grid_nz=None, verbose=False)[source]

Bases: BaseAgent

3-D MT profile inversion using a graph-convolutional network (GCN).

Parameters:
  • api_key (str)

  • model (str)

  • llm_provider (str)

  • n_layers (int) – Number of depth layers per station (default 5).

  • n_freqs (int) – Number of frequencies used for feature extraction (default 32).

  • freqs (array-like, optional — execution-time frequency-grid override.) – Explicit positive frequency grid in hertz. When supplied, it replaces the legacy 10^-410^3 Hz grid and determines n_freqs.

  • depth_max (float, optional — cumulative finite-layer depth in metres.) – Maximum cumulative model depth in metres. When supplied, layer thicknesses are geometrically graded and normalized to this depth. None preserves the legacy Bostick-derived display parameterization.

  • n_train_profiles (int) – Number of synthetic 3-D training profiles (default 150). With physics="mt3d", each profile costs a real 3-D Maxwell solve, not a cheap independent 1-D solve — lower this substantially (e.g. 20-40) for that mode.

  • epochs (int) – Training epochs (default 30).

  • patience (int or None) – Early-stopping patience measured in epochs. None uses one fifth of epochs with a minimum of five.

  • radius (float) – Maximum inter-station edge distance in metres for the adjacency graph (default 5 000 m). Stations farther apart than radius are disconnected in the graph.

  • hidden (tuple of int) – GCN hidden-layer sizes (default (256, 128, 64)).

  • dropout (float) – Dropout probability (default 0.1); also used for MC uncertainty.

  • n_mc (int) – Number of Monte-Carlo dropout passes for uncertainty estimation. Set to 0 to skip uncertainty (faster, default 20).

  • physics ({"mt1d", "mt3d"}, default "mt1d") – Synthetic training-data physics; see the module docstring.

  • correlation_length_x_m (tuple[float, float]) – : (float, float) physics="mt3d" only: horizontal/horizontal/vertical correlation length ranges forwarded to Maxwell3DDatasetConfig.

  • correlation_length_y_m (tuple[float, float]) – : (float, float) physics="mt3d" only: horizontal/horizontal/vertical correlation length ranges forwarded to Maxwell3DDatasetConfig.

  • correlation_length_z_m (tuple[float, float]) – : (float, float) physics="mt3d" only: horizontal/horizontal/vertical correlation length ranges forwarded to Maxwell3DDatasetConfig.

  • log_resistivity_mean (float) – physics="mt3d" only: affine map from the standardized correlated field to log10(resistivity_ohm_m).

  • log_resistivity_std (float) – physics="mt3d" only: affine map from the standardized correlated field to log10(resistivity_ohm_m).

  • mesh_safety_factor (float, int) – physics="mt3d" only: forwarded to Maxwell3DDatasetConfig. max_mesh_cells also builds the matching MT3DAdapter(max_cells=...) used to solve.

  • max_mesh_cells (float, int) – physics="mt3d" only: forwarded to Maxwell3DDatasetConfig. max_mesh_cells also builds the matching MT3DAdapter(max_cells=...) used to solve.

  • cells_per_skin_depth (float or None, default None) – physics="mt3d" only: opt-in frequency-aware solver core resolution, forwarded to Maxwell3DDatasetConfig — see its docstring for the accuracy-vs-cell-cost trade-off this exists to address. None (the default) preserves this agent’s original behavior (core resolution = the geological grid’s own spacing); this agent’s default freqs grid spans up to ~1000 Hz (_DEFAULT_FREQS), so enabling this without also raising max_mesh_cells and/or narrowing freqs risks a “needs N solver cells” error at high frequencies.

  • geology_grid_nx_ny (int, int or None) – physics="mt3d" only: resolution of the geological training grid (how finely the true 3-D resistivity volumes are drawn), independent of the solver mesh’s own frequency-aware resolution above. geology_grid_nz=None (default) uses min(max(n_layers, 4), 8). Raising these increases realism at the cost of more solver cells per realization; see max_mesh_cells for the resulting budget.

  • geology_grid_nz (int, int or None) – physics="mt3d" only: resolution of the geological training grid (how finely the true 3-D resistivity volumes are drawn), independent of the solver mesh’s own frequency-aware resolution above. geology_grid_nz=None (default) uses min(max(n_layers, 4), 8). Raising these increases realism at the cost of more solver cells per realization; see max_mesh_cells for the resulting budget.

  • keys (Output data)

  • ----------

  • path (sites /)

  • coords (ndarray (n_stations, 2), optional — station (x, y) in metres.) – Auto-extracted from EDI lat/lon when absent.

  • adjacency (ndarray (n_stations, n_stations), optional — pre-computed) – normalised adjacency; overrides radius when supplied.

  • output_dir (str, optional)

  • freqs

  • depth_max

  • topography (bool or dict, optional) – True extracts elevation and chainage from sites and adds a terrain-draped section. A mapping may instead provide elevation_m and optionally chainage_km, plus exaggeration and interp_method rendering options. This is a geometry/visualisation contract; it does not alter the MT forward solver or GCN prediction.

  • period_range ([T_min, T_max], optional)

  • keys

  • ----------------

  • (n_sta (adjacency ndarray)

  • log₁₀ρ (n_layers) )

  • (n_sta

  • (metres) (n_layers-1) — log₁₀h)

  • (n_sta

  • std (n_layers) or None — MC-dropout)

  • (km) (depths_km ndarray — depth axis at station midpoints)

  • grid (frequency_grid_hz ndarray — effective feature/forward frequency)

  • contract (configured_depth_max_m float or None — explicit depth)

  • metadata (topography dict or None — resolved terrain)

  • list[str] (station_names)

  • (n_sta

  • metres (2) )

  • (n_sta

  • n_sta)

  • float (rms_global)

  • GCNInverter3D (inverter)

  • dict (figure_paths)

  • dict

  • "mt3d" (physics str — "mt1d" or)

  • used (mode)

  • known-truth (mt3d_recovery dict or None — held-out) – recovery metrics (physics="mt3d" only)

  • losses (training_history dict -- per-epoch training and validation)

  • stopping (epochs_completed int -- epochs actually completed before)

  • loss (best_validation_loss float -- minimum finite validation)

  • verbose (bool | int | str)

Examples

>>> agent = Inv3DAgent(n_layers=5, epochs=20, n_mc=10)
>>> result = agent.execute(
...     {
...         "path": "/data/WILLY_EDIs",
...         "output_dir": "/out/inv3d",
...     }
... )
>>> result["rms_global"]
0.28
SYSTEM_PROMPT: str = 'You are an expert in 3-D MT inversion using graph-convolutional deep learning.\nGiven a GCN-based 3-D inversion result, write 4-5 sentences that:\n1. Describe the survey geometry (station count, spatial extent, adjacency radius).\n2. Interpret the dominant 3-D resistivity structures and their spatial continuity.\n3. Assess prediction quality (RMS, depth range) relative to station spacing.\n4. Compare the GCN spatial result to independent 1-D predictions where possible.\n5. Recommend geological follow-up and areas with highest uncertainty.\nReply in plain scientific English.\n'

Override in subclasses to give the LLM its domain expertise.

execute(input_data)[source]

Run this agent on input_data and return an AgentResult.

Subclasses must implement this method. The contract:

  • Reset self._last_cost = 0.0 at the top.

  • Record wall-clock time with t0 = time.time().

  • Return AgentResult(elapsed_seconds=time.time()-t0, cost_estimate_usd=self._last_cost, ...).

Parameters:

input_data (dict[str, Any])

Return type:

AgentResult