Agents Module#

The agents module provides the multi-agent system for automating geophysical workflows using Large Language Models.

Base Classes#

Base Agent Class for Multi-Agent System

Provides the foundation for all specialized agents in the workflow.

class PyHydroGeophysX.agents.base_agent.AgentResult(status: ~typing.Literal['success', 'failed', 'needs_review'], summary: str, data: ~typing.Dict[str, ~typing.Any], warnings: ~typing.List[str] = <factory>, next_suggested_action: str | None = None, llm_interpretation: str | None = None, elapsed_seconds: float = 0.0, cost_estimate_usd: float | None = None, error: str | None = None, error_fix_hint: str | None = None)[source]#

Bases: object

Standard user-facing result returned by agent workflows.

Parameters:
  • status ({"success", "failed", "needs_review"}) – Execution state for the agent or workflow.

  • summary (str) – One-sentence human-readable summary. This is always populated.

  • data (dict) – Numerical, object, or artifact outputs.

  • warnings (list of str, optional) – Non-fatal issues the user should review.

  • next_suggested_action (str, optional) – Suggested next step for the user.

  • llm_interpretation (str, optional) – AI-generated interpretation. UIs should label this before rendering.

  • elapsed_seconds (float, optional) – Wall-clock runtime.

  • cost_estimate_usd (float, optional) – Approximate LLM cost associated with this result.

  • error (str, optional) – Error message when status="failed".

  • error_fix_hint (str, optional) – Plain-language fix hint for the user.

Returns:

Dict-like result object. Existing code can continue to call result["status"] or result.get("artifact_key").

Return type:

AgentResult

Raises:

KeyError – Raised by __getitem__ when a key is not present.

Examples

>>> result = AgentResult(status="success", summary="Loaded data.", data={"n": 2})
>>> result["status"]
'success'
>>> result.get("n")
2
cost_estimate_usd: float | None = None#
data: Dict[str, Any]#
elapsed_seconds: float = 0.0#
error: str | None = None#
error_fix_hint: str | None = None#
classmethod from_dict(payload: Dict[str, Any], default_summary: str = 'Agent completed.') → AgentResult[source]#

Create an AgentResult from a legacy dictionary.

Parameters:
  • payload (dict) – Legacy result dictionary.

  • default_summary (str, optional) – Summary to use if the dictionary does not provide one.

Returns:

Normalized result object.

Return type:

AgentResult

Raises:

TypeError – If payload is not a dictionary.

Examples

>>> AgentResult.from_dict({"status": "success", "value": 1}).get("value")
1
get(key: str, default: Any = None) → Any[source]#
items() → Iterable[Tuple[str, Any]][source]#
keys() → Iterable[str][source]#
llm_interpretation: str | None = None#
next_suggested_action: str | None = None#
status: Literal['success', 'failed', 'needs_review']#
summary: str#
to_dict(include_data_keys: bool = True) → Dict[str, Any][source]#

Return a dictionary representation.

Parameters:

include_data_keys (bool, optional) – If True, merge data into the top level for legacy callers.

Returns:

Serialized result.

Return type:

dict

Raises:

None –

Examples

>>> AgentResult("success", "ok", {"x": 1}).to_dict()["x"]
1
values() → Iterable[Any][source]#
warnings: List[str]#
class PyHydroGeophysX.agents.base_agent.BaseAgent(name: str, api_key: str | None = None, model: str | None = None, llm_provider: str = 'openai')[source]#

Bases: ABC

Abstract base class for all agents in the multi-agent system.

Each agent is specialized for a specific task and can communicate with other agents through the coordinator.

abstractmethod execute(input_data: Dict[str, Any]) → Dict[str, Any][source]#

Execute the agent’s primary task.

Parameters:

input_data – Input data dictionary

Returns:

Dictionary containing execution results

get_context(key: str, default: Any = None) → Any[source]#

Get value from agent’s context.

property llm_enabled: bool#

Model access can use either an API key or saved local CLI login.

llm_usage_ledger: List[Dict[str, Any]]#
query_llm(prompt: str, system_message: str = None, temperature: float = 0.7, max_tokens: int = 1000, on_text: Callable[[str], None] | None = None) → str[source]#

Query the LLM API for assistance. Supports multiple LLM providers: OpenAI (GPT), Google (Gemini), and Anthropic (Claude).

Parameters:
  • prompt – User prompt for the LLM

  • system_message – System message defining agent behavior

  • temperature – Sampling temperature (0-1)

  • max_tokens – Maximum tokens in response

  • on_text – Called with the reply so far as it is generated, when given: the reply is streamed (OpenAI and Claude; Gemini calls it once, with the whole reply). The return value is the same either way.

Returns:

LLM response as string

static run_legacy_agent_workflow(workflow_config, api_key, llm_model, llm_provider, output_dir, progress_callback=None)[source]#

Unified agent workflow: infers task type from config and runs the appropriate pipeline.

The pre-controller implementation, kept reachable because it covers workflow types whose tools cannot be exercised here - there is no ParFlow output, no SEG-Y file and no GPU on this machine - and because a run that behaves differently can then be compared against the code it replaced. Reached by setting PHGX_LEGACY_WORKFLOW=1. The code lives in PyHydroGeophysX.agents._legacy_workflow. Supported: data fusion, time-lapse, direct ERT conversion. Returns: results dict, execution plan, interpretation, report files

Parameters:
  • workflow_config – Configuration dictionary from ContextInputAgent

  • api_key – LLM API key

  • llm_model – LLM model name

  • llm_provider – LLM provider (‘openai’, ‘gemini’, ‘claude’)

  • output_dir – Output directory path

  • progress_callback – Optional callback function(step: str, progress: float, details: str)

static run_unified_agent_workflow(workflow_config, api_key, llm_model, llm_provider, output_dir, progress_callback=None, **kwargs)[source]#

Run one workflow, choosing each step from what the run has produced.

Kept as the entry point every caller already uses - the desktop studio, the Streamlit app and the one-click runner - while what happens behind it changed. It used to classify the request into one of eight workflow types and run that type’s fixed sequence; it now builds a run context and hands it to the controller in PyHydroGeophysX.agents.runtime, which picks each step from the tools whose inputs exist and observes the result before picking the next.

The returned execution plan is derived from the steps that ran, so it can no longer advertise a step the run skipped.

Set PHGX_LEGACY_WORKFLOW=1 to run run_legacy_agent_workflow() instead, which is the implementation this replaced.

save_results(output_dir: str)[source]#

Save agent results to disk, preserving numpy arrays and PyGIMLi meshes.

  • numpy arrays → {agent}_{key}.npy

  • PyGIMLi meshes → {agent}_{key}.bms

  • Everything else → {agent}_results.json (metadata)

Parameters:

output_dir (str) – Target directory; created if absent.

Returns:

Path to the JSON metadata file.

Return type:

str

update_context(key: str, value: Any)[source]#

Update agent’s context with new information.

validate_input_file(file_path: Any, supported_extensions: Iterable[str], field_name: str = 'data_file', max_size_mb: float | None = None) → AgentResult | None[source]#

Validate a user-provided input file before processing.

Parameters:
  • file_path (Any) – Path-like value to validate.

  • supported_extensions (iterable of str) – Allowed file extensions, including the leading dot.

  • field_name (str, optional) – Name of the field being validated.

  • max_size_mb (float, optional) – Optional file-size limit in megabytes.

Returns:

Failure result if validation fails; otherwise None.

Return type:

AgentResult or None

Raises:

None –

Examples

>>> BaseAgent.__dict__["validate_input_file"]
<function BaseAgent.validate_input_file at ...
PyHydroGeophysX.agents.base_agent.CLAUDE_UNCAPPED_TOKENS = 16000#

Claude’s reply cap when the caller sets none. The Messages API needs a number, and the Anthropic SDK refuses a request it expects to outlast ten minutes without streaming (above about 21,000 tokens); every Claude model the studio offers can write this much.

Coordinator#

Agent Coordinator for Multi-Agent Workflow

Coordinates the execution of multiple specialized agents to complete the full geophysical processing workflow. Supports cross-modal geophysical data processing (ERT, seismic, and more) with multiple LLM API providers (GPT, Gemini, Claude).

class PyHydroGeophysX.agents.agent_coordinator.AgentCoordinator(api_key: str | None = None, output_dir: str = 'results/agents', llm_provider: str = 'openai')[source]#

Bases: object

Coordinates multiple agents to execute a complete workflow.

The coordinator manages cross-modal geophysical workflows such as: “load geophysical data → process → invert → convert to hydrologic parameters → report” with support for multiple data types (ERT, seismic, etc.) and LLM providers (GPT, Gemini, Claude).

execute_workflow(config: Dict[str, Any], dry_run: bool = False, resume: bool = False) → Any[source]#

Execute the complete workflow with registered agents.

Parameters:
  • config –

    Configuration dictionary containing:

    • user_request: Optional natural-language request. It is read as the preview reads it: the ERT files it names are run when this dictionary names none, the instrument, petrophysical parameters and seismic data it names fill what is left out, and keys set here win. What it states that the run does not use is reported in warnings

    • data_file: Path to ERT data file (ert_file is accepted)

    • time_lapse_files: Survey files in acquisition order; when given, every one is loaded and inverted together (a time-lapse inversion) and data_file is ignored

    • instrument: Instrument type (E4D, Syscal, etc.)

    • inversion_params: Parameters for inversion

    • petrophysical_params: Parameters for water content conversion. The water-content step runs unless the configuration says otherwise (convert_to_water_content, or a request that asks for resistivity only), read as the runtime reads it

    • use_seismic: Whether to include seismic processing (default: False)

    • seismic_data: Seismic travel-time data for the structure constraint, loaded or as a file path

    • use_climate: Whether to include climate data (default: False)

    • climate_config: Climate data configuration (coords/geometry, dates, etc.)

    • ert_timestamps: Timestamps for ERT acquisitions (for climate alignment)

    A configuration naming tdem_file, em_file, seismic_file or raw_seismic_file, or a request that asks for TDEM, is refused: this pipeline has no step for them, and BaseAgent.run_unified_agent_workflow() runs them.

  • dry_run – If True, return a preview plan without executing.

  • resume – If True, load checkpointed intermediate results and skip already-completed steps.

Returns:

Dictionary containing workflow results, or AgentResult when dry_run=True.

get_workflow_summary() → Dict[str, Any][source]#

Get summary of workflow execution.

preview_workflow(config: Dict[str, Any]) → AgentResult[source]#

Resolve and validate a workflow plan without running processing.

Parameters:

config (dict) – Workflow configuration. May include user_request or request for deterministic preview parsing.

Returns:

Preview result with resolved config, validation warnings, plan, and approximate LLM cost.

Return type:

AgentResult

Raises:

None –

Examples

>>> import tempfile
>>> coordinator = AgentCoordinator(api_key=None, output_dir=tempfile.mkdtemp())
>>> result = coordinator.preview_workflow({"data_file": "missing.ohm"})

===== PyHydroGeophysX Agent Preview =====
...
>>> result["status"]
'failed'
register_agent(agent_name: str, agent_instance)[source]#

Register an agent with the coordinator.

Parameters:
  • agent_name – Unique identifier for the agent

  • agent_instance – Agent instance to register

Input Agents#

ContextInputAgent#

Context Input Agent for Natural Language Workflow Configuration

Translates user’s natural language requests into structured workflow configurations. Supports multiple LLM providers (OpenAI GPT, Google Gemini, Anthropic Claude).

class PyHydroGeophysX.agents.context_input_agent.ContextInputAgent(api_key: str | None = None, model: str = 'gpt-4', llm_provider: str = 'openai')[source]#

Bases: BaseAgent

Agent that interprets natural language requests and generates workflow configurations.

This agent uses LLM to understand user intent and create appropriate configuration dictionaries for the AgentCoordinator, including parameters for: - Data loading (file paths, instruments, CRS) - Inversion settings (regularization, iterations, time-lapse mode) - Petrophysical parameters - Climate data integration - Seismic constraints - Uncertainty quantification

execute(input_data: Dict[str, Any]) → AgentResult[source]#

Execute the context input agent (parse natural language request).

Parameters:

input_data – Dictionary containing: - user_request: Natural language workflow description - available_data: Optional dict with available files/instruments

Returns:

  • status: ‘success’ or ‘failed’

  • workflow_config: Generated configuration

  • explanation: Human-readable explanation

Return type:

Dictionary containing

explain_config(config: Dict[str, Any]) → str[source]#

Generate human-readable explanation of workflow configuration.

Parameters:

config – Workflow configuration dictionary

Returns:

Formatted explanation string

parse_request(user_request: str, available_data: Dict[str, Any] | None = None) → Dict[str, Any][source]#

Parse natural language request into workflow configuration.

Uses TWO focused prompts for better reliability: 1. Inversion configuration prompt (ERT-specific parameters) 2. Climate configuration prompt (meteorological parameters)

Without an API key, or when a model call fails, the request is read by the deterministic rules of preview_config() instead, with a warning saying so. It used to raise “OPENAI API key not found”, so a caller without a key had no configuration at all.

Parameters:
  • user_request – Natural language description of desired workflow

  • available_data – Optional dict with available data files, instruments, etc.

Returns:

Dict containing workflow_config ready for AgentCoordinator

parse_workers = 4#

Extraction questions parse_request() keeps in flight at once after the router has answered. 1 asks them one after another, as it used to.

preview_config(user_request: str, available_data: Dict[str, Any] | None = None) → AgentResult[source]#

Build a deterministic preview config without calling an LLM.

Parameters:
  • user_request (str) – Natural-language workflow request.

  • available_data (dict, optional) – Optional file and instrument hints supplied by the caller.

Returns:

Preview configuration with missing-field warnings.

Return type:

AgentResult

Raises:

None –

Examples

>>> agent = ContextInputAgent(api_key=None)
>>> result = agent.preview_config("Run ERT inversion on data.ohm")
>>> result.get("workflow_config")["data_file"]
'data.ohm'
request_inputs(user_request: str, known_files: Sequence[Any] = (), methods: Dict[str, Any] | None = None) → Dict[str, Any][source]#

What a request states, read by deterministic rules, without defaults.

The files it names (ERT surveys, an electrode file, seismic, TDEM and GPR data), the instrument and petrophysical parameters written into it. preview_config() completes this with defaults into a runnable configuration; a caller that has a configuration of its own takes only this, so the parser’s defaults cannot replace the caller’s.

Parameters:
  • user_request (str) – Natural-language workflow request.

  • known_files (sequence of str, optional) – Files the caller already uses as ERT data. The request naming them does not make them seismic or TDEM data.

  • methods (dict, optional) – A model’s reading of which methods the request is about (request_methods: {"gpr": True, "seismic": False}). Where it says yes or no, it decides what an ambiguous file is - a SEG-Y line is GPR or seismic - in place of the words of the request.

Returns:

Only the keys the request gives a value for.

Return type:

dict

Raises:

None –

Examples

>>> agent = ContextInputAgent(api_key=None)
>>> agent.request_inputs("Invert a.ohm, measured with a Syscal")
{'data_file': 'a.ohm', 'ert_file': 'a.ohm', 'instrument': 'Syscal'}
>>> agent.request_inputs("Invert survey.dat with the seismic structure from srt.dat",
...                      known_files=["survey.dat"])["seismic_file"]
'srt.dat'
>>> agent.request_inputs("Process the GPR line line1.sgy at 0.08 m/ns")
{'gpr_file': 'line1.sgy', 'gpr_velocity': 0.08}
>>> agent.request_inputs("Process line1.sgy", methods={"gpr": True, "seismic": False})
{'gpr_file': 'line1.sgy'}
suggest_improvements(config: Dict[str, Any], site_conditions: str | None = None) → str[source]#

Suggest improvements to configuration based on best practices.

Parameters:
  • config – Current workflow configuration

  • site_conditions – Optional description of site conditions

Returns:

Suggestions for improving the configuration

PyHydroGeophysX.agents.context_input_agent.drop_borrowed_ert_files(config: Dict[str, Any], roles: Dict[str, Any] | None = None) → Dict[str, Any][source]#

config without ERT data that is another method’s input.

The parser’s ERT stage is told its data file is required, so a request to process TEM2Go data came back with the project the user had selected as TDEM data given as data_file too. The run’s route then showed Load ERT data, Run ERT inversion and Evaluate inversion quality after the TDEM inversion, and its report waited for the ERT loader to be offered a .tiw file. A file given to another method - the same file, or one inside the survey folder another method names - is that method’s data only, and a TEMcompany project is TDEM data whatever key it came under.

Parameters:
  • config (dict) – The workflow configuration.

  • roles (dict, optional) – Inputs the caller selected by role and has not merged into config yet; they decide which files are taken, and are not added.

Returns:

A new configuration. A TEM project dropped from the ERT keys becomes tdem_file when no TDEM data is named.

Return type:

dict

Examples

>>> drop_borrowed_ert_files({'data_file': 'p.tiw', 'ert_file': 'p.tiw',
...                          'tdem_file': 'p.tiw'})
{'tdem_file': 'p.tiw'}
>>> drop_borrowed_ert_files({'data_file': 'Sep06/project.tiw'}, {'tdem_file': 'Sep06'})
{}
>>> drop_borrowed_ert_files({'data_file': 'p.tiw'})
{'tdem_file': 'p.tiw'}
>>> drop_borrowed_ert_files({'data_file': 'a.ohm', 'tdem_file': 'Sep06'})
{'data_file': 'a.ohm', 'tdem_file': 'Sep06'}
PyHydroGeophysX.agents.context_input_agent.drop_unstated_petrophysics(config, request)[source]#

Remove petrophysical values the request does not contain, and say which.

Whether a water content is reliable turns on whose petrophysical relationship it used, and a value the parser produced itself - from the example in its own prompt, or a misread - would be reported as the user’s. A value counts as stated when the request contains that number, or the percentage it is a fraction of.

Returns:

"key value" for each value removed.

Return type:

list of str

Examples

>>> config = {'petrophysical_params': {'n': 1.8, 'porosity': 0.3},
...           'layer_params': {'regolith': {'rho_sat_range': [50, 250],
...                                         'n_range': [1.3, 2.2]}}}
>>> drop_unstated_petrophysics(config, 'n is 1.8, porosity 30%, rho_sat 50 to 250 ohm-m')
['regolith n_range [1.3, 2.2]']
>>> config['layer_params']
{'regolith': {'rho_sat_range': [50, 250]}}
PyHydroGeophysX.agents.context_input_agent.gpr_from_reply(reply: Any, request: str) → Tuple[Dict[str, Any], List[str]][source]#

The GPR configuration in the model’s answer, and a note on each value set aside.

The line (gpr_file), the velocity in m/ns or the permittivity it stands for, and the processing options. A velocity or permittivity the request does not contain is the model’s own - a “typical” 0.1 m/ns - and every depth would rest on it unsaid, so it is dropped and the run told; a velocity converted from cm/ns counts as stated.

Examples

>>> gpr_from_reply({"gpr_file": "a.DZT", "gpr_velocity": 0.1,
...                 "gpr_params": {"migrate": True, "velocity": 0.1}}, "migrate a.DZT")
({'gpr_file': 'a.DZT', 'gpr_params': {'migrate': True}}, ['The model read a radar velocity of 0.1 m/ns that the request does not state; it was not used.'])
>>> gpr_from_reply({"gpr_file": "a.DZT", "gpr_velocity": 0.09}, "a.DZT at 9 cm/ns")[0]
{'gpr_file': 'a.DZT', 'gpr_velocity': 0.09}

ERTLoaderAgent#

ERT Loader Agent

Specialized agent for loading and quality-checking ERT field data.

class PyHydroGeophysX.agents.ert_loader_agent.ERTLoaderAgent(api_key: str | None = None, model: str | None = None, llm_provider: str = 'openai')[source]#

Bases: BaseAgent

Agent specialized in loading ERT data from various instruments.

Uses PyHydroGeophysX data_processing module to load, validate, and prepare ERT data for inversion.

execute(input_data: Dict[str, Any]) → Dict[str, Any][source]#

Load and process ERT data.

Parameters:

input_data – Dictionary containing: - data_file: Path to ERT data file - instrument: Instrument type (E4D, Syscal, ABEM, etc.) - project_dir: Project directory - crs: Coordinate reference system (‘local’ or EPSG code) - quality_check: Whether to perform quality checks (default: True)

Returns:

Dictionary containing loaded ERT data and quality metrics

SeismicAgent#

Seismic Data Processing Agent

Specialized agent for processing seismic refraction data and extracting velocity structures. Supports standalone seismic refraction tomography (SRT) inversion workflows.

class PyHydroGeophysX.agents.seismic_agent.SeismicAgent(api_key: str | None = None, model: str | None = None, llm_provider: str = 'openai')[source]#

Bases: BaseAgent

Agent specialized in seismic refraction tomography (SRT) processing.

Uses PyGIMLI and PyHydroGeophysX seismic processing modules to invert seismic travel time data and extract velocity interfaces for structural constraints.

Supports two modes: - ‘inversion’: Load seismic data file and run SRT inversion - ‘interface’: Extract velocity interfaces from existing velocity model

Example (with your own travel-time file):
>>> agent = SeismicAgent()
>>> result = agent.execute({
...     'seismic_file': 'seismic_data.dat',
...     'velocity_threshold': 1200,
...     'output_dir': 'results/seismic'
... })
execute(input_data: Dict[str, Any]) → Dict[str, Any][source]#

Process seismic data and extract velocity structure.

Parameters:

input_data – Dictionary containing seismic_file or pre-loaded seismic_data (a path given as seismic_data is read as seismic_file), optional velocity thresholds, inversion parameters, an output directory, and an extract_interfaces flag. Supported inversion parameters include lam, zWeight, vTop, vBottom, paraDepth, paraMaxCellSize, and limits.

Returns:

Dictionary containing velocity model, mesh, interfaces, and visualizations

interfaces_from_model(mesh, velocity_model, thresholds, output_dir: str) → Dict[float, Dict[str, Any]][source]#

Trace each threshold velocity through the model, and save it as x, z.

pick_travel_times(input_data: Dict[str, Any]) → Dict[str, Any][source]#

First-arrival travel times picked from a raw SEG-Y file, for the inversion.

The picking half of execute(), on its own so a workflow can show it as a step and stop before the inversion when the picks are wrong. Takes raw_seismic_file, output_dir, geophone_file and topography_file, align_origin and first_break_params as execute() does; first_break_params['reciprocity_check'] (default True) leaves out shots whose times disagree with their reciprocals (reciprocal_shot_check()), and first_break_params['neighbour_check'] (default True) picks again the picks that disagree with the neighbouring shots’ at the same geophone (neighbour_shot_check()).

Returns the travel-time file and the picks (traveltime_file, first_break_picks_file, picks_figure, gathers_figure - shot gathers as wiggles with their picks - n_picks, dropped_shots, segy_metadata) with the geometry and picking warnings, or a failed result - with origin_mismatch when the coordinate file and the headers sit in two frames.

static survey_scaled_defaults(seismic_data) → Dict[str, Any][source]#

Inversion settings scaled to this survey, for any the caller does not give.

The fixed defaults (a 30 m deep mesh of 2 m cells, velocities from 500 m/s at the surface to 5000 m/s, bounded below at 300 m/s) describe a line hundreds of metres long over regolith. On an 11.5 m spread of 0.5 m geophones over soil with a 120 m/s surface layer they left the picks a median 4.9 ms from the model; scaled, 1.4 ms. Each setting is scaled only where the survey calls for it, so a long line keeps the fixed values:

  • paraDepth: 0.4 of the line’s length, at most 30 m;

  • paraMaxCellSize: two sensor spacings, at most 2 m;

  • vTop: the direct wave’s velocity (offsets of two spacings or less), at most 500 m/s;

  • vBottom: one and a half times the apparent velocity at the far offsets - about the refractor’s velocity over flat layers - at least twice vTop and at most 5000 m/s;

  • limits: from half the direct-wave velocity, at most 300 m/s, to 8000 m/s.

ClimateDataAgent#

Processing Agents#

ERTInversionAgent#

ERT Inversion Agent

Specialized agent for performing ERT inversion with optional structural constraints.

class PyHydroGeophysX.agents.ert_inversion_agent.ERTInversionAgent(api_key: str | None = None, model: str | None = None, llm_provider: str = 'openai')[source]#

Bases: BaseAgent

Agent specialized in ERT inversion.

Uses PyHydroGeophysX inversion module to perform resistivity inversion with optional structural constraints from seismic data.

execute(input_data: Dict[str, Any]) → Dict[str, Any][source]#

Perform ERT inversion (standard or time-lapse).

Parameters:

input_data –

Dictionary containing:

  • ert_data: Loaded ERT data (for standard inversion)

  • inversion_mode: ‘standard’ or ‘time-lapse’

  • time_lapse_data: List of ERT datasets (for time-lapse)

  • time_lapse_method: recorded only; the solver implements one scheme (see PyHydroGeophysX.agents._method)

  • temporal_regularization: Temporal smoothing weight (for time-lapse)

  • inversion_params: Inversion parameters (lambda, max_iter, etc.)

  • use_structure_constraint: Whether to use seismic structure (default: False)

  • seismic_structure: Optional seismic structure data

  • output_dir: Directory for saving results

  • interpret: Ask the model to interpret the result (default: True, when there is an API key). A quality loop that re-inverts passes False: it keeps one model of several.

Returns:

Dictionary containing inversion results

InversionEvaluationAgent#

Inversion Evaluation Agent

Specialized agent for evaluating ERT inversion quality and automatically adjusting regularization parameters to achieve optimal results.

class PyHydroGeophysX.agents.inversion_evaluation_agent.InversionEvaluationAgent(api_key: str | None = None, model: str | None = None, llm_provider: str = 'openai')[source]#

Bases: BaseAgent

Agent specialized in evaluating inversion quality and optimizing parameters.

This agent: 1. Evaluates inversion results using multiple quality metrics 2. Determines if results are acceptable 3. Automatically adjusts regularization parameters if needed 4. Triggers re-inversion with improved parameters

execute(input_data: Dict[str, Any]) → Dict[str, Any][source]#

Evaluate inversion results and adjust parameters if needed.

Parameters:

input_data –

Dictionary containing:

  • inversion_results: Results from ERTInversionAgent

  • ert_data: Original ERT data

  • inversion_params: Current inversion parameters

  • time_lapse_data: List of ERT datasets (for time-lapse)

  • inversion_mode: ‘standard’ or ‘time-lapse’

  • auto_adjust: Whether to automatically adjust and re-run (default: True)

  • max_attempts: Maximum total evaluations, including the initial result (default: 3, allowing at most two re-inversions)

  • quality_threshold: Overall quality threshold (default: 70)

  • progress_callback: Optional callback for transparent loop logs

  • custom_thresholds: Optional custom quality thresholds

Returns:

  • status: ‘success’, ‘needs_review’, or ‘failed’

  • quality_score: Overall quality score (0-100)

  • quality_metrics: Detailed quality metrics

  • recommendations: List of improvement recommendations

  • adjusted_params: Adjusted parameters (if auto_adjust=True)

  • final_results: Best inversion results

  • evaluation_history: History of all attempts

Return type:

Dictionary containing

TDEMAgent#

TDEM (Time-Domain Electromagnetic) Agent

Agent for processing Time-Domain Electromagnetic data using SimPEG. Supports forward modeling, inversion, and integration with hydrological models.

class PyHydroGeophysX.agents.tdem_agent.TDEMAgent(api_key: str | None = None, model: str | None = None, llm_provider: str = 'openai')[source]#

Bases: BaseAgent

Agent for Time-Domain Electromagnetic (TDEM) data processing.

This agent provides functionality for: - Loading TDEM sounding data from text files - Forward modeling from hydrological models (MODFLOW, ParFlow) - 1D TDEM inversion with L2 and sparse (IRLS) regularization - Petrophysical conversion between water content and conductivity - Visualization and reporting

Example (with your own sounding file):
>>> agent = TDEMAgent()
>>> result = agent.execute({
...     'data_file': 'tdem_data.txt',
...     'source_radius': 10.0,
...     'n_layers': 20,
...     'output_dir': 'results/tdem'
... })
UNCERTAINTY_NAMES = ('unc', 'err', 'std', 'noise', 'sigma')#

Header names that mark a column as an uncertainty rather than a sounding.

execute(input_data: Dict[str, Any]) → Dict[str, Any][source]#

Execute TDEM workflow based on input configuration.

Parameters:

input_data –

Dictionary containing: - data_file: Path to TDEM data file (optional if forward modeling) - mode: ‘inversion’, ‘forward’, or ‘hydro_to_tdem’ - source_radius: Loop radius in meters (default: 10) - n_layers: Number of layers for inversion (default: 20) - output_dir: Output directory for results - use_irls: Use sparse regularization (default: True)

For forward modeling: - thicknesses: Layer thicknesses (m) - conductivity: Layer conductivities (S/m)

For hydro_to_tdem: - water_content: Water content array - porosity: Porosity array - layer_thicknesses: Layer thicknesses (m) - petrophysical_params: Petrophysical parameters

Returns:

Dictionary containing results based on mode

DataFusionAgent#

Data Fusion Agent

Intelligent coordinator for multi-method geophysical workflows. This agent understands which geophysical methods should work together and orchestrates complex data fusion workflows like seismic-constrained ERT inversion.

The DataFusionAgent is designed to be extensible for future multi-method combinations.

class PyHydroGeophysX.agents.data_fusion_agent.DataFusionAgent(api_key: str | None = None, model: str | None = None, llm_provider: str = 'openai')[source]#

Bases: BaseAgent

Agent for intelligent coordination of multi-method geophysical workflows.

This agent understands common geophysical data fusion patterns and can recommend and execute appropriate multi-method workflows based on user requirements.

Supported Fusion Patterns:
  • ERT + Seismic: Structure-constrained resistivity inversion

  • ERT + Gravity: Density-constrained models

  • Multiple Time-Lapse: Joint temporal inversion

  • (Extensible for future methods)

FUSION_PATTERNS = {'full_integration': {'benefits': 'Complete geological-to-hydrological workflow with constraints', 'description': 'Structure-constrained ERT with hydrological conversion', 'methods': ['seismic', 'ert', 'petrophysics'], 'workflow': ['seismic_inversion', 'interface_extraction', 'constrained_ert', 'petrophysics_conversion']}, 'petrophysics_integration': {'benefits': 'Direct hydrological interpretation from geophysical data', 'description': 'Convert resistivity to hydrological properties', 'methods': ['ert', 'petrophysics'], 'workflow': ['ert_inversion', 'petrophysics_conversion']}, 'structure_constraint': {'benefits': 'Improved layer boundary resolution and reduced artifacts', 'description': 'Use seismic velocity interfaces to constrain ERT inversion', 'methods': ['seismic', 'ert'], 'workflow': ['seismic_inversion', 'interface_extraction', 'constrained_ert']}}#
execute(input_data: Dict[str, Any]) → Dict[str, Any][source]#

Execute multi-method data fusion workflow.

Parameters:

input_data – Dictionary containing: - fusion_pattern: Name of fusion pattern or ‘auto’ for LLM recommendation - methods: List of available methods (e.g., [‘seismic’, ‘ert’]) - workflow_config: Configuration for the fusion workflow - data: Dictionary of data for each method - output_dir: Directory for saving results

Returns:

Dictionary containing fused results and workflow metadata

execute_full_workflow(input_data: Dict[str, Any]) → Dict[str, Any][source]#

Execute the complete multi-method data fusion workflow, not just planning.

This method actually runs the agents and produces results, unlike execute() which only creates a plan.

Parameters:

input_data – Dictionary containing: - fusion_pattern: Name of fusion pattern - methods: List of available methods - workflow_config: Configuration for the fusion workflow - data: Dictionary of data for each method - output_dir: Directory for saving results

Returns:

Dictionary containing complete workflow results

get_available_patterns() → Dict[str, Dict][source]#

Get information about all available fusion patterns.

Returns:

Dictionary of fusion patterns with descriptions

validate_workflow(available_methods: List[str], desired_pattern: str) → Dict[str, Any][source]#

Validate if a desired fusion pattern can be executed with available methods.

Parameters:
  • available_methods – List of available methods

  • desired_pattern – Desired fusion pattern name

Returns:

Validation result dictionary

StructureConstraintAgent#

Structure Constraint Agent

Applies seismic velocity interfaces as structural constraints to ERT inversion. Implements the workflow from Ex_Structure_resinv.py for creating structure-constrained resistivity models.

class PyHydroGeophysX.agents.structure_constraint_agent.StructureConstraintAgent(api_key: str | None = None, model: str | None = None, llm_provider: str = 'openai')[source]#

Bases: BaseAgent

Agent for applying structural constraints from seismic data to ERT inversion.

This agent creates ERT meshes that honor geological boundaries derived from seismic velocity interfaces, leading to more accurate resistivity models that preserve sharp layer contrasts.

execute(input_data: Dict[str, Any]) → Dict[str, Any][source]#

Execute structure-constrained ERT inversion.

Parameters:

input_data – Dictionary containing: - ert_data: ERT measurement data - seismic_data: (Optional) Seismic traveltime data for interface extraction - interface_coords: (Optional) Tuple of (x, z) coordinates from seismic - velocity_threshold: (Optional) Velocity threshold for interface extraction - seismic_params: (Optional) Parameters for seismic inversion - inversion_params: ERT inversion parameters - output_dir: Directory for saving results - mesh_quality: Mesh quality parameter (default: 31)

Returns:

Dictionary containing constrained resistivity model and mesh

Conversion Agents#

PetrophysicsAgent#

Petrophysics Agent

Converts resistivity models to hydrological properties (water content, saturation, porosity) using structure-constrained petrophysical models with Monte Carlo uncertainty quantification. Implements the workflow from Ex_MC_Hydro.py.

PyHydroGeophysX.agents.petrophysics_agent.LAYER_RANGE_KEYS = (('rho_sat_range', 'rho_sat'), ('n_range', 'n'), ('porosity_range', 'porosity'))#

The per-layer ranges a request can give, and the parameter each one sets.

class PyHydroGeophysX.agents.petrophysics_agent.PetrophysicsAgent(api_key: str | None = None, model: str | None = None, llm_provider: str = 'openai')[source]#

Bases: BaseAgent

Agent for converting resistivity to hydrological properties with uncertainty.

This agent uses Archie’s law and modified petrophysical models to convert resistivity to water content, incorporating: - Layer-specific parameters from structural constraints - Monte Carlo uncertainty quantification - Surface conductivity effects in clay-rich materials

DEFAULT_LAYER_PARAMS = {'bedrock': {'m': {'mean': 1.9, 'std': 0.2}, 'n': {'mean': 1.7, 'std': 0.2}, 'porosity': {'mean': 0.25, 'std': 0.15}, 'rho_fluid': 20.0, 'sigma_sur': {'mean': 0.0, 'std': 0.0}}, 'regolith': {'m': {'mean': 1.3, 'std': 0.1}, 'n': {'mean': 2.1, 'std': 0.1}, 'porosity': {'mean': 0.42, 'std': 0.05}, 'rho_fluid': 20.0, 'sigma_sur': {'mean': 0.005, 'std': 0.005}}}#
execute(input_data: Dict[str, Any]) → Dict[str, Any][source]#

Convert resistivity to water content with uncertainty quantification.

Parameters:

input_data – Dictionary containing: - resistivity_model: Resistivity values (can be 1D or 2D for time-lapse) - mesh: PyGIMLI mesh with cell markers - cell_markers: Array identifying geological layers - layer_params: Dictionary of parameters for each layer (optional) - n_realizations: Number of Monte Carlo samples (default: 100) - output_dir: Directory for saving results

Returns:

Dictionary containing water content statistics and uncertainty

WaterContentAgent#

Water Content Conversion Agent

Specialized agent for converting resistivity to water content using petrophysical models.

class PyHydroGeophysX.agents.water_content_agent.WaterContentAgent(api_key: str | None = None, model: str | None = None, llm_provider: str = 'openai')[source]#

Bases: BaseAgent

Agent specialized in converting resistivity to water content.

Uses PyHydroGeophysX petrophysical models and Monte Carlo uncertainty quantification to estimate water content from resistivity.

execute(input_data: Dict[str, Any]) → Dict[str, Any][source]#

Convert resistivity to water content.

Parameters:

input_data – Dictionary containing: - inversion_results: ERT inversion results - petrophysical_params: Parameters for each layer (rhos, n, porosity, etc.) - uncertainty_analysis: Whether to run Monte Carlo (default: False) - n_realizations: Number of MC realizations (default: 100) - output_dir: Directory for saving results

Returns:

Dictionary containing water content estimates and uncertainties

Output Agents#

ReportAgent#