2.26.5.20. pycsamt.agents.inv2d_agent#
pycsamt.agents.inv2d_agent#
Inv2DAgent — U-Net based 2-D MT profile inversion.
Wraps EMInverter2D:
Assembles a 2-D pseudosection image from the observed Sites (station × frequency × impedance component) as the U-Net input.
Generates a matching synthetic 2-D training dataset, using one of two
physicsmodes (see below).Trains the U-Net and produces a resistivity section output: (n_depth × n_stations) in log₁₀ Ω·m.
Visualises the result with
plot_inversion_result_2d().
The U-Net treats the whole profile at once, so it naturally captures lateral continuity — a key advantage over station-by-station 1-D inversion.
Physics modes#
physics="mt1d"(default)Tiles independent 1-D forward models into profile-shaped arrays (
generate_dataset()). This is the original smoke/demo path from the AI-inversion implementation plan: nothing enforces lateral continuity between the tiled 1-D columns, so the plan explicitly does not call it genuine 2-D inversion. Kept unconditionally as the default and as an explicit fallback, per the plan’s requirement.physics="mt2d"Generates genuinely 2-D correlated training models and solves them with the verified
MT2DAdapter(generate_2d_maxwell_dataset()), and can add spatial regularization (fit()’slambda_x/lambda_z/lambda_tv). Only the TE-mode (zxy) response is requested here, even thoughpycsamt.ai.training.dataset2dnow validates the TM-mode (zyx) response too (see its module docstring): this agent’s feature layout is still TE-only, so requestingzyxhere would only cost an extra, unused forward solve per realization, not change accuracy. When a held-out synthetic split is available, a genuine (non-fabricated) recovery check against known-truth resistivity is added to the result viarecovery_report(), since the field survey has no ground truth to check against.physics="mt2d_tri"Generates triangular-mesh correlated training models (
generate_2d_tri_maxwell_dataset()) solved withMare2DEMAdapter– unverified physics, see that adapter’s own module docstring; it needs a real compiled MARE2DEM binary this environment does not have. Uses the same synthetic uniformstation_spacing_mgeometryphysics="mt2d"already documents (real station coordinates are not read here either). Output is a per-trianglelog10(resistivity)field, not a(depth, station)grid, so it is trained and rendered differently from the other two modes: a graph-convolutional inverter (GCNInverter3Dwithn_layers=1, reused rather than duplicated – its architecture has nothing 3-D/station-specific about it, only itsn_layersnaming, here repurposed to mean “one scalar per graph node” instead of “one depth column per station”) over the mesh’s triangle-centroid adjacency graph, rendered withdraw_tri_mesh(). Not plotted viaplot_inversion_result_2d()(that function is rectilinear-only) or draped with real topography (that needs real station coordinates, which this mode – likephysics="mt2d"– does not read).
Requires PyTorch or TensorFlow (physics="mt2d"’s spatial
regularization is PyTorch-only, and physics="mt2d_tri"’s GCN
inverter always needs one; see EMInverter2D.fit/GCNInverter3D).
Classes
|
2-D MT profile inversion using a U-Net convolutional architecture. |
- class pycsamt.agents.inv2d_agent.Inv2DAgent(*, api_key=None, model=None, llm_provider='claude', n_depth=40, n_freqs=32, freqs=None, depth_max=None, n_components=2, arch='unet', n_train_profiles=200, n_stations_per_profile=20, epochs=30, patience=None, physics='mt1d', station_spacing_m=500.0, correlation_length_x_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=8.0, max_mesh_cells=200000, lambda_x=0.0, lambda_z=0.0, lambda_tv=0.0, mesh_target_cell_m=100.0, field_grid_cell_m=50.0, topo_x_m=None, topo_z_m=None, gcn_hidden=(64, 32, 16), gcn_adjacency_radius_m=300.0, mare2dem_adapter=None, verbose=False)[source]
Bases:
BaseAgent2-D MT profile inversion using a U-Net convolutional architecture.
- Parameters:
api_key (str)
model (str)
llm_provider (str)
n_depth (int) – Number of depth cells in the output section (default 40).
n_freqs (int) – Number of input frequencies (default 32).
freqs (array-like, optional — execution-time frequency override) – Explicit positive frequency grid in hertz.
depth_max (float, optional — cumulative model depth in metres) – Maximum cumulative model depth in metres.
Nonepreserves the legacy frequency-derived parameterization.n_components (int) – Number of channels in the input pseudosection (default 2: log10 apparent resistivity and phase for the xy component).
arch (str) – U-Net variant (default
"unet").n_train_profiles (int) – Number of synthetic 2-D profiles for training (default 200).
n_stations_per_profile (int) – Stations per synthetic profile (default 20).
epochs (int) – Training epochs (default 30).
patience (int or None) – Early-stopping patience.
Noneuses one fifth ofepochswith a minimum of five. Set greater thanepochsonly for a controlled fixed-epoch experiment; validation-based stopping is normally safer.physics ({"mt1d", "mt2d", "mt2d_tri"}, default "mt1d") – Synthetic training-data physics; see the module docstring.
station_spacing_m (float, default 500.0) – Uniform synthetic station spacing used by
physics="mt2d"andphysics="mt2d_tri"; the actual survey’s real station geometry is not read by either.correlation_length_x_m ((float, float)) –
physics="mt2d"/"mt2d_tri"only: horizontal/vertical correlation length ranges forwarded toMaxwell2DDatasetConfig/MaxwellTri2DDatasetConfig.correlation_length_z_m ((float, float)) –
physics="mt2d"/"mt2d_tri"only: horizontal/vertical correlation length ranges forwarded toMaxwell2DDatasetConfig/MaxwellTri2DDatasetConfig.log_resistivity_mean (float) –
physics="mt2d"/"mt2d_tri"only: affine map from the standardized correlated field tolog10(resistivity_ohm_m).log_resistivity_std (float) –
physics="mt2d"/"mt2d_tri"only: affine map from the standardized correlated field tolog10(resistivity_ohm_m).mesh_safety_factor (float, int) –
physics="mt2d"only: forwarded toMaxwell2DDatasetConfig.max_mesh_cells (float, int) –
physics="mt2d"only: forwarded toMaxwell2DDatasetConfig.lambda_x (float, default 0.0) – Spatial-regularization weights forwarded to
fit(). Zero by default, so nothing changes unless explicitly requested.physics="mt2d"only.lambda_z (float, default 0.0) – Spatial-regularization weights forwarded to
fit(). Zero by default, so nothing changes unless explicitly requested.physics="mt2d"only.lambda_tv (float, default 0.0) – Spatial-regularization weights forwarded to
fit(). Zero by default, so nothing changes unless explicitly requested.physics="mt2d"only.mesh_target_cell_m (float) –
physics="mt2d_tri"only: forwarded toMaxwellTri2DDatasetConfig.field_grid_cell_m (float) –
physics="mt2d_tri"only: forwarded toMaxwellTri2DDatasetConfig.topo_x_m (array-like, optional) –
physics="mt2d_tri"only: real topography (z positive down) forwarded toMaxwellTri2DDatasetConfig/build_graded_tri_mesh(). Both default toNone(flat surface atz=0, unchanged from before this parameter existed) – when given, training stations sit at their true interpolated elevation instead. This builds a real topography-following training mesh; it is unrelated to thetopographyexecute()-time input key above, which only re-renders an already-flatmt1d/mt2dprediction in an absolute-elevation display frame.topo_z_m (array-like, optional) –
physics="mt2d_tri"only: real topography (z positive down) forwarded toMaxwellTri2DDatasetConfig/build_graded_tri_mesh(). Both default toNone(flat surface atz=0, unchanged from before this parameter existed) – when given, training stations sit at their true interpolated elevation instead. This builds a real topography-following training mesh; it is unrelated to thetopographyexecute()-time input key above, which only re-renders an already-flatmt1d/mt2dprediction in an absolute-elevation display frame.gcn_hidden (tuple of int, default (64, 32, 16)) –
physics="mt2d_tri"only: hidden-layer widths forwarded toGCNInverter3D.gcn_adjacency_radius_m (float, default 300.0) –
physics="mt2d_tri"only: triangle-centroid adjacency radius forwarded tobuild_adjacency(). Must be set relative to the actual mesh scale, not left at the default: it is a hard cutoff in the same metres asmesh_target_cell_m, and if it is smaller than the typical distance between neighbouring triangle centroids,build_adjacencyreturns the identity matrix (every triangle connected only to itself) and the GCN silently degenerates into a per-triangle lookup with no spatial message-passing at all – no error is raised. A radius of roughly 1.5-2xmesh_target_cell_mis a reasonable starting point; verify withbuild_adjacency(mesh.triangle_centroids_m, radius).sum() > mesh.n_triangles(more than just the self-loops) before trusting a training run.mare2dem_adapter (object, optional) –
physics="mt2d_tri"only: pre-builtMare2DEMAdapter(e.g. pointed at a specific compiled binary). Defaults toMare2DEMAdapter(), resolved from the environment.keys (Output data)
----------
path (sites /)
output_dir (str, optional)
topography (bool or dict, optional) – Extract terrain from
sitesand render the predicted section in an absolute-elevation frame. A mapping can provideelevation_mandchainage_kmplusexaggerationandinterp_method.period_range ([T_min, T_max], optional)
freqs
depth_max
keys
----------------
ρ (pred_section ndarray (n_depth × n_stations) — log₁₀) – (
physics="mt1d"/"mt2d"only)"mesh"/"log10_resistivity" (pred_triangles dict with) – (
physics="mt2d_tri"only)(km) (depths_km ndarray — depth axis) – (
physics="mt1d"/"mt2d"only)list[str] (station_names)
metadata (topography dict or None — resolved terrain) – (
physics="mt1d"/"mt2d"only)only) (rms_global float (physics="mt1d"/"mt2d")
GCNInverter3D (inverter EMInverter2D or)
dict (figure_paths)
dict
"mt1d" (physics str —) –
"mt2d_tri", mode used"mt2d" –
"mt2d_tri", mode usedor –
"mt2d_tri", mode usedknown-truth (mt2d_tri_recovery dict or None — held-out) – recovery metrics (
physics="mt2d"only)known-truth – recovery metrics (
physics="mt2d_tri"only)
- SYSTEM_PROMPT: str = 'You are an expert in 2-D MT inversion using deep learning (U-Net architecture).\nGiven a 2-D AI inversion result, write 4-5 sentences that:\n1. Describe the input pseudosection geometry (stations × frequencies).\n2. Interpret the dominant structural features in the resistivity section.\n3. Assess lateral continuity and compare to classical smoothness-constrained results.\n4. Identify artefacts or stations with poor convergence.\n5. Recommend follow-up (regularisation, 3-D verification, drilling targets).\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.0at the top.Record wall-clock time with
t0 = time.time().Return
AgentResult(elapsed_seconds=time.time()-t0, cost_estimate_usd=self._last_cost, ...).
- Parameters:
- Return type: