Imported from lzwei196/KISS-Knowledge-Infrastructure-for-Scientific-Simulation (
models/MODFLOW6/SKILL.md). Install upstream withnpx skills add lzwei196/KISS-Knowledge-Infrastructure-for-Scientific-Simulation --skill MODFLOW6. Copyright stays with the author.
MANDATORY EXECUTION POLICY — READ BEFORE PROCEEDING
You MUST run the actual model binary or package described in this document. If the model fails to import, compile, or execute, you MUST:
- Check
diagnostics/triplets.yamlfor a matching error pattern- Try the fix suggested in the triplet's
remedysection- If still failing, report the error to the user with full details
You MUST NOT substitute a simplified Python formula, regression equation, or hand-coded approximation in place of the real model. Doing so produces scientifically invalid results and defeats the purpose of the KI.
Before starting, run:
python preflight_check.py(in this KI directory) to verify that the model binary/package and required data are available.DEBUGGING PROTOCOL — When something goes wrong, follow this order:
- Check triplets —
diagnostics/triplets.yamlmay already cover this error- Read official docs — The model's own documentation for expected formats/units
- Find working examples — Check
outputs/or the model's shipped test data- Fix the tool — With knowledge of what "correct" looks like
Do NOT write custom debug scripts. The answers are in the docs and examples.
KI map — what to read, and when
| when you need | read | why |
|---|---|---|
| FIRST, always | preflight_check.py |
run it (python preflight_check.py): proves env/binary/data are usable and emits a machine-readable PREFLIGHT_REPORT= line. Do not debug a run that never had a healthy environment. |
| to run the pipeline stages | tools/ (30 tools) |
the executable pipeline. Read each tool's argparse (--help) before composing a command; SKILL.md's stage table says which tool serves which stage. |
| before running a stage | docs/s*_*.md (9 stage docs) |
per-stage procedure, verification and traps — the how-to that SKILL.md's overview compresses. |
| on ANY error, before debugging | diagnostics/triplets.yaml (22 entries) |
symptom → diagnosis → remedy for this model's known failure modes. Check here FIRST; the answer usually exists. Never renumber or rewrite entries. |
| to know what an output IS | dag.yaml |
the model's identity: every output's medium, units, validation_rank (1 = the headline variable) and observability. Scoring and obs-binding read THIS — when asked 'what does this model predict', the dag is the answer, not a guess. |
| when building inputs / parsing outputs | docs/format_spec.yaml |
exact I/O shapes + known_issues, projected from dag + triplets. Regenerate with ki_tools_common/generate_format_spec.py after changing either — never hand-edit. |
| to judge a run's skill | docs/validation_convention.yaml |
how this model's field judges it validated: per-dag_variable metrics, directions and CITED pass-bands. A run is graded against these, not against intuition. |
| for claims and thresholds | docs/gathered_papers.json (21 papers) + docs/papers_index.md |
the literature this KI is judged by; each entry's text_path is fetched full text in the central paper cache. role: benchmark marks the model's own skill paper. |
| for a machine-readable summary | knowledge_infrastructure.yaml |
the manifest (package, pipeline, validation tier, counts) — projected by ki_tools_common/generate_ki_manifest.py; regenerate after structural changes, never hand-edit. |
| what past runs learned | .kdt_evolution.jsonl |
append-only memory of previous runs and fixes on this KI. |
Projected 2026-08-17 from the KI's actual contents — 10 components present. Refresh: python3 ki_tools_common/generate_skill_map.py --ki_dir <this KI>.
Executable tool index (projected — complete by construction)
Every public tool in this KI, by exact path. What each is FOR lives in the
human-written Tool Inventory above; --help on any of these prints its arguments.
| tool (exact path) | invocation |
|---|---|
tools/calib_run.py |
KISSPATH_PYTHON_ENV/bin/python {KI}/tools/calib_run.py --help |
tools/s1/verify_mf6_installation.py |
KISSPATH_PYTHON_ENV/bin/python {KI}/tools/s1/verify_mf6_installation.py --help |
tools/s10_transport/configure_gwt.py |
KISSPATH_PYTHON_ENV/bin/python {KI}/tools/s10_transport/configure_gwt.py --help |
tools/s10_transport/parse_gwt_output.py |
KISSPATH_PYTHON_ENV/bin/python {KI}/tools/s10_transport/parse_gwt_output.py --help |
tools/s11_sfr/configure_sfr.py |
KISSPATH_PYTHON_ENV/bin/python {KI}/tools/s11_sfr/configure_sfr.py --help |
tools/s11_sfr/parse_sfr_output.py |
KISSPATH_PYTHON_ENV/bin/python {KI}/tools/s11_sfr/parse_sfr_output.py --help |
tools/s2/build_dis_package.py |
KISSPATH_PYTHON_ENV/bin/python {KI}/tools/s2/build_dis_package.py --help |
tools/s2/build_layers_from_global.py |
KISSPATH_PYTHON_ENV/bin/python {KI}/tools/s2/build_layers_from_global.py --help |
tools/s2/create_grid_from_basin.py |
KISSPATH_PYTHON_ENV/bin/python {KI}/tools/s2/create_grid_from_basin.py --help |
tools/s3/assign_k_from_glhymps.py |
KISSPATH_PYTHON_ENV/bin/python {KI}/tools/s3/assign_k_from_glhymps.py --help |
tools/s3/build_npf_package.py |
KISSPATH_PYTHON_ENV/bin/python {KI}/tools/s3/build_npf_package.py --help |
tools/s3/build_sto_package.py |
KISSPATH_PYTHON_ENV/bin/python {KI}/tools/s3/build_sto_package.py --help |
tools/s4/build_chd_package.py |
KISSPATH_PYTHON_ENV/bin/python {KI}/tools/s4/build_chd_package.py --help |
tools/s4/build_drn_package.py |
KISSPATH_PYTHON_ENV/bin/python {KI}/tools/s4/build_drn_package.py --help |
tools/s4/build_rch_package.py |
KISSPATH_PYTHON_ENV/bin/python {KI}/tools/s4/build_rch_package.py --help |
tools/s4/build_riv_from_cama.py |
KISSPATH_PYTHON_ENV/bin/python {KI}/tools/s4/build_riv_from_cama.py --help |
tools/s4/build_riv_package.py |
KISSPATH_PYTHON_ENV/bin/python {KI}/tools/s4/build_riv_package.py --help |
tools/s4/build_wel_package.py |
KISSPATH_PYTHON_ENV/bin/python {KI}/tools/s4/build_wel_package.py --help |
tools/s5/assign_transient_stress.py |
KISSPATH_PYTHON_ENV/bin/python {KI}/tools/s5/assign_transient_stress.py --help |
tools/s5/build_ic_package.py |
KISSPATH_PYTHON_ENV/bin/python {KI}/tools/s5/build_ic_package.py --help |
tools/s5/build_tdis_package.py |
KISSPATH_PYTHON_ENV/bin/python {KI}/tools/s5/build_tdis_package.py --help |
tools/s6/build_ims_package.py |
KISSPATH_PYTHON_ENV/bin/python {KI}/tools/s6/build_ims_package.py --help |
tools/s7/write_and_run_simulation.py |
KISSPATH_PYTHON_ENV/bin/python {KI}/tools/s7/write_and_run_simulation.py --help |
tools/s8/extract_budget.py |
KISSPATH_PYTHON_ENV/bin/python {KI}/tools/s8/extract_budget.py --help |
tools/s8/extract_heads.py |
KISSPATH_PYTHON_ENV/bin/python {KI}/tools/s8/extract_heads.py --help |
tools/s9/export_to_netcdf.py |
KISSPATH_PYTHON_ENV/bin/python {KI}/tools/s9/export_to_netcdf.py --help |
tools/s9/plot_head_map.py |
KISSPATH_PYTHON_ENV/bin/python {KI}/tools/s9/plot_head_map.py --help |
tools/s9/plot_water_budget.py |
KISSPATH_PYTHON_ENV/bin/python {KI}/tools/s9/plot_water_budget.py --help |
28 public tools; _-prefixed helpers and packaging files excluded.
Data Preparation
Input data
Input Source: MODFLOW 6 takes recharge and boundary conditions, not raw met forcing.
tools/s4/build_rch_package.py— Builds recharge package (from VIC drainage or direct estimation)tools/s3/assign_k_from_glhymps.py— Assigns hydraulic conductivity from GLHYMPS 2.0 datasettools/s2/build_layers_from_global.py— Builds layer geometry from DTB China / global datasetstools/s4/build_riv_from_cama.py— Builds river boundary from CaMa-Flood network
Data Validation Reference: See data_ki/GLHYMPS/SKILL.md for hydrogeology data documentation.
See data_ki/FanWTD/SKILL.md for water table depth documentation.
See data_ki/GRACE/SKILL.md for GRACE TWS validation data.
MODFLOW 6 Knowledge Infrastructure — Agent Entry Point
Package: modflow6-knowledge-infrastructure v1.2.0
Model: MODFLOW 6 (USGS Modular Hydrologic Model)
Domain: Groundwater hydrology, subsurface flow, solute transport
Role in HydroCraft: Fills the groundwater modeling gap — couples with VIC (recharge), CaMa-Flood (river stage), and Lohmann routing (baseflow)
What This Enables
An AI agent can autonomously:
- Build a MODFLOW 6 groundwater flow model for any basin using FloPy
- Map VIC deep percolation to MODFLOW recharge (RCH package)
- Couple CaMa-Flood river stages to MODFLOW river boundaries (RIV package)
- Feed MODFLOW drain discharge back to routing models as baseflow
- Extract water table depths for VIC soil moisture feedback
- Run transient simulations driven by HydroCraft forcing data
- Add solute transport (GWT) to any existing flow model (nitrate, chloride, etc.)
- Analyze contaminant plumes, breakthrough curves, and mass budgets
- Add streamflow routing (SFR) for physics-based stream-aquifer interaction
- Analyze gaining/losing reaches, stream stage, and downstream flow routing
Quick Reference
| Component | What | Where |
|---|---|---|
| Binary | mf6 (Fortran CLI) |
System PATH or specified path |
| Python interface | FloPy (flopy.mf6) |
pip install flopy |
| Simulation name file | mfsim.nam |
Created by FloPy in workspace directory |
| Input format | Block/keyword text files (BEGIN ... END) |
One file per package |
| Binary output | .hds (heads), .cbc (cell budget) |
Read with flopy.utils.HeadFile, CellBudgetFile |
| Listing file | .lst |
Convergence info, budget summary, warnings |
6. Output Description
Source of truth: dag.yaml. The dag is the model identity for observable outputs; if this section and dag.yaml ever differ, dag.yaml wins.
Headline output (the dag's validation_rank: 1 variable -- the one this model is judged by):
hydraulic_head-- Hydraulic head per active cell; water table is the head in the topmost active non-dry cell per column. (m)
Output variable (dag var) |
Rank | Unit | Description |
|---|---|---|---|
hydraulic_head |
1 | m |
Hydraulic head per active cell; water table is the head in the topmost active non-dry cell per column. |
Other dag outputs currently declared by this KI:
Output variable (dag var) |
|---|
water_table_elevation |
cell_budget_flux_terms |
solute_concentration |
stream_stage |
stream_aquifer_exchange |
volumetric_budget_discrepancy |
8. Unit Conversion Table
Source of truth: model package expectations, the KI's stage tools, and the existing coupling notes in this skill document. MODFLOW 6 does not convert units internally; every row below must be enforced before writing package files or post-processing outputs.
| Variable or exchange | Source unit | MODFLOW/KI unit | Conversion | Notes |
|---|---|---|---|---|
hydraulic_head |
m |
m |
x1 |
Dag rank-1 output unit. |
Recharge from VIC deep percolation / OUT_BASEFLOW |
mm/day |
m/day |
/1000 |
Used by RCH; recharge is a rate, not a volume. |
Hydraulic conductivity K |
m/day |
m/day |
x1 |
Required when MODFLOW LENGTH_UNITS is meters and TIME_UNITS is days. |
| Well, drain, and budget flows | m3/day |
m3/day |
x1 |
Package rates and cell budget terms use volume per model time. |
CaMa-Flood river stage sfcelv |
m |
m |
x1 |
Used by RIV stage coupling. |
| MODFLOW DRN flux to routing baseflow | m3/day |
m3/day |
x1 |
Downstream routing input in the existing coupling note. |
CaMa-Flood outflw to SFR INFLOW |
m3/s |
m3/day |
x86400 |
SFR period inflow conversion. |
VIC runoff to SFR RUNOFF |
mm/day |
m3/day |
runoff * cell_area / 1000 |
SFR runoff conversion. |
| GWT molecular diffusion | m2/s |
m2/day |
x86400 |
Transport parameter conversion. |
8c. Sign Conventions and Output Units
| Variable | Convention in this KI | Impact if wrong |
|---|---|---|
hydraulic_head |
Elevation head in m; dry cells use the HDRY sentinel and must be filtered. |
Statistics and maps are corrupted if HDRY is treated as a real head. |
water_table_elevation |
Derived from the topmost active non-dry cell per column. | Water table validation binds to the wrong layer if dry cells are not handled first. |
| Recharge | Positive downward input to RCH in m/day. |
Passing mm/day as m/day creates a 1000 magnitude error. |
| Cell budget flux terms | Volume rates in m3/day; budget percent discrepancy is diagnostic. |
Coupling and closure checks fail if rates are interpreted as depths. |
| SFR stream-aquifer exchange | Parsed from SFR budget output in model volume/time units. | Gaining and losing reaches can be classified incorrectly if signs are flipped. |
11. Validated Results -- Convention Bars
Source of truth: docs/validation_convention.yaml. A metric without its convention band is not a verdict. Null convention bands are written as no cited threshold.
The dag's rank-1 output is hydraulic_head. The currently extracted convention bars provided for this KI are keyed to water_table_elevation:
| Dag variable | Metric | Direction | Convention bar (cited) |
|---|---|---|---|
water_table_elevation |
nrmse |
minimize | good: 52.0 (guillaumot2022); satisfactory: no cited threshold (guillaumot2022) |
water_table_elevation |
pbias |
zero_centered | satisfactory: no cited threshold |
water_table_elevation |
csi |
maximize | satisfactory: no cited threshold |
No cited threshold is stated here for hydraulic_head because the extracted convention facts supplied for this update do not include a hydraulic_head convention band.
Pipeline Overview (10 Stages)
S1 Installation ──> S2 Grid/DIS ──> S3 NPF/STO ──> S4 Boundary Conditions
│ │
v v
S5 TDIS/IC ──────> S6 Solver/IMS
│
v
S7 Execute mf6
│
v
S8 Extract Heads/Budget
│
v
S9 Postprocess & Visualize
│
v
S10 Transport (GWT) [OPTIONAL]
Add GWT model ──> Run ──> Parse
│
v
S11 Streamflow Routing (SFR) [OPTIONAL]
Add SFR package ──> Run ──> Parse
Skill Documents (Read for Each Stage)
| Stage | Document | Key Decisions |
|---|---|---|
| S1 | docs/s1_installation_skill.md |
Binary source (pre-built vs compile), FloPy version |
| S2 | docs/s2_grid_discretization_skill.md |
Cell size, number of layers, IDOMAIN mask, DIS vs DISV |
| S3 | docs/s3_layer_properties_skill.md |
K values, confined vs convertible, Ss/Sy, Newton formulation |
| S4 | docs/s4_boundary_conditions_skill.md |
Which packages (CHD/WEL/RCH/DRN/RIV/EVT), units, VIC/CaMa coupling |
| S5 | docs/s5_stress_periods_skill.md |
Period lengths, timesteps, steady-state warmup, IC from prior run |
| S6 | docs/s6_solver_skill.md |
SIMPLE/MODERATE/COMPLEX, DVCLOSE, RCLOSE, under-relaxation |
| S7 | docs/s7_execution_skill.md |
Write files, run mf6, check listing file, handle failures |
| S8 | docs/s8_output_extraction_skill.md |
Read .hds/.cbc, HDRY sentinel, budget balance check |
| S9 | docs/s9_postprocessing_skill.md |
Contour maps, budget plots, NetCDF export for coupling |
Tools Reference
| Stage | Tool ID | Script | Purpose |
|---|---|---|---|
| S1 | verify_mf6_installation |
tools/s1/verify_mf6_installation.py |
Check mf6 and FloPy availability |
| S2 | create_grid_from_basin |
tools/s2/create_grid_from_basin.py |
Basin shapefile to MODFLOW grid |
| S2 | build_dis_package |
tools/s2/build_dis_package.py |
Create DIS package via FloPy |
| S3 | build_npf_package |
tools/s3/build_npf_package.py |
Create NPF package (K, ICELLTYPE) |
| S3 | build_sto_package |
tools/s3/build_sto_package.py |
Create STO package (Ss, Sy) |
| S4 | build_chd_package |
tools/s4/build_chd_package.py |
Constant head boundaries |
| S4 | build_rch_package |
tools/s4/build_rch_package.py |
Recharge (incl. VIC coupling) |
| S4 | build_riv_package |
tools/s4/build_riv_package.py |
River boundaries (incl. CaMa coupling) |
| S4 | build_drn_package |
tools/s4/build_drn_package.py |
Drain boundaries (baseflow) |
| S4 | build_wel_package |
tools/s4/build_wel_package.py |
Well pumping/injection |
| S5 | build_tdis_package |
tools/s5/build_tdis_package.py |
Temporal discretization |
| S5 | build_ic_package |
tools/s5/build_ic_package.py |
Initial conditions |
| S5 | assign_transient_stress |
tools/s5/assign_transient_stress.py |
Time-varying stress data |
| S6 | build_ims_package |
tools/s6/build_ims_package.py |
Solver configuration |
| S7 | write_and_run_simulation |
tools/s7/write_and_run_simulation.py |
Write files + execute mf6 |
| S8 | extract_heads |
tools/s8/extract_heads.py |
Read binary head file |
| S8 | extract_budget |
tools/s8/extract_budget.py |
Read cell budget file |
| S9 | plot_head_map |
tools/s9/plot_head_map.py |
Water table contour map |
| S9 | plot_water_budget |
tools/s9/plot_water_budget.py |
Budget bar chart |
| S9 | export_to_netcdf |
tools/s9/export_to_netcdf.py |
Export for HydroCraft coupling |
| S2 | build_layers_from_global |
tools/s2/build_layers_from_global.py |
TOP/BOTM/IDOMAIN/STRT from global rasters |
| S3 | assign_k_from_glhymps |
tools/s3/assign_k_from_glhymps.py |
K/Sy from GLHYMPS 2.0 + HWSD pedotransfer |
| S4 | build_riv_from_cama |
tools/s4/build_riv_from_cama.py |
RIV package from CaMa-Flood river network |
| S10 | configure_gwt |
tools/s10_transport/configure_gwt.py |
Build GWT model (coupled or standalone) |
| S10 | parse_gwt_output |
tools/s10_transport/parse_gwt_output.py |
Parse concentration output, plume analysis |
| S11 | configure_sfr |
tools/s11_sfr/configure_sfr.py |
Add SFR streamflow routing to GWF model |
| S11 | parse_sfr_output |
tools/s11_sfr/parse_sfr_output.py |
Parse SFR stage, exchange, flow budget |
Global Data Sources
| Dataset | Path | What It Provides |
|---|---|---|
| GLHYMPS 2.0 | data/groundwater/glhymps/GLHYMPS.shp |
Hydraulic conductivity (K), porosity — global subsurface hydrogeology |
| Reinecke WTD | data/groundwater/fan_wtd/MeanWaterTableDepth_meter.tif |
Mean water table depth (m) — initial heads for IC package |
| GLiM lithology | data/groundwater/glim/glim_wgs84_0point5deg.txt.asc |
Lithology classes — K validation / cross-check |
| China DTB (Yan/Shangguan 2020) | Manual download from http://globalchange.bnu.edu.cn/research/cdtb.jsp | Depth-to-bedrock for China — layer bottom elevations |
| Pelletier regolith | Manual download from https://daac.ornl.gov/cgi-bin/dsviewer.pl?ds_id=1304 | Global regolith thickness — layer bottom elevations outside China |
Automated Setup Pipeline
Three new tools automate the most error-prone setup steps using the global datasets above:
build_layers_from_global(S2) — Builds TOP/BOTM/IDOMAIN/STRT from DEM + depth-to-bedrock + water table depth rasters. Eliminates manual layer elevation guesswork.assign_k_from_glhymps(S3) — Assigns K/Sy from GLHYMPS 2.0 for deep layers + HWSD pedotransfer for the shallow soil layer. Handles CRS reprojection and logK decoding automatically.build_riv_from_cama(S4) — Builds RIV package from CaMa-Flood river network + discharge. Auto-clamps rbot to prevent fatal MODFLOW errors.
Critical Domain Knowledge (Non-Obvious Facts)
-
Units are the user's responsibility. MODFLOW 6 does not convert units. If LENGTH_UNITS is "meters" and TIME_UNITS is "days", then K must be m/day, recharge must be m/day, well rates must be m3/day. There is no error if units are wrong — results will simply be silently wrong.
-
Recharge is a rate, not a volume. The RCH package expects recharge in LENGTH/TIME (e.g., m/day), not volume (m3/day). A common error is passing VIC deep percolation as mm/day without converting to m/day (divide by 1000).
-
HDRY sentinel for dry cells. When a cell goes dry (head drops below cell bottom), MODFLOW 6 sets head to 1.0e30 (HDRY). Reading this as a real head value corrupts statistics. Always filter HDRY before computing means or plotting.
-
Layer numbering is 0-indexed in FloPy, 1-indexed in MODFLOW input files. FloPy layer 0 = MODFLOW layer 1. Cellid tuples in FloPy are (layer, row, col) with 0-based indices. In raw MODFLOW input files, they are 1-based.
-
ICELLTYPE matters for water table problems. If the top layer is confined (ICELLTYPE=0) but the water table is in that layer, MODFLOW will not correctly compute saturated thickness — heads can rise above cell top with no error. Set ICELLTYPE=1 for convertible (unconfined) layers.
-
Newton-Raphson formulation is essential for drying/rewetting. Standard formulation can oscillate when cells repeatedly wet and dry. Use
NEWTON UNDER_RELAXATIONin the GWF model options for unconfined problems. -
Budget percent discrepancy > 1% indicates a problem. Typical well-posed models have < 0.01% discrepancy. Large discrepancy usually means convergence was not achieved or DVCLOSE/RCLOSE are too loose.
-
The listing file is the primary diagnostic. Budget summaries, convergence iterations, dry cell warnings, and error messages are all in
<model_name>.lst. Always read it after a run. -
FloPy arrays must be 3D (nlay x nrow x ncol). Common error: saving 2D arrays for
strtorsy. FloPy will raise "Unable to set data layer 0. Data is not in a valid format" if you pass a 2D (nrow x ncol) array instead of 3D (nlay x nrow x ncol). Always usenp.broadcast_to()ornp.stack()to ensure 3D shape. -
Layer 1 K: use VIC/HWSD soil Ksat, NOT GLHYMPS bedrock K. GLHYMPS represents deep bedrock permeability (log-scale, often 1e-15 to 1e-12 m²). For the shallow water table layer (0-10m), VIC soil Ksat from HWSD pedotransfer is more appropriate (typically 0.1-10 m/day for alluvial basins). Tested on Wangjiaba: VIC K=1.0 m/d + Sy=0.15 gave GRACE r=0.56; GLHYMPS K=0.002 m/d + Sy=0.01 gave r=0.41. Use GLHYMPS for Layer 2-3 (deeper bedrock) only. The
assign_k_from_glhymps.pytool does this by default — Layer 1 from HWSD, deeper from GLHYMPS — but verify the L1 values are reasonable (>0.1 m/day for alluvial, >0.01 for clay plains). -
GLHYMPS CRS is Cylindrical Equal Area (meters), not WGS84. Use geopandas bbox reprojection when doing spatial joins: reproject your WGS84 grid centroids to GLHYMPS CRS, or reproject GLHYMPS to WGS84. Direct coordinate comparison will fail silently.
-
GLHYMPS logK values are stored x100. The
logK_Ferr_field contains logK(m^2) multiplied by 100. For example, -1180 means logK = -11.80 m^2. Decode:K_m2 = 10**(logK_field / 100), then convert to m/day:K_m_day = K_m2 * rho * g / mu * 86400. -
RIV rbot must be above cell BOTM. If river bottom elevation is below the MODFLOW cell bottom, MODFLOW aborts with "RIVER BOTTOM IS LESS THAN CELL BOTTOM". Always clamp:
rbot = max(rbot, botm + 0.5).
Error Handling
When errors occur, look up symptoms in diagnostics/triplets.yaml (17 triplets across 7 failure domains). Key triplets:
- dt_mf6_001: Convergence failure — solver did not converge within iteration limit
- dt_mf6_002: Dry cells in unconfined simulation — cells going dry causing oscillation
- dt_mf6_003: Budget imbalance — percent discrepancy > 0.1%
- dt_mf6_004: Recharge unit error — mm/day vs m/day mismatch (silent error)
- dt_mf6_008: Silent wrong recharge — RCH applied to wrong layer or IRCH not set
HydroCraft Coupling
See docs/model_couplings.yaml for detailed coupling specifications:
- VIC deep percolation -> MODFLOW RCH: VIC baseflow_out (mm/day) / 1000 -> m/day recharge
- MODFLOW DRN flux -> Routing baseflow: Drain discharge (m3/day) -> routing input
- CaMa-Flood river stage -> MODFLOW RIV: CaMa sfcelv (m) -> RIV package stage
- MODFLOW water table -> VIC feedback: Water table depth modifies VIC soil moisture capacity
Minimal Working Example (FloPy)
import flopy
# 1. Create simulation
sim = flopy.mf6.MFSimulation(sim_name="test", sim_ws="./workspace")
# 2. Temporal discretization (1 steady-state period)
tdis = flopy.mf6.ModflowTdis(sim, nper=1, perioddata=[(1.0, 1, 1.0)])
# 3. Solver
ims = flopy.mf6.ModflowIms(sim, complexity="SIMPLE")
# 4. Groundwater flow model
gwf = flopy.mf6.ModflowGwf(sim, modelname="gwf", newtonoptions="NEWTON")
# 5. Discretization (10x10 grid, 1 layer, 100m cells)
dis = flopy.mf6.ModflowGwfdis(gwf, nlay=1, nrow=10, ncol=10,
delr=100.0, delc=100.0,
top=50.0, botm=[0.0])
# 6. Node properties (K=10 m/day, unconfined)
npf = flopy.mf6.ModflowGwfnpf(gwf, icelltype=1, k=10.0)
# 7. Initial conditions
ic = flopy.mf6.ModflowGwfic(gwf, strt=40.0)
# 8. Constant head on left boundary
chd_data = [[(0, i, 0), 45.0] for i in range(10)]
chd = flopy.mf6.ModflowGwfchd(gwf, stress_period_data=chd_data)
# 9. Recharge
rch = flopy.mf6.ModflowGwfrcha(gwf, recharge=0.001) # 1 mm/day = 0.001 m/day
# 10. Output control
oc = flopy.mf6.ModflowGwfoc(gwf,
head_filerecord="gwf.hds",
budget_filerecord="gwf.cbc",
saverecord=[("HEAD", "ALL"), ("BUDGET", "ALL")])
# 11. Write and run
sim.write_simulation()
sim.run_simulation()
# 12. Read results
import flopy.utils.binaryfile as bf
hds = bf.HeadFile("workspace/gwf.hds")
heads = hds.get_data()
Validated Results — Bengbu Basin (Re-validated 2026-03-22)
Validation Protocol: 3-step (VALIDATION_PROTOCOL.md)
Status: production_validated
FloPy Version: 3.10.0
Step 1: Binary Test
- MODFLOW 6.6.1 binary works (compiled Intel Fortran, double precision)
- FloPy 3.10.0:
run_simulation(exe_name=...)removed, usesubprocess.run()instead - Binary output readable with
precision='double' - Newton + SIMPLE solver = ERROR (CG incompatible with asymmetric matrix) -> use MODERATE/COMPLEX
Step 2: Progressive Data Replacement
| Component | Source | Status | Notes |
|---|---|---|---|
| Grid | VIC 0.25deg basin_grid.nc | Validated | 16x24 grid, 224 active cells |
| DEM/TOP | China DEM 90m | Validated | Range: 5-1378 m |
| Initial heads | Fan/Reinecke global WTD | Validated | WTD mean: 16.9 m, clamped to 1-5m for IC |
| K (Layer 1) | HWSD via VIC soil params, scaled 100x | Validated | HWSD Ksat underestimates aquifer K by ~100x |
| K (Layers 2-3) | Derived from Layer 1 K/3, K/30 | Validated | Typical depth decay factors |
| Recharge | VIC OUT_BASEFLOW (mm/day -> m/day) | Validated | Mean: 0.578 mm/day = 0.000578 m/day |
| Boundary (CHD) | DEM-derived (lowest 10% + edge cells) | Validated | 71 CHD cells |
| Drain (DRN) | All non-CHD active cells | Validated | Prevents ponding, C = K1000W/10 |
| Storage (Ss/Sy) | Literature defaults | Validated | Ss=1e-5, Sy=0.15/0.10/0.05 |
Step 3: Full HydroCraft Run Results
| Metric | Value | Expected | Status |
|---|---|---|---|
| WT depth median | 0.9 m | 1-5 m | PASS |
| WT depth mean | 2.0 m | 2-5 m | PASS |
| WT depth range | 0.5 - 5.0 m | 1-10 m | PASS |
| Budget closure | 0.0002% | < 1% | PASS |
| Convergence failures | 0 | 0 | PASS |
| Flow direction | correct (upstream 361m > downstream 30m) | correct | PASS |
| Artesian cells | 0 | < 10% | PASS |
| Layer 1 dry cells | 0% | < 20% | PASS |
| Runtime | 0.4 seconds | < 60s | PASS |
Key Lessons Learned
- HWSD soil Ksat != aquifer K: HWSD gives soil K (~0.01 m/day), aquifer K is ~1-10 m/day. Scale by ~100x for Layer 1.
- Newton requires MODERATE/COMPLEX solver: SIMPLE uses CG which fails on asymmetric Newton matrices.
- DRN conductance must use realistic dimensions: C = K * drain_width * cell_width / bed_thickness, NOT K * delr * delc.
- FloPy 3.10 API changes:
run_simulation()no longer acceptsexe_name,inner_rclose->rcloserecord, STO period data format changed. - Deep layer dry cells are normal: For unconfined aquifer with WT in Layer 1, Layers 2-3 will be dry. Only check Layer 1 dry cells for validation.
- Initial heads must be within layer bounds: Clamping WTD to 1-5m prevents starting heads below cell bottom.
Validation Files
- Script:
outputs/bengbu_modflow6_revalidation/step2_3_full_bengbu.py - Results JSON:
outputs/bengbu_modflow6_revalidation/validation_results.json - Plot:
outputs/bengbu_modflow6_revalidation/modflow6_bengbu_validation.png - MODFLOW workspace:
outputs/bengbu_modflow6_revalidation/bengbu_workspace/
Stage S10: Groundwater Transport (GWT)
Added: 2026-04-03 KI Version: 1.1.0 Validated: Minimal 1D advection-dispersion test (coupled GWF-GWT)
What GWT Does
The GWT Model simulates three-dimensional transport of a single solute species in flowing groundwater. It solves the advection-dispersion equation using numerical methods and can represent:
- Advective transport with flowing groundwater
- Hydrodynamic mechanical dispersion and chemical diffusion
- Sorption (linear, Freundlich, Langmuir isotherms)
- First-order and zero-order solute decay/production
- Mass transfer between mobile and immobile zones (dual-domain)
- Mixing from GWF stress package sources/sinks
GWT Workflow
Existing GWF Model ──> S10a: configure_gwt.py ──> S10b: Run mf6 ──> S10c: parse_gwt_output.py
(flow) (add GWT packages) (coupled run) (concentrations, plumes)
Two coupling modes:
-
COUPLED mode (recommended): GWF and GWT in same simulation. The GWF-GWT Exchange automatically passes flows. Both models share the same TDIS.
-
STANDALONE mode: GWT reads previously saved GWF head (.hds) and budget (.cbc) files via the FMI (Flow Model Interface) package. Useful for testing multiple transport scenarios without re-running flow.
Required GWT Packages
| Package | Type | Purpose | Key Parameters |
|---|---|---|---|
| DIS | Discretization | Same grid as GWF | nlay, nrow, ncol, delr, delc, top, botm |
| IC | Initial Conditions | Starting concentration | strt (typically 0.0 for clean aquifer) |
| ADV | Advection | Solute transport by flow | scheme: UPSTREAM (stable), CENTRAL, TVD (accurate) |
| DSP | Dispersion | Mechanical dispersion + diffusion | alh, ath1, atv, diffc |
| MST | Mobile Storage | Porosity, sorption, decay | porosity, sorption, bulk_density, distcoef, decay |
| SSM | Source/Sink Mixing | Concentrations for GWF stress packages | sources: [(pname, srctype, auxname)] |
| OC | Output Control | Save concentration, budget | CONCENTRATION FILEOUT, BUDGET FILEOUT |
Optional GWT Packages
| Package | Purpose | When to Use |
|---|---|---|
| CNC | Constant concentration boundary | Fixed concentration sources (e.g., leaking landfill) |
| SRC | Mass source loading | Direct mass injection (not tied to water flow) |
| IST | Immobile zone transfer | Dual-domain (fractured rock, clay lenses) |
| FMI | Flow Model Interface | Standalone mode (reads saved GWF files) |
Key Transport Parameters
| Parameter | Symbol | Typical Range | Unit | Notes |
|---|---|---|---|---|
| Porosity | n | 0.10-0.40 | - | Mobile domain pore volume fraction |
| Longitudinal dispersivity | ALH | 1-100 | m | Scale-dependent (Gelhar 1992). Start with L/10 where L = plume length |
| Transverse dispersivity | ATH1 | ALH/10 | m | Typically 0.1x longitudinal |
| Vertical dispersivity | ATV | ALH/100 | m | Typically 0.01x longitudinal |
| Molecular diffusion | Dm | 1e-10 to 3e-9 | m2/s | Species-dependent. Convert to m2/day: multiply by 86400 |
| Bulk density | rho_b | 1.4-1.8 | kg/L | For sorption calculations |
| Distribution coefficient | Kd | 0-100 | L/kg | Sorption strength (LINEAR isotherm) |
| First-order decay rate | lambda | 0.0001-0.01 | 1/day | Half-life = ln(2)/lambda |
Contaminant Presets
The configure_gwt.py tool includes documentation-derived parameter presets:
| Preset | Sorption | Decay | Use Case |
|---|---|---|---|
| conservative | None | None | Tracer tests, chloride, bromide |
| nitrate | LINEAR (weak) | First-order (denitrification) | Agricultural N pollution |
| chloride | None | None | Saltwater intrusion, road salt |
| ammonium | LINEAR (moderate) | First-order (nitrification) | Wastewater, fertilizer |
| phosphate | LINEAR (strong) | None | Eutrophication sources |
| generic_solute | None | None | General transport studies |
GWF-GWT Exchange (Coupled Mode)
In the simulation name file (mfsim.nam), add:
BEGIN models
gwf6 gwf.nam gwf
gwt6 gwt.nam gwt
END models
BEGIN exchanges
GWF6-GWT6 gwf-gwt.gwfgwt gwf gwt
END exchanges
BEGIN solutiongroup 1
ims6 gwf.ims gwf
ims6 gwt.ims gwt
END solutiongroup 1
The exchange file (gwf-gwt.gwfgwt) is empty -- MODFLOW 6 handles the coupling automatically.
GWT Solver Requirements
- MUST use BICGSTAB (not CG) for linear acceleration -- the advection term makes the matrix asymmetric
- Typical convergence: OUTER_DVCLOSE=1e-6, INNER_DVCLOSE=1e-6
- RCLOSE with "strict" option recommended
- Separate IMS for GWT (do not share with GWF IMS)
SSM (Source/Sink Mixing) -- How Concentrations Enter
Three methods to assign concentrations to GWF stress packages:
- Default: Sources have concentration 0, sinks withdraw at cell concentration
- Auxiliary variable: Add CONCENTRATION auxiliary to GWF package (e.g., WEL with aux CONCENTRATION), reference in SSM SOURCES block
- SPC6 file: Separate file with time-varying concentrations per stress period
Example SSM with WEL concentration:
# In GWF: well with concentration auxiliary
wel = flopy.mf6.ModflowGwfwel(gwf, auxiliary="CONCENTRATION",
stress_period_data=[[(0, 5, 5), -100.0, 50.0]]) # Q=-100, C=50
# In GWT SSM:
ssm = flopy.mf6.ModflowGwtssm(gwt,
sources=[("WEL-1", "AUX", "CONCENTRATION")])
Tools Reference
| Tool | Script | Purpose |
|---|---|---|
configure_gwt |
tools/s10_transport/configure_gwt.py |
Build GWT model coupled to or standalone from GWF |
parse_gwt_output |
tools/s10_transport/parse_gwt_output.py |
Parse .ucn concentration files, compute plume extent, breakthrough curves |
Example Usage
# 1. Coupled mode -- add GWT to existing GWF simulation
from tools.s10_transport.configure_gwt import build_coupled_gwt
result = build_coupled_gwt(
gwf_sim_ws="/path/to/gwf/workspace",
contaminant="nitrate",
source_concentration=50.0, # mg/L
source_cells=[(0, 5, 5)], # layer 0, row 5, col 5
advection_scheme="UPSTREAM",
run_model=True,
)
# 2. Parse results
from tools.s10_transport.parse_gwt_output import GWTOutputParser
gp = GWTOutputParser(
concentration_file=result["concentration_file"],
listing_file=result["listing_file"],
delr=100.0, delc=100.0,
)
summary = gp.summary()
plume = gp.plume_extent(threshold=1.0, layer=0)
btc = gp.breakthrough_curve(layer=0, row=10, col=10)
CLI Usage
# List contaminant presets
python configure_gwt.py --list-presets
# Build coupled GWT from existing GWF
python configure_gwt.py --gwf-dir /path/to/gwf --contaminant nitrate \
--source-conc 50.0 --source-cell 0,5,5
# Parse results
python parse_gwt_output.py gwt.ucn --summary --listing-file gwt.lst
python parse_gwt_output.py gwt.ucn --plume 1.0 --delr 100 --delc 100
python parse_gwt_output.py gwt.ucn --breakthrough 0,10,10
python parse_gwt_output.py gwt.ucn --plume-evolution 1.0 --json
python parse_gwt_output.py gwt.ucn --export-csv concentrations.csv
Critical Domain Knowledge for GWT
-
GWT "flows" are mass flows, not water flows. Budget terms in the GWT listing file are in mass/time units, not volume/time.
-
GWT concentration files (.ucn) use text="CONCENTRATION", not "HEAD". When reading with FloPy's HeadFile, pass
text="CONCENTRATION". -
The GWT solver MUST use BICGSTAB, not CG. The advection term produces an asymmetric matrix. CG will fail or produce wrong results.
-
Dispersion XT3D is expensive. Use
xt3d_off=Truefor 1D problems or when flow aligns with the grid. Remove for 2D/3D problems where diagonal flow is important. -
SSM is required if the GWF model has ANY stress packages, even if all concentrations are zero. Without SSM, MODFLOW 6 will error.
-
GWT has no steady-state option. To find steady-state concentrations, run a long transient simulation until concentrations stabilize.
-
Multiple species require multiple GWT models. Each GWT model simulates one species. Add multiple GWT entries in mfsim.nam, each with its own exchange.
-
Dispersivity is scale-dependent. Field-scale ALH is typically 0.1x to 1x the plume length scale (Gelhar et al., 1992). Lab-scale values are much smaller.
-
MODFLOW 6 GWT does NOT have particle tracking or MOC. Only finite-difference methods (upstream, central, TVD). For sharp fronts, use TVD scheme or refine the grid.
-
HYPE-MODFLOW coupling for nutrients: HYPE nutrient leachate (N, P) can be mapped to MODFLOW GWT via the SSM package. HYPE deep percolation concentration -> RCH auxiliary CONCENTRATION, or via SPC6 files for time-varying concentrations.
HydroCraft Coupling for Transport
| Coupling | From | To | Mechanism |
|---|---|---|---|
| HYPE N leachate -> GWT | HYPE deep_perc_N (mg/L) | GWT SSM or CNC | RCH aux CONCENTRATION or SPC6 file |
| HYPE P leachate -> GWT | HYPE deep_perc_P (mg/L) | GWT SSM or CNC | Separate GWT model for P |
| WEL injection -> GWT | Well concentration | GWT SSM | WEL aux CONCENTRATION |
| GWT plume -> HYPE | GW concentration | HYPE return flow quality | Not yet automated |
Validation Results (Minimal Test)
| Metric | Value | Expected | Status |
|---|---|---|---|
| mf6 runs coupled GWF-GWT | Yes | Yes | PASS |
| Concentration file readable | Yes | Yes | PASS |
| CNC boundary holds C=1.0 | 1.0000 | 1.0 | PASS |
| Advection-dispersion plume | 9 cells > 0.01 at t=500d | Plume grows over time | PASS |
| Breakthrough curve monotonic | Yes | Yes (continuous source) | PASS |
| parse_gwt_output.py works | All features tested | - | PASS |
Stage S11: Streamflow Routing (SFR)
Added: 2026-04-03 KI Version: 1.2.0 Validated: Self-contained test model (10-reach diagonal stream, 10x10 grid)
What SFR Does
The SFR Package simulates 1D open-channel flow in stream networks. Unlike the simpler RIV package (which requires user-specified stream stage), SFR computes stream stage from Manning's equation based on actual stream geometry and flow. This provides:
- Physically realistic stream-aquifer interaction based on computed stream depth
- Downstream flow routing through connected reaches
- Diversions (irrigation canals, water management)
- Tracking of gaining/losing reaches along the network
- Proper handling of dry reaches (no-flow conditions)
When to Use SFR vs RIV
| Criterion | Use RIV | Use SFR |
|---|---|---|
| Stream stage is known (from observations or CaMa-Flood) | YES | Not needed |
| Stream stage should be computed from flow | No | YES |
| Need to route flow downstream | No | YES |
| Need diversions / irrigation canals | No | YES |
| Need gaining/losing analysis per reach | Approximate | YES (exact) |
| Simple setup, few river cells | YES | Overkill |
| Complex stream network, many reaches | Possible but simplistic | YES |
| Data available: only river stages | YES | Not enough (need geometry) |
| Data available: stream network + geometry | Possible | YES (preferred) |
Rule of thumb: Use RIV when you have CaMa-Flood or observed stream stages. Use SFR when you need to compute stream stage from flow, route streamflow, or analyze stream-aquifer interaction in detail.
SFR Workflow
Existing GWF Model ──> S11a: configure_sfr.py ──> S11b: Run mf6 ──> S11c: parse_sfr_output.py
(flow) (add SFR package) (simulation) (stages, exchange, budget)
SFR Key Concepts
-
Reach: A segment of stream within a single model cell. Each reach has static properties (length, width, slope, Manning's n, streambed K, streambed thickness) defined in PACKAGEDATA.
-
Connection: Topology defining which reaches connect to which. Upstream connections are positive; downstream connections are negative in the CONNECTIONDATA block.
-
Period data: Time-varying settings per reach (inflow, rainfall, evaporation, runoff, diversion, status).
-
Manning's equation: Q = (1/n) * A * R^(2/3) * S^(1/2). SFR uses this to compute stream stage from flow. Requires unit conversion factors when using meters + days.
Required SFR Parameters
| Parameter | Symbol | Typical Range | Unit | Notes |
|---|---|---|---|---|
| Reach length | rlen | Cell dimension | m | Along-stream length within cell |
| Reach width | rwid | 1-100 | m | Top width of channel |
| Stream gradient | rgrd | 1e-4 to 0.05 | - | Bed slope (must be > 0) |
| Streambed top | rtp | DEM - 1 to 5 m | m | Elevation of streambed surface |
| Streambed thickness | rbth | 0.5-3.0 | m | Thickness of low-K streambed |
| Streambed K | rhk | 0.01-10 | m/day | Hydraulic conductivity of streambed |
| Manning's n | man | 0.015-0.06 | s/m^(1/3) | Channel roughness |
Stream Type Presets
The configure_sfr.py tool includes documentation-derived presets:
| Preset | Manning's n | Streambed K | Width | Slope | Use Case |
|---|---|---|---|---|---|
| mountain_stream | 0.04 | 1.0 m/day | 5 m | 0.01 | Headwaters, cobble bed |
| lowland_river | 0.03 | 5.0 m/day | 30 m | 0.001 | Alluvial floodplain |
| canal | 0.015 | 0.01 m/day | 10 m | 0.0005 | Lined irrigation channel |
| wetland_channel | 0.06 | 0.5 m/day | 15 m | 0.0001 | Marsh drainage |
| generic | 0.035 | 1.0 m/day | 10 m | 0.001 | General purpose |
Tools Reference
| Tool | Script | Purpose |
|---|---|---|
configure_sfr |
tools/s11_sfr/configure_sfr.py |
Add SFR package to GWF model |
parse_sfr_output |
tools/s11_sfr/parse_sfr_output.py |
Parse stage, exchange, budget |
Example Usage
# 1. Simple linear stream -- auto-builds reaches from cell list
from tools.s11_sfr.configure_sfr import build_simple_sfr
result = build_simple_sfr(
gwf_sim_ws="/path/to/gwf/workspace",
stream_cells=[(0,0,0), (0,1,1), (0,2,2), (0,3,3), (0,4,4)],
stream_elevations=[100.0, 98.0, 96.0, 94.0, 92.0],
stream_preset="lowland_river",
inflow_rate=1000.0, # m3/day
run_model=True,
)
# 2. Full control -- custom reach data + connections
from tools.s11_sfr.configure_sfr import build_sfr_package
reach_data = [
{"ifno": 1, "cellid": (0,0,5), "rlen": 500, "rtp": 50.0, "ncon": 1},
{"ifno": 2, "cellid": (0,1,5), "rlen": 500, "rtp": 49.5, "ncon": 2},
{"ifno": 3, "cellid": (0,2,5), "rlen": 500, "rtp": 49.0, "ncon": 1},
]
connection_data = [
[1, -2], # reach 1 flows downstream to reach 2
[2, 1, -3], # reach 2 connects to upstream 1 and downstream 3
[3, 2], # reach 3 connects to upstream 2
]
result = build_sfr_package(
gwf_sim_ws="/path/to/gwf",
reach_data=reach_data,
connection_data=connection_data,
stream_preset="generic",
period_data={0: [[1, "INFLOW", 500.0]]},
run_model=True,
)
# 3. Parse results
from tools.s11_sfr.parse_sfr_output import SFROutputParser
sp = SFROutputParser(
stage_file=result["stage_file"],
budget_file=result["budget_file"],
listing_file=result["listing_file"],
)
summary = sp.summary()
exchange = sp.get_stream_aquifer_exchange()
ts = sp.stage_time_series(reach_number=1)
CLI Usage
# List stream presets
python configure_sfr.py --list-presets
# Build self-contained test model
python configure_sfr.py --test --workspace /tmp/sfr_test
# Add SFR to existing GWF model from JSON
python configure_sfr.py --gwf-dir /path/to/gwf --reach-json reaches.json \
--preset lowland_river
# Parse output
python parse_sfr_output.py --stage gwf.sfr.stg --budget gwf.sfr.cbc \
--listing gwf.lst --summary
python parse_sfr_output.py --stage gwf.sfr.stg --reach-ts 5
python parse_sfr_output.py --budget gwf.sfr.cbc --exchange
python parse_sfr_output.py --stage gwf.sfr.stg --export-csv stages.csv
python parse_sfr_output.py --stage gwf.sfr.stg --summary --json
Critical Domain Knowledge for SFR
-
SFR uses Manning's equation for stage calculation. Stream stage is NOT user-specified (unlike RIV). SFR computes depth from flow using Q = (1/n) * A * R^(2/3) * S^(1/2), where A = width * depth, R = A / (width + 2*depth).
-
Unit conversion is required for Manning's equation. For meters + days, use
length_conversion=1.0andtime_conversion=86400.0. Theunit_conversionkeyword is deprecated since v6.4.2. Without proper conversion, computed stages will be wildly wrong. -
Stream gradient (rgrd) must be positive. MODFLOW 6 will terminate if any reach has rgrd <= 0. When building from DEM, clamp minimum slope to ~1e-6.
-
Streambed top (rtp) minus streambed thickness (rbth) must be above cell bottom. Otherwise MODFLOW 6 will abort. Always validate: rtp - rbth > botm[lay, row, col].
-
Connection data uses signed reach numbers. Negative values = downstream, positive = upstream. The first entry in each connection list is the reach number itself.
-
SFR reaches must be connected sequentially. Unlike RIV (independent cells), SFR requires proper network topology. Incorrect connections cause mass balance errors or convergence failures.
-
Dry reaches get DNODATA sentinel (-1e30) in stage output. When a reach goes dry (no flow), MODFLOW sets stage to -1e30. Always filter these before statistics or plotting.
-
SFR is an "advanced" stress package. Unlike simple packages (RIV, CHD), SFR period data persists across stress periods unless explicitly changed. An empty PERIOD block does NOT remove SFR stresses.
-
SFR and RIV should NOT be used in the same cell. Having both packages in the same cell causes double-counting of stream-aquifer exchange. Use one or the other per cell.
-
CaMa-Flood integration: CaMa-Flood can provide river discharge as SFR upstream inflow (INFLOW keyword in period data), or stream widths for reach width. However, CaMa-Flood does not provide cross-sections or Manning's n -- those must come from literature or field data.
HydroCraft Coupling for SFR
| Coupling | From | To | Mechanism |
|---|---|---|---|
| CaMa-Flood Q -> SFR INFLOW | CaMa outflw (m3/s) | SFR period INFLOW | outflw * 86400 -> m3/day at headwater reaches |
| CaMa-Flood width -> SFR rwid | CaMa rivwth (m) | SFR packagedata rwid | Direct mapping |
| VIC runoff -> SFR RUNOFF | VIC runoff (mm/day) | SFR period RUNOFF | runoff * cell_area / 1000 -> m3/day |
| SFR outflow -> CaMa-Flood | SFR downstream-flow | CaMa lateral inflow | Not yet automated |
Validation Results (Test Model)
| Metric | Value | Expected | Status |
|---|---|---|---|
| mf6 runs with SFR | Yes | Yes | PASS |
| Normal termination | Yes | Yes | PASS |
| Stage file readable | Yes | Yes | PASS |
| Budget file readable | Yes | Yes | PASS |
| Stream stage computed (reach 1) | 98.03 m | Near land surface - 2m | PASS |
| Stream-aquifer exchange | -5000 m3/day (all losing) | Inflow goes to aquifer | PASS |
| Budget closure | -0.00% | < 0.01% | PASS |
| Convergence failures | 0 | 0 | PASS |
| parse_sfr_output.py works | All features tested | - | PASS |
This knowledge infrastructure was built using the Knowledge Dissection Toolkit v1.0 (Jianyun Zhang Research Group, Hohai University).