pyiwfm
Python package for reading, writing, visualizing, and comparing IWFM (Integrated Water Flow Model) models.
Installation
# Basic installation (includes matplotlib, geopandas, shapely, h5py)
pip install pyiwfm
# With mesh generation (triangle, gmsh)
pip install "pyiwfm[mesh]"
# With VTK 3D export
pip install "pyiwfm[viz]"
# With web viewer (FastAPI + React + vtk.js + deck.gl)
pip install "pyiwfm[webapi]"
# With PEST++ integration (scipy)
pip install "pyiwfm[pest]"
# With all optional dependencies
pip install "pyiwfm[all]"
# Development (editable install with dev tools)
pip install -e ".[dev]"
Windows install pitfalls
These are environmental issues we've seen in the wild — none are pyiwfm bugs, but the fixes aren't always obvious.
1. Use a clean virtual environment. Layering [webapi] on top of
an interpreter that already has matplotlib / numpy / vtk installed
is the most common source of import errors on Windows. Start from a
fresh venv and install the extras you need in one shot:
py -3.13 -m venv .venv
.venv\Scripts\activate
pip install "pyiwfm[webapi]"
2. ImportError: cannot import name '_c_internal_utils' from
matplotlib. This is a known matplotlib-on-Windows install
corruption (often after antivirus or OneDrive interferes with a
.pyd write). Reinstall matplotlib:
pip install --force-reinstall matplotlib
3. WinError 206 The filename or extension is too long. Windows
limits paths to 260 characters by default. Models nested under
OneDrive-synced or deeply-named user directories can exceed this.
Two fixes:
- System-wide (recommended): enable Windows long-path support, then
reboot. Affects every tool on the system, not just pyiwfm:
reg add HKLM\SYSTEM\CurrentControlSet\Control\FileSystem /v LongPathsEnabled /t REG_DWORD /d 1 /f
- Per-model workaround: move the model to a shallower path such as
C:\iwfm\<model_name>, away from OneDrive-synced or deeply-nested user paths.
4. Install extras upfront, not incrementally. pip install pyiwfm followed later by pip install pyiwfm[webapi] should work
in theory, but the second resolver pass can leave half-installed
state if anything errors mid-stream (Windows AV, OneDrive latency).
Prefer a single pip install "pyiwfm[webapi]" in a fresh venv.
Budget Post-Processing
Export IWFM budget and zone budget results to Excel workbooks:
# Export budgets from a control file (one .xlsx per budget spec)
pyiwfm budget C2VSimFG_Budget_xlsx.in
# Export zone budgets from a control file
pyiwfm zbudget C2VSimFG_ZBudget_xlsx.in
Or use the Python API directly:
from pyiwfm.io import BudgetReader, budget_to_excel
reader = BudgetReader("GW_Budget.hdf")
budget_to_excel(
reader, "GW_Budget.xlsx",
volume_factor=2.29568e-05, volume_unit="AC.FT.",
)
Quick Start
from pyiwfm import AppGrid, Node, Element, Stratigraphy
import numpy as np
# Create a simple mesh
nodes = {
1: Node(id=1, x=0.0, y=0.0),
2: Node(id=2, x=100.0, y=0.0),
3: Node(id=3, x=100.0, y=100.0),
4: Node(id=4, x=0.0, y=100.0),
}
elements = {
1: Element(id=1, vertices=(1, 2, 3, 4), subregion=1),
}
grid = AppGrid(nodes=nodes, elements=elements)
grid.compute_areas()
grid.compute_connectivity()
print(f"Grid: {grid.n_nodes} nodes, {grid.n_elements} elements")
Model I/O Example
from pathlib import Path
from pyiwfm.io import load_complete_model, save_complete_model
# Load a complete IWFM model from simulation main file
model = load_complete_model("Simulation/Simulation.in")
print(f"Loaded model with {model.grid.n_nodes} nodes")
print(f"Groundwater wells: {len(model.groundwater.wells) if model.groundwater else 0}")
# Save model to new directory
save_complete_model(model, Path("output_model"))
# Write time series to HEC-DSS (bundled C library)
from pyiwfm.io.dss import DSSTimeSeriesWriter, DSSPathnameTemplate
template = DSSPathnameTemplate(a_part="IWFM", c_part="HEAD", e_part="1DAY")
with DSSTimeSeriesWriter("output.dss") as writer:
writer.write_timeseries(head_timeseries, template.make_pathname(location="WELL_1"))
Web Visualization
pyiwfm includes an interactive web viewer built with FastAPI (backend) and React + vtk.js + deck.gl (frontend). Launch it with:
# CRS is optional — defaults to C2VSimFG UTM Zone 10N for most models
pyiwfm viewer --model-dir /path/to/model
# Specify CRS explicitly if needed
pyiwfm viewer --model-dir /path/to/model --crs "+proj=utm +zone=10 +datum=NAD83 +units=us-ft +no_defs"
The viewer provides six tabs:
- Overview: Model summary, metadata, and mesh quality metrics
- 3D Mesh: Interactive vtk.js 3D rendering with layer visibility, cross-section slicing, stream network overlay, and z-exaggeration
- Results Map: deck.gl + MapLibre map showing head contours, drawdown with diverging color scale, subsidence surface, hydrograph locations, head statistics, and observation upload/comparison (CSV, TSV, SMP formats)
- Diagnostics: Convergence iteration charts, mass balance error timeseries, filterable message table
- Budgets: Plotly charts of water budget time series with location/column selection
- Z-Budgets: Zone budget visualization organized by spatial zones
Additional API endpoints:
- Data Export: CSV (heads, budgets, hydrographs), GeoJSON mesh, GeoPackage (multi-layer), and publication-quality matplotlib plots (PNG/SVG)
- Model Comparison: Load a second model and compare meshes/stratigraphy via the
ModelDifferengine - Head Statistics: Time-aggregated min/max/mean/std per node across all timesteps
The frontend is pre-built into src/pyiwfm/visualization/webapi/static/. To rebuild from source:
cd frontend && npm install && npm run build
Calibration Tools
pyiwfm provides calibration tools that mirror and extend IWFM's Fortran utilities (IWFM2OBS, CalcTypHyd):
# Explicit SMP mode: interpolate simulated heads to observation times
pyiwfm iwfm2obs --obs observed.smp --sim simulated.smp --output interp.smp
# Model discovery mode: auto-discover .out files from simulation main file
pyiwfm iwfm2obs --model C2VSimFG.in --obs-gw gw_obs.smp --output-gw gw_out.smp
# With multi-layer T-weighted averaging and PEST instruction file
pyiwfm iwfm2obs --model C2VSimFG.in \
--obs-gw gw_obs.smp --output-gw gw_out.smp \
--well-spec obs_wells.txt \
--multilayer-out GW_MultiLayer.out \
--multilayer-ins GWHMultiLayer.ins
# CalcTypHyd with Fortran config file (produces PEST .out/.ins files)
pyiwfm calctyphyd --config CalcTypeHyd.in
# Deduplicate per-layer SMP output (strip %N suffixes)
pyiwfm iwfm2obs --deduplicate-smp GW_OUT.smp --output GW_OUT_dedup.smp
Or use the Python API:
from pyiwfm.calibration import iwfm2obs_from_model, discover_hydrograph_files
# Auto-discover .out files and interpolate to observation times
results = iwfm2obs_from_model(
simulation_main_file="C2VSimFG.in",
obs_smp_paths={"gw": "GW_Obs.smp"},
output_paths={"gw": "GW_OUT.smp"},
)
PEST++ Calibration CLI
Set up, run, and analyze PEST++ calibration from the command line:
# Generate PEST++ files from an IWFM model
pyiwfm pest setup --model-dir /path/to/model --case-name c2vsim_cal
# Run PEST++ (requires pestpp-glm or pestpp-ies on PATH)
pyiwfm pest run --pst-file ./pest_setup/c2vsim_cal.pst --num-workers 8
# Analyze results
pyiwfm pest analyze --pst-file ./pest_setup/c2vsim_cal.pst --format json
Features
- Core Data Structures: Node, Element, Face, AppGrid, Stratigraphy, TimeSeries
- BaseComponent ABC: Common interface (
validate(),n_items) for all model components (groundwater, streams, lakes, root zone, small watersheds, unsaturated zone) - Complete Model I/O: Full roundtrip support for reading and writing IWFM models
- ASCII files (nodes, elements, stratigraphy, time series)
- Binary files (Fortran unformatted)
- HDF5 files (efficient large model storage)
- HEC-DSS 7 files (bundled C library)
- Budget Post-Processing: Parse IWFM budget/zbudget control files and export to Excel
- One sheet per location/zone with title lines, bold headers, and auto-fitted columns
- Unit conversion factors (FACTLTOU, FACTAROU, FACTVLOU) applied per column type
- CLI commands:
pyiwfm budgetandpyiwfm zbudget
- Component Writers: Write complete IWFM input files with shared
BaseComponentWriterConfig- Groundwater: wells, pumping, boundary conditions, aquifer parameters
- Streams: nodes, reaches, diversions, bypasses, rating curves
- Lakes: definitions, elements, rating curves, outflows
- Root Zone: crop types, soil parameters, land use
- Small Watersheds: watershed units, root zone/aquifer parameters
- Unsaturated Zone: element layers, soil moisture
- Simulation: main control file
- PreProcessor Integration: Load/save complete models from IWFM file structure
- Model Factory: Extracted construction helpers (reach building, coordinate resolution, parametric grids, binary loading) into
pyiwfm.core.model_factory - Mesh Generation: Triangle and Gmsh wrappers
- Calibration Tools: IWFM2OBS time interpolation with automatic model file discovery and Fortran-verified timestamp alignment, multi-layer T-weighted observation well processing (GW_MultiLayer.out + PEST .ins), fuzzy c-means well clustering, typical hydrograph computation (CalcTypHyd) with Fortran-format config file parsing and PEST output, and publication-quality calibration figures
- Visualization: GIS export (GeoPackage download), VTK 3D export, matplotlib plot generation (PNG/SVG), interactive web viewer with budget charts, head maps, hydrograph comparison, drawdown animation, and head statistics
- Model Comparison: Diff and comparison metrics, including web viewer comparison endpoint
- Drawdown Analysis: Compute drawdown relative to reference timesteps with per-node, per-element, max-map, and robust range calculations
- Stream Depletion: Compare baseline and pumping model runs to quantify stream flow depletion per reach
- Budget Checks: Mass balance sanity checks to detect timestep-level inflow/outflow/storage imbalance
- Mesh Quality: Element-level quality metrics (aspect ratio, skewness, min/max angle) with aggregate statistics
- Simulation Diagnostics: Parse SimulationMessages.out for convergence tracking, mass balance errors, and spatial hotspot analysis
- PEST++ CLI:
pyiwfm pest setup/run/analyzefor end-to-end calibration from the command line
Versioning
The package version is derived automatically from git tags using
hatch-vcs. There is no hardcoded version
string to maintain — pyiwfm.__version__, pyproject.toml metadata, and the
Sphinx docs all read from the same source.
| Scenario | Version produced |
|---|---|
On a tagged commit (v1.0.4) |
1.0.4 |
| 3 commits after a tag | 1.0.5.dev3+gabcdef1 |
| Uncommitted changes (dirty) | 1.0.5.dev3+gabcdef1.d20260228 |
Release workflow:
git tag -a v1.0.4 -m "Release v1.0.4"
git push && git push origin v1.0.4
# CI builds and publishes automatically
Building locally:
pip install -e ".[dev]" # editable install — version from git
python -m build # sdist + wheel — version baked into _version.py
python -c "import pyiwfm; print(pyiwfm.__version__)"
The auto-generated src/pyiwfm/_version.py is gitignored and should not be
committed.
License
GPL-2.0 - Same as IWFM
Metadata
Release files for pyiwfm 1.3.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| pyiwfm-1.3.0.tar.gz | 4.5 MB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| pyiwfm-1.3.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 8.1 MB
Release files / pyiwfm-1.3.0.tar.gz
| Download URL | pyiwfm-1.3.0.tar.gz |
|---|---|
| Size | 4.5 MB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
1cd579319489384f44083cbd0c6305ebcedb378480d8e3bef2d789dac4964d2e
|
|
BLAKE2b-256 checksum How to use checksums |
d32784a01deb2c06408e529b12a4f087c67a53c4abd32f3c4d56eed2b4e0e049
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Release files / pyiwfm-1.3.0-py3-none-any.whl
| Download URL | pyiwfm-1.3.0-py3-none-any.whl |
|---|---|
| Size | 3.6 MB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
d63495a2183f21a5726d25356ea1c0f422b9ec90b56ff0e51edb30dfa4a85fbc
|
|
BLAKE2b-256 checksum How to use checksums |
c9ee12ed0af786120f50c1652a3ce4db89bd84519ac5008415a40b1aa5b10b2d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|