iwfm-io
Python file I/O, DLL wrapper, and visualization library for the Integrated Water Flow Model (IWFM).
📓 Tutorial notebooks — twelve executed Jupyter notebooks covering every feature area, from reading files to PEST++ calibration, CalSim/HEC-DSS integration, and GIS/VTK exports.
📊 Example plot gallery — all 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() iwfm_io.pest— PEST(++) calibration support (pure Python;pyemuoptional viaiwfm-io[pest]):- One-call quickstart:
pest_setup_from_model(model_dir, obs, dest)goes from a model folder + observed heads straight to a runnable pestpp-ies template — also available from the command line asiwfm-io pest setup / agents / run / analyze(plusiwfm-io describe) - Observation-name codec, PESTPP-IES ensemble loader + diagnostics, residual/calibration statistics (bias, RMSE, R², NSE, KGE, phi) at ensemble scale
- SMP file I/O, sim-to-obs time matching (IWFM2OBS equivalent), budget-component and derived observations (head changes, vertical gradients, gauge accretion–depletion)
- Multi-layer transmissivity-weighted well observations, paired output/instruction-file writers, worker orchestration, parameter write-back
- Zone/group and pilot-point parameterization on the FE mesh, constrained reparameterization with Texture2Par hooks, and a
PestSetupbuilder that assembles the whole PEST interface - DataFrame-first well (
gwl_metadata) and stream-gauge (gauge_metadata) metadata suites: link metadata to IWFM hydrograph outputs by name, composite per-layer heads, extract per-gauge flow/stage series — no hand-maintained configuration files
- One-call quickstart:
- HEC-DSS + CalSim (
iwfm-io[dss], cross-platform viapydsstools): catalog and read DSS-6/DSS-7 time series, link gauges to CalSim channel arcs, and extract monthly channel flows (CFS or TAF) whose timestamps align with IWFM's24:00convention out of the box - GIS exports (
iwfm-io[geo]):export_gis(model, "model.gpkg")/model.to_gis(...)write the grid (nodes with stratigraphy, element polygons), dissolved subregions, stream network, lakes, tile drains, and wells to a GeoPackage or shapefiles — with optional per-node/per-element attribute joins (heads, depth to water, land use, …) and a user-supplied CRS; per-layer*_gdf()builders return GeoDataFrames for use in Python - VTK exports (no VTK library needed — pure numpy):
export_vtk(model, "model.vtu")/model.to_vtk(...)extrude the FE grid through the stratigraphy into a 3D layered mesh (wedges/hexahedra, aquitard gaps preserved) for ParaView, with vertical exaggeration and per-node/per-element data arrays;export_vtk_timeseries()writes a.pvdanimation of simulated heads and depth to water - Python ctypes wrapper for IWFM DLL — Windows x64 only (8 modules)
- 66 plotting functions across 15 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.9 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, pest
pip install iwfm-io[geo] # + geopandas/shapely for GeoDataFrame output
pip install iwfm-io[viz] # + plotly/kaleido for interactive Sankey diagrams
pip install iwfm-io[dss] # + pydsstools for HEC-DSS / CalSim reading
pip install iwfm-io[pest] # + pyemu (only needed for .jcb IES binaries)
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.validate_references() # cross-file pointer checks (empty = clean)
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)
Readers are strict: a truncated or malformed input file raises
IWFMParseError naming the file, section and line. Pass
open_model(path, strict=False) (or wrap calls in
iwfm_io.strict_mode(False)) to keep whatever parsed, with warnings.
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
Prefer notebooks? The notebooks/ folder holds twelve fully-executed Jupyter notebooks covering the same ground with narrative and rendered output — quickstart, every reader/writer, the DLL wrapper, scenario runs, plotting, the complete PEST++/CalSim calibration workflow, and GIS/VTK exports.
| 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/10_pest_calibration.py |
.assets/sample_model/Results | One-call PEST++ setup (pest_setup_from_model) + extract-step demo |
examples/11_relationships.py |
.assets/sample_model | Cross-file relationships: series() resolution, column_usage() reverse lookup, validate_references(), and the convenience accessors |
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 the 58 DLL plot tests pass 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/ # 66 plot functions across 15 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 - 10 of the 58 DLL 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 reading needs the optional
[dss]extra (iwfm_io.dss); the input-file readers store DSSFL pathname assignments but do not auto-fetch the referenced values — read them explicitly withread_dss_timeseries - 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.14.1 (2026-09-11) - Portability fixes found by CI on 2.14.0. Reader-resolved child paths are absolute (a relative one was relative to the CWD at read time and written verbatim into decks living elsewhere);
write_keyed_pathrelativises an absolute path against the folder of the file being written when nobase_diris given, and refuses to write an absolute POSIX path raw (its leading/reads back as a blank value — the Linux CI failure); pandas 3 copy-on-write read-only arrays (read_all_land_use_areaselement areas, DSS values); the 2015-DLL environment-variable regression case skips when that build is not installed. - v2.14.0 (2026-09-10) - Hardening release, part 2 (Medium/Low) — the deferred Medium/Low findings of the 2026-09-09 audit + hostile-QA pass, each flipped from a strict-xfail regression case to a passing test. Adapter: every table read lazily from disk (components, time series, budgets, text heads, stream flows, zone budgets) refreshes itself when its source file changes (mtime/size signature) and
IOModelAdapter.reload()forces it; an empty/truncatedGWHeadAll.outraises a clearValueError;open_modelprefers*main*files and skipscopy/backup/oldvariants; HDF outputs whoseNTimeStepsdisagrees with the dataset keep the common prefix instead of a misaligned index. Aggregation:aggregate_budgetreturns an empty frame for an empty long input and propagates NaN on both the typed and heuristic paths. Readers: text budgets (read_budget_text) assemble multi-line column titles per data column (was: last header line only — duplicateLosscolumns, truncatedSubreg.); hydrograph metadata labels match whole words (NODESno longer read asNODE+S); decks with legacy cp1252 bytes in comments decode as cp1252 instead of U+FFFD;read_diver_specschooses the row layout by numeric-token count only and warns on an out-of-range TYPDSTDL (was: silently re-parsed under the other layout); a land-use block missing its date raises (was: merged into the previous block);read_zone_defrejects an element assigned twice. Writers: a numeric-looking cell with a comma ("1,000") and a name containing/are refused (both are read back wrong by IWFM);write_gw_initial_conditionsrequires layers1..NLwithout gaps and writes multi-line banners as comment lines; datetime dates in land-use tables are formatted as IWFM stamps;validate_nodes/validate_stratigraphycatch NaN/inf,validate_elementsallows0only fornode4,validate_preprocessorchecks declared counts (ND/NE/NREGN/NRH/NLAKE) against the tables. Plots:plot_gw_head_contourhonourstime_indexandfactorwithout a date range;plot_head_vs_gse_scatterdraws the aquifer-top elevation (was the aquitard thickness); trend/drawdown maps raise on an empty date window and skip NaN heads;plot_element_maprejects a wrong-length array (was: colours recycled); animations keep one contour level set across frames (colorbar matched frame 0 only) and rejectfps <= 0; the stream–aquifer exchange map handles a model without streams. GIS/VTK/wells/gauges:export_gisrejects unknown suffixes (.geojsonused to become a shapefile folder) and duplicatenode_data/element_dataids, accepts a single layer name; VTK array names are XML-escaped;build_well_mappingrejects NaN coordinates and warns for wells outside the mesh;stream_hydrograph_series(quantity="stage")explains when the HDF holds flows only. PEST:read_smpaccepts24:00:00stamps;parrep_v2rejects NaN values, never overwrites fixed/tied parameters, and findsNOPTMAXcase-insensitively;setup_agents(overwrite=True)removes staleagent_NNfolders;balance_weightsrejects negative/NaN targets and infinite residuals; observation-name codecs decode year-9999 stamps and refuse 2-digit-year stamps that would not round-trip;ParamSpecrejects NaN zones, zone labels that slugify to the same name, and tie chains/cycles; the quickstart validates weights;budget_observationscompares labels as text and rejects name collisions. DSS:dss_catalog/read_dss_timeseriesraiseFileNotFoundErrorinstead of creating an empty file; recurring-year (4000) records read at second resolution. CLI:--tracebackis accepted after the subcommand too. API surface:IOModelAdaptergains publicmodel_root,simulation,preprocessor,gw_main,stream_main,heads_file,available_budgets,available_zbudgets(used bycompare_models, which now also counts text.budbudgets, and by the PEST quickstart instead of private attributes);scenario.NEVER_LINK_SUFFIXESis public;write_swshed/write_unsatzonetakebase_dir;collect_gwheadsraises when node/layer filters are given withoutn_nodes/n_layers(they were silently ignored);plot_contour_mapmasks NaN nodes (points fallback when too few remain) instead of drawing substitutes, the recovery-lag map leaves never-recovered nodes blank,plot_budget_sankeydefaults to matplotlib; GIS builder failures log at warning; PEST template fields are at least 12 wide. DLL wrapper: intervals are checked against IWFM's recognised list (3MONcounted as zero intervals); unknown hydrograph location types, NaN coordinates, unknown zone extents, out-of-range Z-Budget columns/zones and windows outside the file raiseValueError(several were access violations);get_column_headers_general(max_columns)is a buffer floor;describe()on a closed model,print_results/advance_stateand the stream-inflow /get_supply_purposegetters in inquiry mode raise (partial instantiation);delete_inquiry_data_filerefuses a file that is not the active model's simulation main (the DLL export ignores its argument);download_dllverifies the extracted file is a PE image andload_dllrefuses a library without the IWFM exports. - v2.13.0 (2026-09-10, folded into 2.14.0 — never published on its own) - Hardening release, part 1 (Critical/High) — every Critical and High finding of the 2026-09-09 pre-release audit and hostile-QA pass, plus the CI gating that keeps them fixed. Breaking: Python 3.8 dropped (
>=3.9); readers raiseIWFMParseError(file, section, line) on truncated, short-row, non-numeric or malformed input instead of warning and returning partial objects — the previous keep-what-parsed behaviour isiwfm_io.strict_mode(False)/open_model(strict=False), which warns withIWFMReadWarning;IWFMParseErrorno longer subclassesStopIteration; writers refuse NaN/None/inf/empty/line-break cells and non-integral IDs (ValueErrornaming file, column and row),fmt_numno longer returns""for NaN, positional tables (col_N,head_layer_N, root-zone element tables, land-use areas) are written by column name, never DataFrame order, and per-layer tables must hold layers1..NLexactly once;parse_iwfm_daterequires a fullMM/DD/YYYY_HH:MM(24:MMwith minutes rejected); a non-date row in a time-series file is an error (was: silent truncation of everything after it);write_keyed_value(None)writes a blank;read_velocity_outreturns one row per element per timestep (was an alias of the hydrograph reader keeping 1 element in 400); text-heads columns are labelled by real node id. PEST:apply_parametersreads a pristine<file>.basesnapshot (created byPestSetup.writeor on first use) so multipliers no longer compound across forward runs; duplicate/NaN/header-only value files and a missingbase_dirraise; generated run steps use the building interpreter (sys.executable);setup_agentsrefuses a destination inside the template (was: unbounded recursion); the quickstart copies the model before writing the template, refuses a populated destination, and honours SMPexcludedflags and per-rowweightcolumns; kriging rejects NaN pilot values and checks weight sums. Scenario/run:create_scenariorefuses identical or nested source/destination (it could delete the base model), removes a half-built scenario when a change fails, no longer copies baseline outputs underBudget//ZBudget/(copy_outputs=Trueto keep),set_keyed_valueedits only the value span and can set blank entries;run_modelreports timeouts as failedRunResults (timed_out), keeps the FATAL detail lines andstdout_tail, raisesRunErrorcarrying partial results, and resolves a relativeinput_fileagainst the model. Aggregation: monthly/annual window ends are generated with IWFM's own month arithmetic (iwfm_increment_months: last-day-of-month sticky, February clamp) — daily models starting late in a month now match the DLL exactly;collect_*date bounds use the 24:00 owning-day convention. DLL wrapper: every call goes through a guard (process-wide lock, model activation viaIW_Model_Switch, closed-object check, working-directory anchor) so twoIWFMModels no longer alias or crash each other; dates, intervals, layers, locations, ids and array lengths are validated in Python before the Fortran (a mistyped date used to STOP the process with exit code 0, location 0 corrupted the heap);IWFMBudget/IWFMZBudgetinstances re-open their own file transparently;generate_zone_listalways sends zone names; non-native hydrograph intervals are rejected (the DLL used fixed-day strides);download_dllwrites atomically with a timeout. Plots: all 66 functions render DLL-free (IOModelAdaptergainedget_budget_*/get_hydrograph*shims), land-use time series use the requested window, matplotlib/numpy deprecations removed, stale__main__demo blocks deleted. Tooling: CI fetchessample_model.zip, runsruff --select F, asserts a minimum passed count, and adds a Windows DLL job; pytest markerssample_model/exe/dll/c2vsimfg/regressionreplace ad-hoc skips;tests/regression/holds the 135 hostile-QA reproductions (strict xfail until fixed;tests/_ci/unxfail.pyflips them) andtests/io/test_plots_smoke.pycovers every plot function. - v2.12.0 (2026-09-04) - DLL-faithful temporal aggregation (#33, #34). Aggregated budget reads now reproduce the IWFM DLL exactly — semantics verified line-by-line against DWR's Fortran source and proven at machine precision against
IWFMBudget.get_values/IWFMZBudget.get_values_for_zoneon the sample model (GW/LWU/RootZone budgets and LWU/UnsatZone zone budgets, every location/zone, native/monthly/annual) and on C2VSimFG v1.5 (all 48 water years ≤ 6e-16). Anchored windows (breaking):read_budget_hdf/read_zbudget_hdfinterval="1YEAR"now means what the DLL means — consecutive 12-month windows anchored to the data begin (for October-start models, the water year), stamped at the window end (09/30_24:00→ Oct 1 midnight under the library's 24:00 convention), with a trailing partial window dropped as the DLL does;"1MON"windows are likewise anchored, so monthly labels from daily data now match native monthly files (next-period-begin stamps) and each24:00stamp lands in the month it belongs to; calendar years remain available under the new explicitinterval="1CALYEAR"(January-anchored, partial years kept). LWU carry-over corrected: the carried shortage is the previous step's raw shortage column (the Fortran never feeds back the recomputed one), reset at each window start; aggregated shortage is signed (never clipped) and subtracts the other-inflow column (type 11) where present; Potential CUAW contributes nothing at steps with zero supply requirement; all vectorized. Zone budgets (#33): zone-mode frames now carry the DLL's full column set — datasets with no active elements appear as all-zero columns instead of vanishing (positional column indexing now matches the DLL) — with clean display names (the raw@...@unit annotations are stripped;metadatakeeps the raw names and addsdata_names_clean), and the LWU carry-over columns are aggregated per element before zone summation exactly as the DLL does (the clipping does not commute with spatial sums), in raw element mode too.aggregate_budget(df, period=, data_types=)(#34): pass thedata_typesmappingread_budget_hdfreturns and the helper applies the DLL's per-type rules — volumetric rates (1/9/10/11) sum, beginning storage (2) keeps the period's first value, ending storage/area/length (3/4/5) the last (a monthly Area column no longer sums to ~12× its real value), and the LWU trio (6/7/8) uses the carry-over accumulation — for wide and long frames alike, with the existing name heuristic as the fallback for untyped columns. New DLL-parity test module (tests/io/test_dll_agg_parity.py, runs wherever the DLL loads) plus an engine test suite that checks the vectorized carry-over against a straight transcription of the Fortran loop. Also new: GW initial-conditions (restart) file support —write_gw_initial_conditions(path, heads, facthp=)writes the optional restart file the GW main names as its INITIAL CONDITIONS FILE (theFinalGWHeads.outlayout),initial_heads_from_head_all(head_all, date=)builds the heads table from oneGWHeadAll.outtimestep (defaults to the last — the spin-up → production handoff), andread_final_state_outnow also reads hand-written restart files (no dashed separators, bare factor line without the/ FACTHPkeyword — layouts IWFM itself accepts). - v2.11.1 (2026-08-29) - Relationships walkthrough. New
examples/11_relationships.py— a comprehensive, runnable tour of the cross-file relationship layer: why pointer columns exist,series()resolution shown raw / factor-applied / calendar-expanded, thecolumn_usage()reverse lookup recovering the meaning of anonymouscol_Ncolumns,validate_references()on a clean model and on a deliberately broken one, and all seven convenience accessors including the informative 0-pointer error. No library changes. - v2.11.0 (2026-08-29) - Cross-file relationships as a first-class layer. IWFM inputs constantly reference each other — pointer columns hold column numbers into role-referenced time-series files, ID columns reference the grid, and
(TYPDST, DST)pairs pick destinations under per-file code tables. The model now knows every such relationship. On the object returned byopen_model():model.component(name)andmodel.timeseries(role)lazily read and cache any component/sub-file or pointer-target time-series file (~24 roles);model.series(role, column)returns the actual series a pointer points at — conversion factor applied (raw=Truefor file-native values) and recurring-year data (sentinel years 4000/2500) expanded onto the simulation period (expand=Falsefor the raw pattern), with step-function semantics matching IWFM;model.column_usage(role)is the reverse lookup that gives the anonymouscol_Ncolumns their meaning (on the sample model it reconstructs the ET file's lost column-label comment exactly: col 1 = tomatoes … col 7 = the lake);model.validate_references()checks every pointer against its target's column count, every entity ID against the grid, and every destination pair against its code table, returning a findings DataFrame (C2VSimFG v1.5 validates clean in ~30 s — and the layer directly reproduces real QA findings such as deliveries pointed at a never-irrigable crop column). Convenience accessors answer common questions in one call, handling theelement_id=0"all elements" sentinel and applying share fractions (FRACWL/FRACSK/diversion fractions):crop_series(kind, crop, element=)(non-ponded codes, ponded types,"native"/"riparian"; kindset,irrigation_period,supply_requirement,min_moisture,target_moisture,return_flow,reuse,min_perc,ponding_depth),urban_series(kind, element=),well_pumping(id)/element_pumping(id)(kind="max",scaled=False),bc_series(node, layer=)(searches all four BC files; constant BCs return their value at simulation start),diversion_series(id, kind=), andlake_max_elevation(). A pointer of 0 raises an informative "none / computed internally" error instead of an index error. New public helperexpand_recurring(data, begin, end)maps recurring-year data onto a real calendar period (single constant entries, leap-day fallback). The relationship registry itself is internal for now and will be considered for public API once mature. - v2.10.0 (2026-08-29) - Complete file coverage, audited twice. Every dataset in every IWFM input file reachable from the preprocessor and simulation mains now has a full reader/writer pair, proven end-to-end: the sample model rebuilt from all regenerated inputs reproduces baseline heads exactly through the real executables, and a regenerated C2VSimFG v1.5 preprocessor deck produces a byte-identical binary. New API:
read_timeseries_file/write_timeseries_file(any standard 3/4/5-param time-series file — covers RootDepthFrac, MinMoist, PondDepth, RiceOps, Population, PerCapWaterUse, UrbanWaterUseSpecs, ReturnFlowFrac, ReuseFrac, incl. DSS mode; spec keywords preserved,has_factor=/has_dssfl=/columns=overrides),read_surface_flow_dest/write_surface_flow_dest(v4.12(T,D)destination tuples),read_max_lake_elev/write_max_lake_elev,read_irr_period/write_irr_period, and a rebuilt lake component (bed factors, multi-lake table, initial elevations). Correctness fixes (several found by auditing against DWR's Fortran source and negative-testing the executables): v4.2 stream bed-table column order dispatched by version (IR WETPR IGW CSTRM DSTRM— previously misread on C2VSimFG-class decks), element-pumping per-layer fractions sized by the model's layer count instead of a hard-coded 2 (read_elem_pump(n_layers=)), 7-token hydrograph rows (x-y + placeholder node) keep their names,/-prefixed lines parse as blank-value data lines (IWFM's comment chars areC c *only), DSS mode for IrigFrac/SupplyAdjust, DELTAT (non-time-tracked mains), stream-geometry partial-interaction table,IWFMModelopens from any working directory (deck-relative paths anchored to the simulation folder,run_dir=override). Contracts: declared dimension variables (ND, NE, NRDV, NOUTH, …) are stored on the dataclasses and writers refuse count↔table mismatches with a clearValueError; NaN cells can no longer write silently short rows; time-series values write at full practical precision (%.10g). Annotations preserved: end-of-line/comments round-trip — element-group/delivery-area names (e.g. C2VSimFG's "Arvin-Edison WSD"), hydrograph station/InSAR notes, well and crop names (crop_namesincl. budget crops), all re-emitted on write. Issue fixes #28, #30, #31, #32. Breaking renames (the old names were wrong):StreamMain.reach_paramscolumns are nowstream_node_id/conductance/bed_thickness/wetted_perimeter(rows are per stream node; the previouswidth/bed_thicknesslabels mis-assigned DSTRM/WETPR), andSpecifiedHeadFile.data.ibctypis nowitscol(it is the time-series BC column pointer). Diversion NAMEs are written positionally again (a/-relocated name reads as blank to IWFM). Typed parse errors (IWFMParseError) name the file;write_subsidencejoinswrite_subsidence_fileas the canonical name. - v2.9.0 (2026-08-25) - Land use area tables + IWFM2OBS/CalcTypHyd parity.
read_land_use_area(path, columns=)/write_land_use_area()cover all four root-zone land use area files (non-ponded crops, ponded crops, urban, native/riparian vegetation — one shared format) as a long DataFrame (date,element_id, one area column per land use;columns=names them with crop codes), including the DSS-pathname variant; both directions are vectorized (C2VSimFG's 140 MB urban file reads in ~19 s and round-trips exactly; the 1 GB non-ponded file — 3.25M rows × 20 crops — reads in under two minutes), and the writer's output is exe-verified: the sample model reproduces baseline heads exactly with all four area files regenerated.read_all_land_use_areas(rootzone_main, element_areas=)combines every land use group of a model into one DataFrame with each file's conversion factor applied so columns are areas in model plane units; fraction-based files (FACT=0.0) are converted withelement_areas=(pass the parsed preprocessor — element areas are computed from the grid — a Series, or a dict) or kept as fractions when it's omitted, with a warning only if that would mix fractions and areas. PEST additions matching DWR's calibration utilities:read_smp/write_smphandle the exclusion-flag column and IWFM2OBS fixed-width layout (fixed_width=True),match_sim_to_obs(extrapolate=)adds IWFM2OBS-style bounded endpoint extrapolation, and newiwfm_io.pest.typhyd(typical_hydrographs) is a CalcTypHyd equivalent — cluster-averaged, de-meaned typical hydrographs from period-year slot averages. - v2.8.0 (2026-08-24) - One-call PEST++ setup and the
iwfm-iocommand line.pest_setup_from_model(model_dir, obs, dest)goes from a model folder plus observed heads (long-form frame, SMP, or CSV) straight to a runnable pestpp-ies template: multiplier parameters for the requested aquifer properties (kh, ss, sy, kv, aquitard_kv— NGROUP=0 tables and single parametric grids both supported) zoned by subregion × layer (or per layer, or global) plus stream-reach conductance; observations paired to the model's own GW hydrograph outputs by name (unmatched or unpairable rows reported, never silently kept); a hardlinked copy of the model inside the template; and a generated fail-fast forward run — apply multipliers → run the IWFM executables → re-extract simulated values at the observation timestamps. Exe-verified: the generated forward run reproduces baseline heads exactly at every observation through the real executables, and the template runs unmodified under the real pestpp-ies. New console scriptiwfm-io:describe(model inventory as JSON),pest setup(the quickstart from the shell),pest agents(hardlinked agent directories + manager script),pest run(serial, or manager plus N local agents launched and awaited), andpest analyze(phi +diagnose_iesreport, text or JSON). Supporting improvements:ApplyActionreaches nested tables via dotted paths ("parametric_grids.0.params") and takesbase_dirso component writers re-relativise referenced file paths on rewrite — fixing cumulative path corruption when the apply step ran outside the simulation working directory; the writers now emit the load-bearing comment lines that terminate the time-series spec block and stream-inflow node list (exe-verified — IWFM otherwise consumes the first data line);budget_observations(aggregate="annual")applies per-component water-year rules (flows sum, storage stocks take first/last — never summed); IWFM_24:00hydrograph stamps parse directly (faster, no more dateutil warning). Newexamples/10_pest_calibration.py(cross-platform, runs in seconds — no executable needed), notebook 09 re-executed with a leading one-call quickstart section, and theiwfm-analystskill teaches the new workflow. - v2.7.1 (2026-08-15) - Correct calendar grouping and budget aggregation for IWFM's
24:00stamps. IWFM stamps every output value at the first instant after its period ends (24:00= next-day midnight), so naive.dt.year/.dt.monthlabels andresample()bins drift at period boundaries — a9/30value lands in the wrong water year — and plain.sum()aggregation destroys the storage stocks (Beginning/Ending Storage are levels, not flows). New tools fix both, and the tutorial notebooks' aggregation examples now use them:iwfm_day(times)returns the day a stamp belongs to (midnight stamps map to the day they close; accepts IWFM date strings, datetimes, Series, or a DatetimeIndex);water_year(times)returns the Oct–Sep water year as a plain integer labeled by ending year (09/30/2024_24:00→ 2024,10/01/2024_24:00→ 2025);aggregate_budget(df, period="WY"|"CY"|"MON")aggregates a widebudget_df()frame or the longcollect_budgetsframe with the right rule per component (budget_component_agg: flows sum,Beginning Storagetakes the period's first value,Ending StorageandCumulative …the last) — verified by stock continuity on the sample model (each water year begins exactly where the previous one ended); andday_index=Trueonheads_df()/budget_df()/hydrograph_df()(bothIOModelAdapterand the DLLIWFMModel) returns the frame indexed by owning day, so calendar idioms likeresample("YE-SEP")label periods correctly when you want to stay in plain pandas. Parsing is unchanged:parse_iwfm_datestill returns the true instant, which DSS/CalSim alignment and exact-timestamp joins depend on. - v2.7.0 (2026-08-14) - Complete reader/writer coverage + text-budget fallback. Writers now exist for every reader — the last unpaired files gained theirs: the three boundary-condition sub-files (
write_spec_flow_bc,write_general_head_bc,write_constrained_head_bc— keywords match DWR's release files, and the constrained rows'/nameannotations round-trip) and the four root-zone sub-component mains (write_nonponded_ag_main,write_ponded_ag_main,write_urban_main,write_native_veg_main— crop-code blocks, root depths, every per-element pointer table including the element-0 "all elements" shorthand, and blank-optional-file handling; all takebase_dirlike the other component writers). Verified three ways: write→re-read equality on the sample model, write→re-read equality on the real C2VSimFG v1.5 files (the v4.11 variants with 20 crops and 32,537-element tables), and the executable round-trip — the sample model reproduces baseline heads exactly with the root-zone sub-mains and BC files regenerated too.open_model()now surfaces text.budbudgets: for packaged/older models (or fresh executable runs) that ship no budget HDF files,.budfiles inResults/andBudget/are discovered automatically,describe()lists them with"format": "text", andbudget_df()serves them identically (locations by index or name, date windows) at the file's native output interval; where a budget exists in both formats the HDF wins, matched on a normalized stem (Strm.buddefers toStrmBud.hdf). - v2.6.0 (2026-08-13) - GIS and VTK exports (roadmap item 5). GIS (
pip install iwfm-io[geo]): newiwfm_io.gis—export_gis(model, "model.gpkg")/IOModelAdapter.to_gis()write every spatial layer to a multi-layer GeoPackage or a folder of ESRI Shapefiles: nodes (stratigraphy attributes joined), element polygons (with subregion names), dissolved subregion polygons, stream-reach LineStrings, stream-node points, merged lake polygons, tile drains, and wells. Per-layer*_gdf()builders return GeoDataFrames for spatial analysis in Python;node_data=/element_data=merge simulation results (heads, depth to water, land use, …) onto the layers;crs=georeferences the output (IWFM files carry no CRS). Geometry is rebuilt from node coordinates and element configurations, so bothopen_model()adapters and the DLLIWFMModelwork as sources; geopandas imports at call time, keeping[geo]optional. VTK (no extra install — written with plain numpy): newiwfm_io.vtk—export_vtk(model, "model.vtu")/to_vtk()extrude the FE grid through the stratigraphy into a 3D layered mesh for ParaView (wedges for triangles, hexahedra for quads, one cell layer per aquifer, aquitard gaps preserved via IWFM's aquitard-above-aquifer convention), with built-inlayer/thickness/element_id/subregioncell arrays,z_scalevertical exaggeration, layer subsetting, and(n,)/(n, n_layers)point/cell data arrays;export_vtk_timeseries()writes per-timestep frames withhead+dtwpoint arrays and the.pvdcollection file ParaView animates. Cell orientation verified against pyvista (positive volumes) on the sample model and C2VSimFG v1.5 (130k mixed cells, ~2 s). Tutorial notebook 12 now covers both, including an inline 3D render. - v2.5.0 (2026-08-13) - PEST(++) calibration support (#6–#27): the new
iwfm_io.pestsubpackage covers both sides of a PEST++ calibration of an IWFM model — pure pandas, withpyemuneeded only for.jcbbinary ensembles (pip install iwfm-io[pest]). Building: round-trip-safe observation-name codec (standard + DWR grouped schemes, custom schemes registrable); SMP bore-sample file I/O; IWFM2OBS-equivalent sim-to-obs time matching (gap-guarded,24:00-aware); budget-component observations (means, water-year totals, full series); derived observations (successive/seasonal/drawdown head changes, vertical head differences, gauge accretion–depletion, long-term stats); zone/group parameterization (ParamSpec→ PEST++ v2 external tables + templates + initial-value files, with a fill-and-compare verify); pure-numpy pilot-point kriging on the FE mesh (exp/sph/gau variograms, anisotropy, zones); constrained reparameterization (RatioChainwith corner-checked ordering guarantees, Texture2ParPP_LOCSI/O); paired output+instruction writers (ObsFileSpec— the.insand the output file come from one spec, consistent by construction); multiplier write-back through the round-trip writers (apply_parameters) plus IWFM's native GW overwrite file; phi-budget weight balancing; hardlinked agent replication, fail-fast forward-run scripts, and pyemu-free finals reruns; and thePestSetupcapstone that writes a complete runnable PEST++ v2 template directory. Analyzing:load_ies_ensembles(lazy PESTPP-IES loader — per-iteration ensembles, tidy phi, per-group phi, prior-data conflict, obs+noise, base REI), vectorized fit statistics (residual_stats/rei_stats/ies_stats: bias, RMSE, R², NSE, KGE, phi at ensemble scale),diagnose_ies(convergence, collapse, conflict, bound railing, bias, objective balance as JSON state + signals), and a newiwfm_io.plots.calibrationfigure module. Core additions:iwfm_io.wells(validatedgwl_metadataschema, hydrograph linking incl. thename%layerconvention, multi-layer compositing,build_well_mappingwith perforation∩stratigraphy transmissivity weighting, best-layer selection) andiwfm_io.gauges(the stream mirror, incl. the IHSQR=2 flow+stage block layout);iwfm_io.dss(pip install iwfm-io[dss]): HEC-DSS cataloging and reading, CalSim channel-arc linking, month-length-aware CFS→TAF. Plus eleven executed tutorial notebooks (notebooks/) covering every feature area. The sample model was regenerated with IHSQR=2 stream hydrographs (flow + stage) — re-downloadsample_model.zipfrom this release. Validated against a production CalSim3 + C2VSimCG IES calibration. - v2.4.0 (2026-07-26) -
create_scenario(..., link_unchanged=True)(#5): hardlink unchanged input files into the scenario instead of copying them — stamping out N worker copies of a multi-GB model takes seconds and near-zero marginal disk. Copy-on-change semantics: files touched bychangesbecome independent real files (the change factories andIWFMFileWriter.flushnow write via temp file + atomic rename, never in-place),Results/stays a real directory, and file types the executables or DLL inquiry mode rewrite (.out,.bin,.bud,.log,.dss,.hdf,.h5) are always real copies. Falls back to copying with a warning when source and destination are on different filesystems. - 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
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.14.1.tar.gz.
File metadata
- Download URL: iwfm_io-2.14.1.tar.gz
- Upload date:
- Size: 625.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/6.2.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b0f7dce8853288ee9e50a8141ada5ec27247a98aafedca77aceb27aaecbcee3c
|
|
| MD5 |
fbcfe3282a52373b5b9404ec71b4b3a4
|
|
| BLAKE2b-256 |
5f66082674217921ad17b408995009d0d67fbe538a3425ea2b740cb52b5721ee
|
File details
Details for the file iwfm_io-2.14.1-py3-none-any.whl.
File metadata
- Download URL: iwfm_io-2.14.1-py3-none-any.whl
- Upload date:
- Size: 474.5 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 |
b0660daba1bd786e4a9299f266d48e5794c38da4a28c94708a0b70be4c9ec586
|
|
| MD5 |
2857354c00e2c050714361c010177ca2
|
|
| BLAKE2b-256 |
8f85a8e77a1b76a3a021bf4d582f1ecfefdc094a0d75cae0cf5ba2a87c293455
|