Read, write, analyze and visualize IWFM (Integrated Water Flow Model) models: pure-Python file I/O, DLL wrapper, and 58 plot functions
Project description
iwfm-io
Python file I/O, DLL wrapper, and visualization library for the Integrated Water Flow Model (IWFM).
๐ Example plot gallery โ all 58 plot functions rendered from DWR's C2VSimFG v1.5 Central Valley model.
โ๏ธ How does this compare to PyWFM and cfbrush/iwfm? โ a factual feature comparison of the IWFM Python packages.
Features
iwfm_ioโ Pure-Python file I/O (no DLL, cross-platform):- Read and write all IWFM text input files (preprocessor, simulation, groundwater, stream, lake, root zone, time series)
- Read IWFM HDF5 output files (budgets, heads, hydrographs, zone budgets)
- Read IWFM text output files (hydrographs, final states, flow files, budget text)
IOModelAdapterpresents the same DataFrame API as the DLL wrapper, so plot functions work without the DLL- Model comparison:
compare_models()reports what changed between two model versions (checksum file diff + grid + head/budget statistics);head_difference()/budget_difference()return alignedB โ ADataFrames - Scenario builder:
create_scenario()copies a model and applies input changes (set_keyed_value,replace_text, or your own functions)
- Run models from Python (Windows):
iwfm_io.run_model()drives the PreProcessor โ Simulation โ Budget โ ZBudget executables with error detection โ the full loop iscreate_scenario()โrun_model()โcompare_models() - Python ctypes wrapper for IWFM DLL โ Windows x64 only (8 modules)
- 58 plotting functions across 13 modules โ matplotlib PNGs by default, and key plots (Sankey, budget time series/pie/bars, hydrographs, butterfly) accept
engine="plotly"for interactive HTML with hover, zoom, and range sliders (pip install iwfm-io[viz]):- Maps (11 functions) โ Grid, heads, streams, wells, lakes, tile drains
- Profiles (2 functions) โ Cross-sections, longitudinal profiles
- Time Series (7 functions) โ Hydrographs, budgets, land use
- Trends (4 functions) โ Long-term trends, seasonal patterns, drought analysis
- Seasonal (4 functions) โ Ridgelines, heatmaps, polar plots
- Spatial Patterns (3 functions) โ Sparklines, small multiples, scatter plots
- Summary (7 functions) โ Rating curves, histograms, pie charts, water balance
- Water Balance (5 functions) โ Sankey diagrams, butterfly charts, cumulative departure
- Animations (3 functions) โ GIF animations of heads, flows, depth to water
- Subsidence (2 functions) โ Subsidence bowls and correlations
- Supply/Demand (4 functions) โ Gap analysis, shortage plots
- Cross Sections (2 functions) โ Multi-layer panels, animations
- Connectivity (2 functions) โ Diversion networks, bypass diagrams
- Built on matplotlib, numpy, pandas, geopandas, h5py
Installation
Prerequisites
-
Python 3.8 or higher
-
For
iwfm_ioand plotting: any OS (plots work DLL-free viaIOModelAdapter) -
For the DLL wrapper: Windows 10+ x64 and a copy of
IWFM_C_x64.dll. One line fetches an official build (GPLv2, published with its corresponding source on this project's GitHub releases):import iwfm_io iwfm_io.dll.download_dll("2025.0.1747") # โ ~/.iwfm/dlls/2025.0.1747/IWFM_C_x64.dll
Published builds (sha256-verified):
2025.0.1747,2025.0.1688,2024.2.1594(C2VSimFG v1.5),2015.3.1443,2015.1.1273,2015.0.1403โ all official DWR builds from the CNRA Open Data release archive. The DLL is version-sensitive: match it to your model's IWFM version. Other builds ship with IWFM from the DWR IWFM site โ place them indlls/<version>/or~/.iwfm/dlls/<version>/and select withload_dll(version=...)/IWFMModel(..., dll_version=...).
Install
pip install iwfm-io # core: file I/O, DLL wrapper, plotting
pip install iwfm-io[geo] # + geopandas/shapely for GeoDataFrame output
pip install iwfm-io[viz] # + plotly/kaleido for interactive Sankey diagrams
Without the geo extra, spatial tables are returned as plain pandas
DataFrames instead of GeoDataFrames โ everything else works the same.
Install from Source (Development Mode)
cd iwfm-io
pip install -e .
Quick Start
Open a Model (no DLL required)
Point open_model at the model folder โ the main input files and all
HDF5 results are found automatically:
from iwfm_io import open_model
model = open_model(".assets/sample_model")
print(model.describe()) # what does this model contain?
model.nodes_df() # grid nodes (GeoDataFrame)
model.heads_df(layer=1) # simulated heads, one column per node
model.budget_df("GW", location=1) # groundwater budget time series
from iwfm_io.plots import maps
fig, ax = maps.plot_gw_head_contour(model, layer=1)
Read Individual Files
from iwfm_io import read_preprocessor, read_simulation
pp = read_preprocessor(".assets/sample_model/Preprocessor/PreProcessor_MAIN.IN")
pp.nodes # GeoDataFrame: node_id, x, y, geometry
pp.elements # GeoDataFrame: element_id, node1-4, subregion, geometry
pp.stratigraphy # DataFrame: elevation, layer thicknesses
sim = read_simulation(".assets/sample_model/Simulation/Simulation_MAIN.IN")
print(f"{len(pp.nodes)} nodes, sim runs {sim.sim_begin} โ {sim.sim_end}")
# Or any file directly, without going through the main file
from iwfm_io import read_budget_hdf, read_head_hdf
gw_bud = read_budget_hdf(".assets/sample_model/Results/GW.hdf")
head_df = read_head_hdf(".assets/sample_model/Results/GWHeadAll.hdf", n_nodes=441, n_layers=2)
See examples/01_read_inputs.py for a complete walkthrough of all input file readers, and docs/agents.md for compact recipes aimed at scripts and AI agents.
Using the DLL Wrapper (Windows only)
import iwfm_io
with iwfm_io.dll.IWFMModel(
preprocessor_file=".assets/sample_model/Preprocessor/PreProcessor_MAIN.IN",
simulation_file=".assets/sample_model/Simulation/Simulation_MAIN.IN",
is_for_inquiry=True,
) as model:
x, y = model.get_node_coordinates()
print(f"{model.n_nodes} nodes, {model.n_elements} elements")
Run a Scenario (Windows)
from iwfm_io import run_model
from iwfm_io import create_scenario, set_keyed_value, compare_models
scenario = create_scenario(
"runs/baseline", "runs/short_run",
changes=[set_keyed_value("Simulation/Simulation_MAIN.IN",
"EDT", "09/30/1995_24:00")],
)
run_model(scenario, steps=("preprocessor", "simulation", "budget"))
report = compare_models("runs/baseline", scenario)
Creating Plots
from iwfm_io.plots import maps, timeseries
# Works with IWFMModel or IOModelAdapter
maps.plot_stream_network(model_or_adapter)
timeseries.plot_gw_head_hydrographs(
model_or_adapter, node_indices=[1, 50, 100], layer=1,
begin_date="10/01/1990_24:00", end_date="09/30/2000_24:00",
)
Examples
| File | Requires | Description |
|---|---|---|
examples/01_read_inputs.py |
.assets/sample_model | Reading all IWFM input files via iwfm_io |
examples/02_read_outputs.py |
.assets/sample_model/Results | Reading HDF5 and text output files |
examples/03_roundtrip.py |
.assets/sample_model | Read โ modify โ write input files |
examples/04_dll_wrapper.py |
Windows + DLL | IWFMModel, IWFMBudget, IWFMZBudget |
examples/05_plotting.py |
.assets/sample_model | Plotting gallery โ all 13 modules |
examples/06_multi_run_budgets.py |
.assets/sample_model/Results | Multi-run unified budget DataFrame |
examples/07_compare_models.py |
.assets/sample_model | File diff + comparison report between model versions |
examples/08_run_scenario.py |
Windows + sample_model/Bin | Full loop: create scenario โ run IWFM โ compare |
examples/09_full_input_datasets.py |
.assets/sample_model | Every input dataset as a DataFrame; edit + write back |
examples/test_plots_dllfree.py |
.assets/sample_model (any OS) | The nine formerly DLL-only plots via IOModelAdapter |
Claude Code Skill (analyze models by chatting)
skills/iwfm-analyst/ is a Claude Code
skill that lets non-programmers analyze IWFM models conversationally โ
"show me the groundwater budget for subregion 5", "map depth to water",
"compare these two runs" โ with Claude doing the iwfm-io work and
returning tables and plot images. Install by copying it into your
personal skills folder:
# Windows
Copy-Item skills\iwfm-analyst "$env:USERPROFILE\.claude\skills\" -Recurse
# macOS / Linux
cp -r skills/iwfm-analyst ~/.claude/skills/
Then start Claude Code anywhere and ask about your model (have
pip install iwfm-io available, or let Claude install it).
Testing
# Pure-Python I/O test suite (pytest, no DLL required)
pytest tests/
# Full 58-function plot test suite (requires DLL + .assets/sample_model/)
python examples/test_plots.py
# Output goes to test_output/
See docs/TEST_PLOTS_RESULTS.md for detailed plot-test results: 48 of 58 pass through the DLL in inquiry mode (all failures are DLL/inquiry-mode limitations, not wrapper bugs), and every failing function also renders DLL-free through IOModelAdapter โ run python examples/test_plots_dllfree.py to verify.
Project Structure
iwfm-io/
โโโ iwfm_io/ # Python package (pure-Python I/O at top level)
โ โโโ _tokens.py # Date/line parsing primitives
โ โโโ _parser.py # IWFMFileReader
โ โโโ _writer.py # IWFMFileWriter
โ โโโ _validation.py # Cross-file consistency checks
โ โโโ model_adapter.py # IOModelAdapter (DLL-free DataFrame API)
โ โโโ scenario.py / run.py / compare.py / collect.py
โ โโโ models/ # Dataclasses for each subsystem
โ โโโ readers/ # read_* functions
โ โโโ writers/ # write_* functions
โ โโโ plots/ # 58 plot functions across 13 modules
โ โโโ dll/ # ctypes DLL wrapper (Windows only)
โ โโโ model.py # IWFMModel
โ โโโ budget.py / zbudget.py
โ โโโ _dll.py / _marshal.py / _errors.py
โโโ examples/
โ โโโ 01_read_inputs.py โฆ # Numbered examples 01โ09 (see table above)
โ โโโ test_plots.py # 58-function plot test suite
โโโ tests/
โ โโโ io/ # pytest suite for iwfm_io
โโโ .assets/
โ โโโ sample_model/ # Reference model (441 nodes, 400 elements)
โโโ dlls/ # Versioned DLL storage (dlls/<version>/IWFM_C_x64.dll)
โโโ docs/
The sample model (441 nodes, 400 elements โ used by the tests and examples)
is published as a release asset on the GitHub Releases page. Download
sample_model.zip and extract it to .assets/sample_model/. Tests skip
automatically when it is absent.
DLL Wrapper Architecture
The iwfm package wraps the IWFM C DLL using ctypes:
- STDCALL convention - All functions use WinDLL
- Fortran interfacing - All parameters passed by reference
- Column-major arrays - 2D arrays use Fortran order (
order='F') - Error handling - Status codes checked via
IW_GetLastMessage - String marshaling - Fortran character arrays with length parameters
DLL Exports Wrapped:
- ~161 Model functions (
IW_Model_*) - Grid, flow, BC, pumping - 14 Budget functions (
IW_Budget_*) - Water budget analysis - 16 ZBudget functions (
IW_ZBudget_*) - Zone budget analysis - ~34 Misc functions (
IW_*) - Utilities, time conversion
Requirements
- numpy >= 1.20
- matplotlib >= 3.3
- pandas >= 1.2
- h5py >= 3.0
- Optional (
iwfm-io[geo]): geopandas >= 0.10, shapely >= 1.8
Known Limitations
DLL wrapper (Windows only):
- Some features require
is_for_inquiry=Falsewith a full simulation run - 12 of 58 plot tests fail on the sample model due to DLL inquiry-mode limitations (spurious duplicate-node error, partial instantiation) โ not wrapper bugs
iwfm_io (cross-platform):
IOModelAdapter.subsidence_df()returns an empty DataFrame (per-node subsidence exists only as DLL state; observation-point series are readable viaread_hydrograph_out)stream_flows_df()needs a stream node budget HDF in Results (returns empty otherwise);supply_demand_df()/land-use areas need the L&WU or RootZone budget HDF; aquifer parameters need a per-node (NGROUP=0) parameter block โ parametric-grid models require the DLL- HEC-DSS file reading is not supported (DSS pathnames are stored but not parsed)
- Binary
PreProcessor.binfiles cannot be read โ only the text input files
See docs/TEST_PLOTS_RESULTS.md for detailed plot-test results and known issues.
License
- iwfm-io code: Apache-2.0 (see
LICENSEandNOTICE) - IWFM itself and the DLL builds published as release assets: GPL-2.0, Copyright California Department of Water Resources (see
LICENSE-DLLS.md)
The split reflects who wrote what: the Python code is an independent work that reads IWFM's file formats and calls the DLL's C API, while the DLL release assets are unmodified redistributions of DWR's GPL-2.0 binaries, published with their corresponding source.
Credits
- IWFM: California Department of Water Resources
- iwfm-io: Python file I/O, DLL wrapper, and visualization toolkit (2026)
Version History
- v2.3.0 (2026-07-19) - Fixes all four issues from the first round of downstream feedback (#1โ#4). 2015-line DLL open failures are no longer silently swallowed (#1): the wrapper now detects the DLL generation from its export set and calls the matching 7-argument
IW_Model_Newโ previously the DLL wrote its status code into the model-id slot and a failed open returned a model reporting 0 nodes.read_budget_hdf(#2) now returns the file's native output interval ('interval'key, fromTimeStep%Unit) and lists locations in the file's native DLL order (recovered from theAttributes/cLocationNamesdataset) instead of h5py's alphabetical order โ this also fixes wrong ordering for numeric location names (NODE 1, 19, 8โNODE 1, 8, 19) and aligns per-location column metadata in files where the orders differ.read_head_all_out(#3) names columnsnode_<id>_layer_<L>from the file's header node IDs, mirroringread_head_hdf.load_dll(version=...)(#4) now downloads published builds on a cache miss via the existing sha256-verifieddownload_dllmachinery (download=Falserestores search-only behavior for air-gapped machines);IWFMModel/IWFMBudget/IWFMZBudgetinherit this when opened withdll_version=. - v2.2.0 (2026-07-14) - Relicensed iwfm-io code from GPL-2.0 to Apache-2.0. IWFM and the DLL builds distributed as release assets remain GPL-2.0 (DWR) โ see
LICENSE-DLLS.md. No code changes. - v2.1.0 (2026-07-10) - Every input dataset is now a DataFrame, and writers regenerate files entirely from them. Readers no longer stash unparsed sections as raw text: GW main aquifer parameters (per-node and parametric-grid layouts), Kh anomalies, return-flow specs and initial heads; subsidence parameters; tile-drain hydrograph controls; per-well pumping configuration; diversion specs incl. recharge zones and (old-format) spill locations; small watersheds; unsaturated zone; the root-zone soil table; plus new readers for the root-zone sub-components (non-ponded/ponded crops, urban, native vegetation) and the specified-flow / general-head / constrained general-head BC files. Pointer columns (
ic*,irn*,itscol*) are documented per dataclass with the file they reference. Writers rebuild every section from the parsed DataFrames โ verified against the real IWFM executables: the sample model reproduces baseline heads exactly from fully regenerated inputs (read โ write โ PreProcessor โ Simulation, max head difference 0.0).download_dll()now offers six official DWR builds (2015.0.1403 โ 2025.0.1747, incl. 2024.2.1594 used by C2VSimFG v1.5), sha256-verified from this project's releases. All examples repaired and a newexamples/09_full_input_datasets.pytours the parsed datasets. Fixed:validate_stratigraphyfalse positives; element-group parsing of zero-element recharge zones. - v2.0.0 (2026-07-09) - Import package renamed
iwfmโiwfm_ioto match the distribution name and coexist with other IWFM Python packages (cfbrush/iwfm, DWR's PyWFM). The pure-Python I/O layer moves to the top level and the DLL wrapper into an explicit subpackage โ migration:iwfm.io.Xโiwfm_io.X,iwfm.plotsโiwfm_io.plots,iwfm.IWFMModel/download_dll/load_dllโiwfm_io.dll.โฆ,iwfm.run_modelโiwfm_io.run_model. No functional changes. - v1.4.0 (2026-07-08) - Wells and diversions fully readable without the DLL: new
read_well_spec(well locations, screens, names, delivery element groups), complete diversion-spec parsing (all component column/fraction pairs incl. spills where the format has them, destination type/id resolved to delivery elements for group/subregion/element destinations, recharge zones with loss fractions), and element-group parsing shared across well specs, element pumping, and diversion specs.wells_df()anddiversions_df()onIOModelAdapterare now fully populated;plot_well_locationsandplot_diversion_network(with delivery arrows) render DLL-free. Robust to real-world file quirks (comments glued to numbers, name comments missing the leading slash). - v1.3.0 (2026-07-08) -
iwfm_io.dll.download_dll(version): one-line install of official IWFM DLL builds from the project's GitHub releases (sha256-verified, GPLv2 with corresponding source attached) into~/.iwfm/dlls/.IOModelAdapter.get_zbudget_timeseries(): DLL-free zone-budget time series (zones = subregions), soplot_zbudget_timeseriesworks without the DLL.examples/test_plots.pynow runs against any model root. New example plot gallery โ all 58 plot functions rendered from DWR's C2VSimFG v1.5. - v1.2.0 (2026-07-08) - DLL-free plotting for everything inquiry mode can't do:
IOModelAdapternow serves tile drains, bypasses, aquifer parameters (per-node NGROUP=0 blocks), supply requirement/shortage, land-use areas, and per-node streamโGW exchange from the model's input and budget-output files.open_model()follows the GW/stream mains to their child files. DLL wrapper hardening:get_hydrographmasks the DLL's invalid trailing dates and the uninitialized values that accompany them; stream-state getters raise a cleanIWFMErrorin inquiry mode instead of letting older DLL builds crash Python (root-cause analyses indocs/DLL_INQUIRY_MODE_LIMITS.md). Fixed component child-file path resolution (relative to the simulation folder, not the component file's folder). - v1.1.1 (2026-07-07) - Fix two budget HDF reader bugs found by validating against a full C2VSimFG v1.5 simulation run: budget column labels were shifted one column left of the data (the first data column was silently dropped as a supposed time marker), and monthly/annual output DatetimeIndexes drifted by using fixed 30-day steps instead of calendar months. All budget HDF users should upgrade.
- v1.1.0 (2026-07-07) -
open_model()one-call model opening,describe()model summaries, direct data properties on parsed files; scenario loop:create_scenario()input editing,run_model()executable driver (Windows),compare_models()/head_difference()/budget_difference()comparison tools, multi-run budget collection; readers validated against C2VSimFG v1.5 and handle IWFM 2024.x format variants (keyword-driven parsing); plotting library moved into the package (iwfm_io.plots), geopandas made optional, modern packaging (pyproject.toml) - v1.0 (2026-02-15) - Initial release with full plotting library and DLL wrapper
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file iwfm_io-2.3.0.tar.gz.
File metadata
- Download URL: iwfm_io-2.3.0.tar.gz
- Upload date:
- Size: 213.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
75ca5f4b1ca12afdca3c2b41e5d977cd1aa71cb20f8f1cc2fd2f0bb28c9b8037
|
|
| MD5 |
8b50cc9ed9971822ed69bcacaf2c099d
|
|
| BLAKE2b-256 |
c09e4d982d398c69e45a9c4065b5a6c35f89413850e471f571c6f92bc371ae8b
|
File details
Details for the file iwfm_io-2.3.0-py3-none-any.whl.
File metadata
- Download URL: iwfm_io-2.3.0-py3-none-any.whl
- Upload date:
- Size: 237.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
94ea1b0578299362ffac8078ea33e5bb26f4566b9c501417fe2c8996fd7ab486
|
|
| MD5 |
d3969799267646d600cf82ffce7f04e9
|
|
| BLAKE2b-256 |
a5fd13d2da393f1b25c3ac0f07bdc982105d731f720bda511ff640c6a6d92c41
|