Roundtrip and CLI Modules#

The roundtrip modules provide read-write-read verification pipelines, run script generation, model packaging, and CLI subcommands for IWFM models.

Roundtrip Pipeline#

The pipeline module orchestrates a full read-write-read verification cycle: load a model, write it to a new directory, optionally run both versions, and compare results.

Main roundtrip testing pipeline orchestrator.

class pyiwfm.roundtrip.pipeline.StepResult(name='', success=False, message='', data=None, error='')[source]#

Bases: object

Result of a single pipeline step.

Variables:
  • name (str) – Step name.

  • success (bool) – Whether the step completed successfully.

  • message (str) – Human-readable status message.

  • data (Any) – Step-specific result data.

  • error (str) – Error message if the step failed.

name: str = ''#
success: bool = False#
message: str = ''#
data: Any = None#
error: str = ''#
__init__(name='', success=False, message='', data=None, error='')#
class pyiwfm.roundtrip.pipeline.RunPairResult(preprocessor=None, simulation=None)[source]#

Bases: object

Result of running preprocessor + simulation.

Variables:
  • preprocessor (PreprocessorResult | None) – Result from preprocessing.

  • simulation (SimulationResult | None) – Result from simulation.

preprocessor: PreprocessorResult | None = None#
simulation: SimulationResult | None = None#
property success: bool#

True if both preprocessor and simulation succeeded.

__init__(preprocessor=None, simulation=None)#
class pyiwfm.roundtrip.pipeline.RoundtripResult(steps=<factory>, baseline_run=None, written_run=None, input_diff=None, results_comparison=None)[source]#

Bases: object

Aggregate result of the full roundtrip pipeline.

Variables:
  • steps (dict[str, StepResult]) – Results for each pipeline step.

  • baseline_run (RunPairResult | None) – Result of running the original model.

  • written_run (RunPairResult | None) – Result of running the written model.

  • input_diff (InputDiffResult | None) – Input file comparison result.

  • results_comparison (Any) – Simulation output comparison result.

steps: dict[str, StepResult]#
baseline_run: RunPairResult | None = None#
written_run: RunPairResult | None = None#
input_diff: InputDiffResult | None = None#
results_comparison: Any = None#
property success: bool#

True if all executed steps succeeded.

summary()[source]#

Generate a human-readable summary of all steps.

__init__(steps=<factory>, baseline_run=None, written_run=None, input_diff=None, results_comparison=None)#
class pyiwfm.roundtrip.pipeline.RoundtripPipeline(config)[source]#

Bases: object

Orchestrates the full roundtrip test: load -> write -> run -> verify.

Parameters:

config (RoundtripConfig) – Pipeline configuration.

__init__(config)[source]#
step_load()[source]#

Load the model via IWFMModel.from_simulation_with_preprocessor().

step_write()[source]#

Write the model using CompleteModelWriter.

step_place_executables()[source]#

Find/download executables and place them in model directories.

step_generate_scripts()[source]#

Generate .bat/.sh run scripts for model directories.

step_diff_inputs()[source]#

Compare written input files against originals.

step_run_baseline()[source]#

Copy original model to temp dir and run PP + Sim.

step_run_written()[source]#

Run PP + Sim on the written model.

step_compare_results()[source]#

Compare simulation outputs between baseline and written runs.

run()[source]#

Execute the full pipeline: load -> write -> run -> verify.

Returns:

Aggregate result with all step outcomes.

Return type:

RoundtripResult

Roundtrip Configuration#

Configuration dataclass for the roundtrip pipeline.

Configuration for the roundtrip testing pipeline.

class pyiwfm.roundtrip.config.RoundtripConfig(source_model_dir=<factory>, simulation_main_file='', preprocessor_main_file='', output_dir=<factory>, executable_manager=None, run_baseline=True, run_written=True, compare_results=True, preprocessor_timeout=300, simulation_timeout=3600, head_atol=0.01, budget_rtol=0.001)[source]#

Bases: object

Configuration for a roundtrip test run.

Parameters:
  • source_model_dir (Path) – Root directory of the source IWFM model.

  • simulation_main_file (str) – Relative path to the Simulation main file within the model dir.

  • preprocessor_main_file (str) – Relative path to the PreProcessor main file within the model dir.

  • output_dir (Path) – Directory for written model and test outputs.

  • executable_manager (IWFMExecutableManager | None) – Manager for finding/downloading executables. Created automatically if not provided.

  • run_baseline (bool) – Whether to run the original model for comparison.

  • run_written (bool) – Whether to run the written (roundtripped) model.

  • compare_results (bool) – Whether to compare simulation results between baseline and written.

  • preprocessor_timeout (float) – Timeout in seconds for preprocessor runs.

  • simulation_timeout (float) – Timeout in seconds for simulation runs.

  • head_atol (float) – Absolute tolerance for head comparisons (ft).

  • budget_rtol (float) – Relative tolerance for budget comparisons.

source_model_dir: Path#
simulation_main_file: str = ''#
preprocessor_main_file: str = ''#
output_dir: Path#
executable_manager: IWFMExecutableManager | None = None#
run_baseline: bool = True#
run_written: bool = True#
compare_results: bool = True#
preprocessor_timeout: float = 300#
simulation_timeout: float = 3600#
head_atol: float = 0.01#
budget_rtol: float = 0.001#
__post_init__()[source]#

Convert strings to Path objects.

classmethod from_env()[source]#

Create config from environment variables.

Reads: - IWFM_MODEL_DIR: source model directory - IWFM_ROUNDTRIP_OUTPUT: output directory - IWFM_BIN: path to executables

Returns:

Configuration populated from environment.

Return type:

RoundtripConfig

classmethod for_sample_model(iwfm_dir)[source]#

Create config for the IWFM Sample Model.

Parameters:

iwfm_dir (Path | str) – Root directory containing the sample model.

Returns:

Configuration for sample model roundtrip test.

Return type:

RoundtripConfig

classmethod for_c2vsimfg(c2vsimfg_dir)[source]#

Create config for the C2VSimFG model.

Parameters:

c2vsimfg_dir (Path | str) – Root directory of the C2VSimFG model.

Returns:

Configuration for C2VSimFG roundtrip test.

Return type:

RoundtripConfig

classmethod for_c2vsimcg(c2vsimcg_dir)[source]#

Create config for the C2VSimCG (Coarse Grid) model.

Parameters:

c2vsimcg_dir (Path | str) – Root directory of the C2VSimCG model.

Returns:

Configuration for C2VSimCG roundtrip test.

Return type:

RoundtripConfig

__init__(source_model_dir=<factory>, simulation_main_file='', preprocessor_main_file='', output_dir=<factory>, executable_manager=None, run_baseline=True, run_written=True, compare_results=True, preprocessor_timeout=300, simulation_timeout=3600, head_atol=0.01, budget_rtol=0.001)#

Run Script Generator#

Generates platform-appropriate run scripts (.bat, .ps1, .sh) for IWFM preprocessor, simulation, and optional Budget/ZBudget post-processors.

Generate platform-appropriate run scripts for IWFM models.

pyiwfm.roundtrip.script_generator.generate_run_scripts(model_dir, preprocessor_main, simulation_main, preprocessor_exe='PreProcessor_x64.exe', simulation_exe='Simulation_x64.exe', budget_exe=None, zbudget_exe=None, formats=None)[source]#

Generate run scripts for an IWFM model.

Creates scripts in the model directory for each requested format: - run_preprocessor (.bat/.ps1/.sh) - run_simulation (.bat/.ps1/.sh) - run_all (.bat/.ps1/.sh) - run_budget (.bat/.ps1/.sh) — if budget_exe is provided - run_zbudget (.bat/.ps1/.sh) — if zbudget_exe is provided

Parameters:
  • model_dir (Path) – Root directory of the model.

  • preprocessor_main (str) – Relative path from model_dir to the preprocessor main file.

  • simulation_main (str) – Relative path from model_dir to the simulation main file.

  • preprocessor_exe (str) – Name of the preprocessor executable.

  • simulation_exe (str) – Name of the simulation executable.

  • budget_exe (str | None) – Name of the Budget post-processor executable. When None (default), no budget run script is generated.

  • zbudget_exe (str | None) – Name of the ZBudget post-processor executable. When None (default), no zbudget run script is generated.

  • formats (list[str] | None) – Script formats to generate. Accepted values: "bat", "ps1", "sh". None selects the platform-appropriate default (["bat", "ps1"] on Windows, ["sh"] elsewhere).

Returns:

Paths to the generated scripts.

Return type:

list[Path]

Model Packager#

Packages an IWFM model directory into a distributable ZIP archive with an embedded manifest.json.

Package IWFM model directories into distributable ZIP archives.

class pyiwfm.io.model_packager.ModelPackageResult(archive_path, files_included=<factory>, total_size_bytes=0, manifest=<factory>)[source]

Bases: object

Result of a model packaging operation.

Variables:
  • archive_path (Path) – Path to the created ZIP archive.

  • files_included (list[Path]) – Absolute paths of all files that were added to the archive.

  • total_size_bytes (int) – Total uncompressed size of included files in bytes.

  • manifest (dict[str, str]) – Mapping of relative path (inside ZIP) to the file-type category.

archive_path: Path
files_included: list[Path]
total_size_bytes: int = 0
manifest: dict[str, str]
__init__(archive_path, files_included=<factory>, total_size_bytes=0, manifest=<factory>)
pyiwfm.io.model_packager.collect_model_files(model_dir, *, include_executables=False, include_results=False)[source]

Collect all model files from a directory tree.

Walks model_dir recursively and returns files relevant to an IWFM model, excluding output/cache directories by default.

Parameters:
  • model_dir (Path) – Root directory of the IWFM model.

  • include_executables (bool) – If True, include .exe, .dll, and .so files.

  • include_results (bool) – If True, include the Results/ directory and HDF5 output files.

Returns:

Sorted list of absolute paths to included files.

Return type:

list[Path]

pyiwfm.io.model_packager.package_model(model_dir, output_path=None, *, include_executables=False, include_results=False, compression=8, compresslevel=6)[source]

Package an IWFM model directory into a ZIP archive.

Creates a ZIP file preserving the directory structure (Preprocessor/, Simulation/, etc.) with a manifest.json embedded in the archive.

Parameters:
  • model_dir (Path) – Root directory of the IWFM model.

  • output_path (Path | None) – Path for the output ZIP file. If None, defaults to <model_dir_name>.zip in the parent of model_dir.

  • include_executables (bool) – If True, include .exe, .dll, and .so files.

  • include_results (bool) – If True, include the Results/ directory and HDF5 output files.

  • compression (int) – ZIP compression method (default ZIP_DEFLATED).

  • compresslevel (int) – Compression level (0-9, default 6).

Returns:

Result with archive path, file list, size, and manifest.

Return type:

ModelPackageResult

Raises:

FileNotFoundError – If model_dir does not exist or is not a directory.

Executable Manager#

Downloads, locates, and places IWFM executables (PreProcessor, Simulation, Budget, ZBudget) for model execution.

IWFM executable manager for finding, downloading, and placing executables.

class pyiwfm.runner.executables.IWFMExecutableManager(github_repo='', version='', config='Release', search_paths=<factory>)[source]#

Bases: object

Manage IWFM executable discovery and download.

Handles finding local executables, downloading from GitHub releases, placing executables into model directories, and verification.

Parameters:
  • github_repo (str) – GitHub repository in ‘owner/repo’ format.

  • version (str) – IWFM version tag to download.

  • config (str) – Build configuration: ‘Release’ or ‘Debug’.

  • search_paths (list[Path] | None) – Additional paths to search for local executables.

Examples

>>> mgr = IWFMExecutableManager()
>>> exes = mgr.find_or_download()
>>> print(exes.available)
['preprocessor', 'simulation', 'budget', 'zbudget']
github_repo: str = ''#
version: str = ''#
config: str = 'Release'#
search_paths: list[Path]#
__post_init__()[source]#

Apply defaults from environment variables.

find_or_download()[source]#

Try to find local executables, download if not found.

Returns:

Discovered or downloaded executables.

Return type:

IWFMExecutables

download_from_github(dest=None)[source]#

Download IWFM executables from a GitHub release.

Parameters:

dest (Path | None) – Destination directory. Defaults to ~/.pyiwfm/bin/{version}/.

Returns:

Downloaded executables.

Return type:

IWFMExecutables

Raises:

RuntimeError – If the download or extraction fails.

place_executables(exes, model_dir)[source]#

Copy executables into a model directory.

Parameters:
  • exes (IWFMExecutables) – Source executables.

  • model_dir (Path) – Target model directory.

Returns:

Mapping of executable name to placed path.

Return type:

dict[str, Path]

verify_executables(exes)[source]#

Verify that executables can run.

Attempts to run each executable with a quick invocation to check that it is a valid binary. On Windows, passing an empty stdin typically causes IWFM exes to print usage and exit.

Parameters:

exes (IWFMExecutables) – Executables to verify.

Returns:

Mapping of executable name to verification status.

Return type:

dict[str, bool]

__init__(github_repo='', version='', config='Release', search_paths=<factory>)#

Results Differ#

Compares simulation results between baseline and written model runs.

Compare simulation results between two IWFM model runs.

Provides the critical verification step for roundtrip testing: compares heads, budgets, and hydrographs between a baseline model run and a roundtripped (written) model run.

class pyiwfm.comparison.results_differ.HeadComparison(n_timesteps=0, n_nodes=0, max_abs_diff=0.0, mean_abs_diff=0.0, n_mismatched_timesteps=0, within_tolerance=False)[source]#

Bases: object

Result of comparing groundwater head outputs.

Variables:
  • n_timesteps (int) – Number of timesteps compared.

  • n_nodes (int) – Number of nodes compared.

  • max_abs_diff (float) – Maximum absolute difference across all nodes and timesteps.

  • mean_abs_diff (float) – Mean absolute difference.

  • n_mismatched_timesteps (int) – Number of timesteps that exceed tolerance.

  • within_tolerance (bool) – True if all timesteps are within the specified tolerance.

n_timesteps: int = 0#
n_nodes: int = 0#
max_abs_diff: float = 0.0#
mean_abs_diff: float = 0.0#
n_mismatched_timesteps: int = 0#
within_tolerance: bool = False#
__init__(n_timesteps=0, n_nodes=0, max_abs_diff=0.0, mean_abs_diff=0.0, n_mismatched_timesteps=0, within_tolerance=False)#
class pyiwfm.comparison.results_differ.BudgetComparison(name='', n_datasets=0, max_rel_diff=0.0, within_tolerance=False, details=<factory>)[source]#

Bases: object

Result of comparing a single budget file.

Variables:
  • name (str) – Budget file identifier.

  • n_datasets (int) – Number of datasets compared.

  • max_rel_diff (float) – Maximum relative difference.

  • within_tolerance (bool) – True if all datasets are within tolerance.

  • details (list[str]) – Per-dataset comparison notes.

name: str = ''#
n_datasets: int = 0#
max_rel_diff: float = 0.0#
within_tolerance: bool = False#
details: list[str]#
__init__(name='', n_datasets=0, max_rel_diff=0.0, within_tolerance=False, details=<factory>)#
class pyiwfm.comparison.results_differ.HydrographComparison(name='', n_locations=0, min_nse=0.0, mean_nse=0.0, n_poor_matches=0, within_tolerance=False)[source]#

Bases: object

Result of comparing hydrograph outputs.

Variables:
  • name (str) – Hydrograph file identifier.

  • n_locations (int) – Number of hydrograph locations compared.

  • min_nse (float) – Minimum Nash-Sutcliffe Efficiency across locations.

  • mean_nse (float) – Mean NSE across locations.

  • n_poor_matches (int) – Number of locations with NSE below threshold.

  • within_tolerance (bool) – True if all locations meet the NSE threshold.

name: str = ''#
n_locations: int = 0#
min_nse: float = 0.0#
mean_nse: float = 0.0#
n_poor_matches: int = 0#
within_tolerance: bool = False#
__init__(name='', n_locations=0, min_nse=0.0, mean_nse=0.0, n_poor_matches=0, within_tolerance=False)#
class pyiwfm.comparison.results_differ.ResultsComparison(head_comparison=None, budget_comparisons=<factory>, hydrograph_comparisons=<factory>, final_heads_match=None, errors=<factory>)[source]#

Bases: object

Aggregate comparison of all simulation outputs.

Variables:
  • head_comparison (HeadComparison | None) – Head comparison results.

  • budget_comparisons (list[BudgetComparison]) – Per-budget file comparisons.

  • hydrograph_comparisons (list[HydrographComparison]) – Per-hydrograph file comparisons.

  • final_heads_match (bool | None) – Whether final heads files match.

  • errors (list[str]) – Any errors encountered during comparison.

head_comparison: HeadComparison | None = None#
budget_comparisons: list[BudgetComparison]#
hydrograph_comparisons: list[HydrographComparison]#
final_heads_match: bool | None = None#
errors: list[str]#
property success: bool#

True if all comparisons pass.

summary()[source]#

Generate a human-readable summary.

__init__(head_comparison=None, budget_comparisons=<factory>, hydrograph_comparisons=<factory>, final_heads_match=None, errors=<factory>)#
class pyiwfm.comparison.results_differ.ResultsDiffer(baseline_dir, written_dir, head_atol=0.01, budget_rtol=0.001, nse_threshold=0.9999)[source]#

Bases: object

Compare simulation outputs between two model runs.

Parameters:
  • baseline_dir (Path) – Directory containing baseline model outputs.

  • written_dir (Path) – Directory containing written (roundtripped) model outputs.

  • head_atol (float) – Absolute tolerance for head comparisons (ft).

  • budget_rtol (float) – Relative tolerance for budget comparisons.

  • nse_threshold (float) – Minimum NSE for hydrograph comparisons.

__init__(baseline_dir, written_dir, head_atol=0.01, budget_rtol=0.001, nse_threshold=0.9999)[source]#
compare_all()[source]#

Run all available comparisons.

Returns:

Aggregate results.

Return type:

ResultsComparison

compare_heads_hdf5()[source]#

Compare GW head outputs from HDF5 files.

Loads timesteps one-at-a-time for memory efficiency.

Returns:

Comparison result, or None if no HDF5 head files found.

Return type:

HeadComparison | None

compare_heads_text()[source]#

Compare GW head outputs from text .out files.

Returns:

Comparison result, or None if no text head files found.

Return type:

HeadComparison | None

compare_final_heads()[source]#

Compare FinalGWHeads.dat files with float tolerance.

Returns:

True if they match, False if not, None if files not found.

Return type:

bool | None

compare_budgets()[source]#

Compare budget HDF5 files between runs.

Returns:

Per-file comparison results.

Return type:

list[BudgetComparison]

compare_hydrographs()[source]#

Compare hydrograph .out files between runs.

Returns:

Per-file comparison results.

Return type:

list[HydrographComparison]

CLI Subcommands#

Package Command#

The pyiwfm package subcommand packages a model directory into a ZIP archive.

CLI subcommand for packaging an IWFM model into a ZIP archive.

Usage:

pyiwfm package --model-dir ./model [--output model.zip]
                [--include-executables] [--include-results]
pyiwfm.cli.package.add_package_parser(subparsers)[source]#

Register the pyiwfm package subcommand.

pyiwfm.cli.package.run_package(args)[source]#

Execute model packaging.

Run Command#

The pyiwfm run subcommand generates run scripts and optionally downloads executables.

CLI subcommand for generating run scripts and optionally downloading executables.

Usage:

pyiwfm run --model-dir ./model [--download-executables] [--scripts-only]
           [--format bat] [--format ps1] [--format sh]
pyiwfm.cli.run.add_run_parser(subparsers)[source]#

Register the pyiwfm run subcommand.

pyiwfm.cli.run.run_run(args)[source]#

Execute the run subcommand.