MODFLOW coupling
MODFLOW_250m package contents, the one-way steady-state MODFLOW 6 build from SWAT+ percolation, and research two-way BMI/XMI files (mf6.con).
“SWAT+MODFLOW-6” refers to two related but different deliverables. Establish which one a given package contains before working with exchange files or BMI.
Two coupling modes (do not conflate)
- Standard deliverable: sequential one-way steady-state. SWAT+ builds first; a sibling MODFLOW_250m grid is then built from annual-mean percolation (perc). Heads and baseflow are not fed back into SWAT+ in this mode. Transient MODFLOW is out of scope for standard builds.
- Research path: live daily two-way BMI/XMI coupling inside the SWAT+ day loop. Activated only when TxtInOut contains
mf6.conplus map files; the engine loads libmf6 at run time. Not included in standard packages.
mf6.con in TxtInOut, it is the standard deliverable.Build pipeline (one-way steady state)
- Order a coupled SWAT+/MODFLOW build (or attach MODFLOW to a finished SWAT+ workspace).
- After the SWAT+ package is ready, a 250 m MODFLOW 6 grid is built from annual-mean SWAT+ percolation.
- Percolation is mapped HRU→cell (area-weighted); the grid is assembled with standard MF6 packages (RCHA, SFR/DRN/GHB/WEL, …), solved steady-state, then optionally head-calibrated when observations exist.
- Artifacts land as a sibling folder MODFLOW_{resolution}m under the same site directory — not inside Scenarios/.../TxtInOut.
{site}/
├── SWAT_MODEL_Web_Application/ # (or resolved model name) — SWAT+ package
│ └── Scenarios/Default/TxtInOut/
└── MODFLOW_250m/ # sibling steady-state MF6 grid (not inside TxtInOut)
├── mfsim.nam
├── MODFLOW_250m.nam
├── *.dis *.npf *.ic *.ims *.oc *.sto *.tdis
├── *.rcha *.sfr *.drn *.ghb *.wel …
├── Grids_MODFLOW*
├── obs_vs_sim.csv / .png # when head obs exist
└── head_calibration_report.json- SWAT+ and MODFLOW download as separate archives. The standard MODFLOW folder has no README and no
mf6.con/ exchange maps. - Default grid spacing for chained builds is 250 m. You can also attach MODFLOW to an existing finished SWAT+ workspace from the product UI/API.
- Groundwater coverage for hosted coupled builds: Michigan (Lower Peninsula), Indiana, Wisconsin, and Ohio. The request point / HUC12 centroid must fall in coverage; sparse well density can refuse a build.
Units and one-way link
- MF6 lengths in meters; time in days. Recharge is derived from SWAT+ percolation (often converted mm/yr → m/day). Well elevations commonly convert feet→meters (× 0.3048).
- Spatial link: an HRU↔cell area-weighted map places recharge on the grid. No daily exchange loop in the shipped archive.
- The stock SWAT+ gwflow module is a different mechanism and is not this external-MF6 path.
Running the MODFLOW package locally
- Install USGS MODFLOW 6 (mf6). FloPy is optional for inspection/edits; the platform already ran MF6 at build time.
- Unzip the MODFLOW_* package; keep the tree intact (
mfsim.namat the root of that folder). - From that directory, run mf6 (or point your runner at
mfsim.nam). Re-read heads (.hds), budget (.cbc), and listing files. - SWAT+ still runs from Scenarios/Default/TxtInOut with a SWAT+ executable — independently of mf6 unless you assemble the research coupling below.
Daily two-way BMI/XMI coupling (research)
Live coupling is built into the public SWAT+ fork. Each SWAT+ day: percolation is pushed to MF6 recharge; SFR groundwater exchange is aggregated back to SWAT+ channels (replacing native aquifer return to avoid double counting). An optional PFAS leach map feeds groundwater transport when present.
./mf6 ! 1: folder (inside TxtInOut) holding mfsim.nam
1 ! 2: groundwater FLOW step, in days (1 = daily)
30 ! 3: groundwater TRANSPORT step, in days (30 = monthly)
1.0 ! 4: (optional) recharge multiplier for calibration
! 5: (optional) full path to libmf6 if not on the default search path- Place
mf6.conin TxtInOut. Convention: MF6 workspace at TxtInOut/mf6/ containingmfsim.nam. Absentmf6.con⇒ plain SWAT+ (no-op). - Maps next to
mf6.con: mf6_recharge.map (HRU→cell), mf6_baseflow.map (SFR reach→channel), optional pfas_leach.map. - Needs a coupler-enabled swatplus binary (see Compile the SWAT+ fork) plus libmf6.so / libmf6.dll on the library path.
- Standard packages do not ship
mf6.conor the exchange maps; assemble them only for the research daily-coupling path.
Failure modes
- Expected
mf6.conmissing — standard archives intentionally omit it; the BMI path requires adding con + maps + a coupler binary. - MF6 fails to open packages — run from the MODFLOW_* directory; do not nest it inside TxtInOut unless assembling research daily coupling.
MODFLOW_250m package contents
File-by-file contents of the sibling MODFLOW_{resolution}m folder a coupled build produces. Assembled as a standard MODFLOW 6 simulation; the archive contains the entire folder.
The standard deliverable is a standalone steady-state MODFLOW 6 model built from SWAT+ annual-mean percolation. Lengths are meters, time is days. Run it locally with mf6 from the folder root (mfsim.nam). The archive (MODFLOW_Model_{site}_{res}m.zip) contains the whole tree, including intermediate rasters and grids.
MODFLOW 6 packages in the archive
| File | Description | Units / key values |
|---|---|---|
| mfsim.nam | Simulation namefile: TDIS + GWF model + IMS solver | — |
| {name}.tdis | Single steady stress period | TIME_UNITS days; period (1.0, 1, 1.0) |
| {name}.ims | Newton / BiCGSTAB solver settings | outer head close 0.1 m; inner 0.01 m |
| {name}.nam | GWF package list | dis, ic, npf, sto, ghb, rcha, sfr/riv, drn, wel, oc |
| {name}.dis | Structured grid (delr/delc/top/botm/idomain) | LENGTH_UNITS meters |
| {name}.npf | Node property flow: k, k33, icelltype | K in m d⁻¹ |
| {name}.ic | Starting heads = land surface (top), all layers | m |
| {name}.sto | Storage; stress period 0 steady-state | — |
| {name}.rcha | Array recharge from SWAT+ annual-mean percolation | m d⁻¹ (mm yr⁻¹ ÷ 1000 ÷ 365.25); default 5.5e-4 |
| {name}.ghb | Perimeter general-head boundary to kriged water table | head m; conductance m² d⁻¹ (K × thickness) |
| {name}.sfr | Streamflow routing (default stream representation) | lengths m; bed K m d⁻¹; inflow m³ d⁻¹; TIME_CONVERSION 86400 |
| {name}.riv | RIV fallback only if the SFR build fails | stage/rbot m; conductance m² d⁻¹ |
| {name}.drn | Surface-seepage drains (default lake representation) | elevation m; conductance m² d⁻¹ (capped 1e5) |
| {name}.wel | Pumping wells from the wells inventory | flux m³ d⁻¹ (gpm × 5.678, negative = extraction) |
| {name}.oc | Output control | writes {name}.hds + {name}.cbc |
| {name}.hds / .cbc / .lst | Run outputs: heads, cell budgets, listing | heads m; budgets m³ (per day) |
| {name}.sfr.stage / .sfr.cbc | SFR stage and budget outputs | stage m; flows m³ d⁻¹ |
Companion files (not MF6 packages)
| File | Description |
|---|---|
| Grids_MODFLOW_centroids.parquet | Cell centroids (row, col, geometry) in the projected CRS — used for well placement and GIS joins |
| Grids_MODFLOW.geojson / Grids_MODFLOW/*.shp | Cell polygons (GeoJSON only when cell count ≤ 120,000; shapefile alongside) |
| obs_vs_sim.csv | Head observations vs simulated: wellid, row, col, obs_head_m, sim_head_m (obs = land surface − 0.3048 × SWL_ft) |
| obs_vs_sim.png | Observed-vs-simulated head scatter |
| head_calibration_report.json | Differential-evolution head calibration report (multipliers, NSE, cal/val metrics) — present only after the head-calibration pass, not the initial build |
| {name}_overview.png | Model overview figure |
| lake_raster.tif, swat_river.tif, rasters_input/ | Intermediate build rasters (included because the ZIP archives the whole folder) |
mf6.con and no exchange-map files: it is a one-way steady-state deliverable, not the research daily BMI/XMI coupling. See mf6.con and exchange maps below.mf6.con and exchange maps
The four TxtInOut files that activate daily two-way SWAT+/MODFLOW 6 BMI/XMI coupling: mf6.con and the mf6_recharge.map, mf6_baseflow.map, pfas_leach.map exchange maps.
Coupling activates when TxtInOut contains mf6.con and a coupler-enabled swatplus binary can dlopen libmf6.so. Absent mf6.con → plain SWAT+ (no-op). Comments after ! are stripped on every line of every coupling file.
mf6.con
./mf6 ! 1: folder (inside TxtInOut) holding mfsim.nam
1 ! 2: groundwater FLOW step, in days (1 = daily)
30 ! 3: groundwater TRANSPORT step, in days (30 = monthly)
1.0 ! 4: (optional) recharge multiplier for calibration
! 5: (optional) full path to libmf6 if not on the default search path| Line | Description | Type | Default |
|---|---|---|---|
| 1 | MF6 workspace path relative to TxtInOut (folder holding mfsim.nam) | path | "." (required in practice) |
| 2 | Groundwater FLOW step cadence in days (clamped to ≥ 1) | integer | 1 |
| 3 | Groundwater TRANSPORT step cadence in days (< 1 falls back to the flow cadence) | integer | 30 |
| 4 (optional) | Recharge multiplier rch_mult applied to every pushed recharge value | real | 1.0 |
| 5 (optional) | Full path to libmf6.so | path | bin/libmf6.so on the default search path |
mf6.con comments mislabel line 4 as the library path. The engine parser is the authority: line 4 is the recharge multiplier; line 5 is the optional library path.mf6_recharge.map — HRU → cell (down-coupling)
Optional; absent → MF6 keeps its own recharge. Header line, then one row per HRU–cell overlap link. Each day the coupler overwrites MF6 RECHARGE with Σ sepbtm(hru)/1000 × weight × rch_mult (SWAT+ mm/day → MF6 m/day).
| Field | Description | Type | Units |
|---|---|---|---|
| N_ENTRIES | Header: number of overlap links | integer | — |
| N_CELLS | Header: RECHARGE array size (NROW × NCOL) | integer | — |
| NCOL / N_HRU | Header: grid columns and max HRU id (written by the builder; unused by the coupler) | integers | — |
| cell_index | Row value 1: 0-based cell index (row × NCOL + col) | integer | — |
| hru | Row value 2: SWAT+ HRU id (1-based) | integer | — |
| weight | Row value 3: overlap area ÷ cell area | real | dimensionless |
mf6_baseflow.map — SFR reach → channel (up-coupling)
Optional; absent → up-coupling off. Header N_LINKS N_REACHES, then one row per link. The coupler reads SFR GWFLOW (m³/day; positive = stream→aquifer) and adds −GWFLOW as baseflow to the mapped SWAT+ channel, replacing native aquifer return to avoid double counting.
| Field | Description | Type | Units |
|---|---|---|---|
| N_LINKS | Header: number of reach→channel links | integer | — |
| N_REACHES | Header: SFR GWFLOW array length | integer | — |
| reach_index | Row value 1: 0-based SFR reach index | integer | — |
| gis_channel | Row value 2: SWAT+ channel GIS id | integer | — |
pfas_leach.map — HRU → GWT source cell (PFAS down-coupling)
Optional; requires the PFAS module and a GWT model with an SRC package. Daily push: SRC load = Σ perc(kg/ha) × overlap_ha × 1e9 (µg/day); GWT concentrations are treated as ng/L on the way back.
| Field | Description | Type | Units |
|---|---|---|---|
| N_ENTRIES | Header: number of HRU→source links | integer | — |
| N_SRCCELLS | Header: SRC BOUND array length | integer | — |
| src_index | Row value 1: 0-based SRC list index | integer | — |
| hru | Row value 2: SWAT+ HRU id | integer | — |
| overlap_ha | Row value 3: HRU–cell overlap area | real | ha |
How maps are produced, and coupled-run outputs
- Exchange maps are assembled offline from the SWAT+ HRU geometry and the MF6 grid (area-weighted HRU→cell for recharge / PFAS leach; SFR reach→channel for baseflow). They are not written by a standard website order.
- A research daily-coupling workspace also needs a daily-TDIS MF6 (and optional GWT+SRC) model next to
mf6.con— typically under TxtInOut/mf6/. - Coupled-run outputs under TxtInOut/mf6/: pfas.ucn (groundwater PFAS concentration per cell over time), pfas.lst (transport mass budget + percent discrepancy), MODFLOW_sfr.lst (flow budget incl. stream–aquifer exchange) — standard MODFLOW 6 formats.
- Ordinary website packages ship none of these coupling files; only the one-way sibling MODFLOW_250m archive is standard.
