Installs into .claude/skills of the current project.
Are you the author of CE QUAL W2?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/lzwei196-ce-qual-w2)
> **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:
> 1. Check `diagnostics/triplets.yaml` for a matching error pattern
> 2. Try the fix suggested in the triplet's `remedy` section
> 3. 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:
> 1. **Check triplets** — `diagnostics/triplets.yaml` may already cover this error
> 2. **Read official docs** — The model's own documentation for expected formats/units
> 3. **Find working examples** — Check `outputs/` or the model's shipped test data
> 4. **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:BEGIN (projected by generate_skill_map.py — edit the KI, not this table) -->
## 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/` (16 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` (6 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` (32 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` (20 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. |
*Projected 2026-10-03 from the KI's actual contents — 9 components present. Refresh: `python3 ki_tools_common/generate_skill_map.py --ki_dir <this KI>`.*
<!-- KI-MAP:END -->
<!-- KI-TOOL-INDEX:BEGIN (projected by generate_skill_map.py — the discoverability contract: every public tool, exact path; PURPOSE stays human-authored elsewhere) -->
### 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/s10_execution/run_w2.py` | `KISSPATH_PYTHON_ENV/bin/python {KI}/tools/s10_execution/run_w2.py --help` |
| `tools/s11_output_analysis/parse_w2_output.py` | `KISSPATH_PYTHON_ENV/bin/python {KI}/tools/s11_output_analysis/parse_w2_output.py --help` |
| `tools/s11_output_analysis/plot_w2_curtain.py` | `KISSPATH_PYTHON_ENV/bin/python {KI}/tools/s11_output_analysis/plot_w2_curtain.py --help` |
| `tools/s11_output_analysis/plot_w2_timeseries.py` | `KISSPATH_PYTHON_ENV/bin/python {KI}/tools/s11_output_analysis/plot_w2_timeseries.py --help` |
| `tools/s12_calibration/calibrate_w2.py` | `KISSPATH_PYTHON_ENV/bin/python {KI}/tools/s12_calibration/calibrate_w2.py --help` |
| `tools/s13_coupling/w2_to_cama_coupling.py` | `KISSPATH_PYTHON_ENV/bin/python {KI}/tools/s13_coupling/w2_to_cama_coupling.py --help` |
| `tools/s1_bathymetry/build_reservoir_grid.py` | `KISSPATH_PYTHON_ENV/bin/python {KI}/tools/s1_bathymetry/build_reservoir_grid.py --help` |
| `tools/s2_branch_topology/build_branch_topology.py` | `KISSPATH_PYTHON_ENV/bin/python {KI}/tools/s2_branch_topology/build_branch_topology.py --help` |
| `tools/s3_met_forcing/convert_met_to_w2.py` | `KISSPATH_PYTHON_ENV/bin/python {KI}/tools/s3_met_forcing/convert_met_to_w2.py --help` |
| `tools/s4_inflow/convert_inflow_to_w2.py` | `KISSPATH_PYTHON_ENV/bin/python {KI}/tools/s4_inflow/convert_inflow_to_w2.py --help` |
| `tools/s4_inflow/generate_distributed_inflow.py` | `KISSPATH_PYTHON_ENV/bin/python {KI}/tools/s4_inflow/generate_distributed_inflow.py --help` |
| `tools/s5_outflow/configure_w2_outflow.py` | `KISSPATH_PYTHON_ENV/bin/python {KI}/tools/s5_outflow/configure_w2_outflow.py --help` |
| `tools/s6_init_conditions/build_init_conditions.py` | `KISSPATH_PYTHON_ENV/bin/python {KI}/tools/s6_init_conditions/build_init_conditions.py --help` |
| `tools/s7_hydraulic_params/set_hydraulic_params.py` | `KISSPATH_PYTHON_ENV/bin/python {KI}/tools/s7_hydraulic_params/set_hydraulic_params.py --help` |
| `tools/s8_wq_config/configure_wq.py` | `KISSPATH_PYTHON_ENV/bin/python {KI}/tools/s8_wq_config/configure_wq.py --help` |
| `tools/s9_control_file/generate_w2_control.py` | `KISSPATH_PYTHON_ENV/bin/python {KI}/tools/s9_control_file/generate_w2_control.py --help` |
*16 public tools; `_`-prefixed helpers and packaging files excluded.*
<!-- KI-TOOL-INDEX:END -->
---
## Data Preparation
### Forcing data
**Met file**: one command, `tools/s3_met_forcing/convert_met_to_w2.py --source cmfd|mswx|nasa_power --lat .. --lon .. --start_year .. --end_year .. [--forcing_dir ..] --output <run_dir>/<met file name>`.
The tool reads the source itself through `ki_tools_common.load_forcing.load_hourly_forcing` (3-hourly for cmfd / mswx, hourly for nasa_power). Do NOT call the loader first and do NOT write the met file by hand. `--source` has no default.
- China: `--source cmfd`. Elsewhere: `--source mswx --forcing_dir KISSPATH_FORCING` (that disk is exfat: one reader at a time, never in parallel). `nasa_power` (network, 2001 onward) only when no local store covers the case.
- The tool makes no value up: a missing value, an uneven time axis or a period the source does not cover stops it with nothing written.
- Read `<output>.summary.json`: it says what was estimated (dew point from specific humidity and pressure; cloud from short-wave, one value per local day; wind direction is a constant because the sources have none; time moved from UTC to local standard time).
- CE-QUAL-W2 input is never made from another model's input files (dt_026). Details: `docs/s3_met_forcing_skill.md`.
---
# CE-QUAL-W2 v4.5 — Knowledge Infrastructure
**Package**: `hydrocraft-cequalw2-reservoir` v1.0.0
**Model**: CE-QUAL-W2 v4.5 (2D laterally-averaged hydrodynamic and water quality)
**Created by**: Jianyun Zhang Research Group, Hohai University
**Last updated**: 2026-03-24
**Stats**: 16 tools | 6 skill documents | 32 diagnostic triplets | 3,434 lines of validated Python
**Validation status**: `binary_only` (pending source compilation and example validation)
---
## Overview
This knowledge infrastructure enables autonomous 2D reservoir simulation using CE-QUAL-W2, the U.S. Army Corps of Engineers' standard laterally-averaged hydrodynamic and water quality model. CE-QUAL-W2 resolves **longitudinal and vertical** gradients in elongated reservoirs where 1D models (like GLM) are physically inadequate.
**What CE-QUAL-W2 does**: 2D laterally-averaged model for reservoirs, rivers, and estuaries. Simulates:
- Thermal stratification with longitudinal AND vertical resolution
- Density-driven inflow routing (plunging, interflow, overflow)
- Multi-branch topology (main stem + tributary arms)
- Selective withdrawal from multiple dam outlets at different elevations
- 21+ water quality constituents (temperature, DO, nutrients, algae, organic matter, sediment)
- Ice cover dynamics
- Adaptive CFL-limited timestepping
**Key difference from GLM (1D)**: CE-QUAL-W2 explicitly resolves longitudinal gradients. Use for:
- Reservoirs with length-to-width ratio > 5:1
- Reservoirs with multiple inflow tributaries at different locations
- Multi-level selective withdrawal (e.g., Three Gorges: outlets at 90m, 120m, 155m)
- Turbidity current and density current routing
**When GLM is sufficient**: Compact/round lakes (L:W < 5), quick assessments, global screening.
---
## 6. Output Description
**Source of truth**: `dag.yaml`. The dag is the model identity for outputs; if this section and `dag.yaml` disagree, `dag.yaml` wins and this section must be corrected.
**Headline output** (the dag's `validation_rank: 1` variable, used as the primary judging variable):
> `outflow discharge` -- withdrawal/dam-release discharge used for downstream coupling (`m^3/s`)
| Output variable (dag `var`) | Validation rank | Unit | Description / notes |
|-----------------------------|----------------:|------|---------------------|
| `outflow discharge` | 1 | `m^3/s` | withdrawal/dam-release discharge used for downstream coupling |
| `water temperature` | not restated in extracted facts | see `dag.yaml` | Other dag output. |
| `dam release temperature` | not restated in extracted facts | see `dag.yaml` | Other dag output. |
| `dissolved oxygen` | not restated in extracted facts | see `dag.yaml` | Other dag output. |
| `water surface elevation` | not restated in extracted facts | see `dag.yaml` | Other dag output. |
| `chlorophyll-a / algal biomass` | not restated in extracted facts | see `dag.yaml` | Other dag output. |
| `ice thickness` | not restated in extracted facts | see `dag.yaml` | Other dag output. |
Use `outflow discharge` for downstream coupling and headline validation decisions unless the dag is intentionally revised and regenerated.
---
## Installation
### Binary
```
CE-QUAL-W2: KISSPATH_BINARIES/ce_qual_w2/bin/w2_v5
Source: model/ce_qual_w2/src/ (TO BE CLONED from GitHub)
Repository: https://github.com/EnvironmentalSystems/CE-QUAL-W2
```
**The binary on this server is v5** (`w2_v5`). It reads `w2_con.csv` (not `w2_con.npt`), CSV or fixed-width input files, and writes `tsr_<n>_seg<segment>.csv`, `snp.opt`, `spr.csv`; stop reasons go to `w2.err` (dt_032). The shipped working case is `model/ce_qual_w2/examples/DeGray/` (1980, JDAY 64.5-358.7): copy its input files into a run folder, swap what you need (for example the met file from `convert_met_to_w2.py`, under the name the control file expects), and run `tools/s10_execution/run_w2.py --run_dir <folder>`. `generate_w2_control.py` writes the v4.x `w2_con.npt` form, which this binary does not read.
### Compilation from Source
```bash
# Clone source
git clone https://github.com/EnvironmentalSystems/CE-QUAL-W2.git model/ce_qual_w2/src
# Compile (exact steps depend on repo structure)
cd model/ce_qual_w2/src
gfortran -O2 -ffree-line-length-none -o ../bin/w2_v5 *.f90
# Or if Makefile exists: make
# Dependencies
sudo apt install gfortran libnetcdf-dev libnetcdff-dev
```
**Known compilation pitfalls**:
- Some source files assume Intel Fortran (`ifort`) — add `-fallow-argument-mismatch` for gfortran
- Mixed fixed-form (.f) and free-form (.f90) files may need `-ffixed-line-length-132`
- Array dimension limits may be hardcoded as PARAMETER — increase for large reservoirs
- CHARACTER path widths may need expanding beyond 72 for HydroCraft directory structure
### Python Dependencies (all in HydroCraft venv)
```
numpy, pandas, xarray, netCDF4, geopandas, shapely, rasterio, matplotlib, scipy
```
---
## Pipeline (14 stages)
| # | Stage | Tool(s) | Description |
|---|-------|---------|-------------|
| 0 | Configuration | (manual) | Reservoir selection, period, forcing, WQ toggle |
| 1 | Bathymetry | `build_reservoir_grid` | DEM/idealized geometry to segment-layer grid + bth_wb*.npt |
| 2 | Branch topology | `build_branch_topology` | Multi-branch connectivity, slopes, segment ranges |
| 3 | Met forcing | `convert_met_to_w2` | CMFD / MSWX / NASA POWER (direct, `--source`) to W2 v5 met file (**cloud in tenths 0-10!**) |
| 4 | Inflow | `convert_inflow_to_w2`, `generate_distributed_inflow` | CaMa/VIC discharge + temperature |
| 5 | Outflow | `configure_w2_outflow` | Selective withdrawal, dam outlets, outflow timeseries |
| 6 | Init conditions | `build_init_conditions` | 2D initial temperature/WQ fields |
| 7 | Hydraulic params | `set_hydraulic_params` | AX, DX, WSC, CBHE, TSED, EXH2O auto-estimated |
| 8 | WQ config | `configure_wq` | Constituent activation, kinetic rates, algae groups |
| 9 | Control file | `generate_w2_control` | Assemble w2_con.npt (**8-char fixed-width!**) |
| 10 | Execution | `run_w2` | Preflight checks, run binary (`w2_con.csv` for v5, `w2_con.npt` for v4.x), read `w2.err` |
| 11 | Output analysis | `parse_w2_output`, `plot_w2_curtain`, `plot_w2_timeseries` | Parse + visualize |
| 12 | Calibration | `calibrate_w2` | GLUE-style against observed temperature profiles |
| 13 | Coupling | `w2_to_cama_coupling` | Dam release to CaMa-Flood downstream |
### Parallelism
Stages 1-8 can largely run in parallel after stage 0 (with s2 depending on s1, s4 depending on s3).
Stage 9 depends on ALL of s1-s8.
Stage 10 depends on s9. Stages 11-13 depend on s10.
---
## 8. Unit Conversion Table
**Exact I/O shapes live in `docs/format_spec.yaml`**. This table restates unit conversions and unit traps already captured in this KI body and diagnostics; do not add rows from memory. If a needed conversion is absent, read the stage document, `docs/format_spec.yaml`, and the relevant tool before running.
| Variable / field | Source unit or representation | Model / coupling unit or representation | Conversion / handling | Source in this KI |
|------------------|-------------------------------|------------------------------------------|-----------------------|-------------------|
| Cloud cover | fraction `0-1` when supplied as a common forcing representation | tenths `0-10` | multiply by `10`; CE-QUAL-W2 reads cloud cover as tenths | `dt_001`; `convert_met_to_w2` stage notes |
| Humidity for dewpoint | specific humidity `kg/kg` + pressure `Pa` (cmfd, mswx, nasa_power through the loader) | dewpoint temperature, `deg C` | `e = q p / (0.622 + 0.378 q)`, then `TDEW = (237.3 * ln(VP/0.6108)) / (17.27 - ln(VP/0.6108))` with `VP` in kPa; done by `convert_met_to_w2` | `dt_002`; `dt_027` |
| Wind direction PHI | none in the sources | radians | constant `--wind_dir_deg` (default 270) written as radians (4.712); said in the summary | `dt_031` |
| Met time stamp | UTC in the sources | `JDAY` in local standard time | add `int(lon/15)` hours (the model's own standard-meridian rule) | `dt_031` |
| Vapor pressure for dewpoint | Pa for ERA5 | kPa intermediate, then dewpoint temperature, `deg C` | divide by `1000`, then apply the `TDEW` formula | `dt_002`; Critical Domain Knowledge |
| Julian day | day plus clock time | decimal `JDAY` | `day_of_year + hour/24 + minute/1440` | `dt_005`; Critical Domain Knowledge |
| Inflow discharge | source-dependent; wrong source units such as `mm/day` are a known trap | `m^3/s` | use `tools/s4_inflow/convert_inflow_to_w2.py`; do not apply a generic factor without source area and source metadata | `dt_003`; Pipeline stage 4 |
| Outflow / dam release discharge | configured or simulated dam release | `m^3/s` | preserve `m^3/s` for downstream coupling; headline dag output is `outflow discharge` | `dag.yaml`; Pipeline stage 13 |
| Output file paths | absolute or long paths | Fortran `CHARACTER*72` path fields | use relative paths or basenames to avoid truncation | `dt_008`; Critical Domain Knowledge |
| Control-file numeric fields | Python numeric values | 8-character fixed-width Fortran fields | format as exactly 8 characters, right-justified, for example `{:>8.2f}` | `dt_006`; Critical Domain Knowledge |
**Output unit verification checklist:**
- Read `dag.yaml` before interpreting output units; the dag wins over prose.
- For `outflow discharge`, expect `m^3/s` and treat it as the dam-release discharge used for downstream coupling.
- Print first values before scoring or coupling; values that imply sudden full drainage or filling are a unit error until proven otherwise.
---
## Tools Reference
| Tool | Stage | Script Path | Lines | Purpose |
|------|-------|-------------|------:|---------|
| `build_reservoir_grid` | s1 | `tools/s1_bathymetry/build_reservoir_grid.py` | 310 | DEM or idealized geometry to segment-layer grid |
| `build_branch_topology` | s2 | `tools/s2_branch_topology/build_branch_topology.py` | 150 | Branch connectivity and slopes |
| `convert_met_to_w2` | s3 | `tools/s3_met_forcing/convert_met_to_w2.py` | 330 | cmfd / mswx / nasa_power to W2 v5 met file (q+p->TDEW, cloud 0-10, PHI rad, local time) |
| `convert_inflow_to_w2` | s4 | `tools/s4_inflow/convert_inflow_to_w2.py` | 250 | CaMa/VIC to W2 inflow files (qin + tin + cin) |
| `generate_distributed_inflow` | s4 | `tools/s4_inflow/generate_distributed_inflow.py` | 160 | SYNTHETIC distributed tributary flow (qdt + tdt), needs `--synthetic`; reads no runoff result |
| `configure_w2_outflow` | s5 | `tools/s5_outflow/configure_w2_outflow.py` | 130 | Dam outlet config + outflow timeseries |
| `build_init_conditions` | s6 | `tools/s6_init_conditions/build_init_conditions.py` | 130 | 2D initial T/WQ fields |
| `set_hydraulic_params` | s7 | `tools/s7_hydraulic_params/set_hydraulic_params.py` | 100 | Auto-estimate AX, DX, WSC from geometry |
| `configure_wq` | s8 | `tools/s8_wq_config/configure_wq.py` | 130 | WQ constituent activation and rates |
| `generate_w2_control` | s9 | `tools/s9_control_file/generate_w2_control.py` | 350 | Assemble w2_con.npt with validation |
| `run_w2` | s10 | `tools/s10_execution/run_w2.py` | 180 | Execute with preflight + postrun checks |
| `parse_w2_output` | s11 | `tools/s11_output_analysis/parse_w2_output.py` | 190 | Parse snapshot/timeseries/spreadsheet output |
| `plot_w2_curtain` | s11 | `tools/s11_output_analysis/plot_w2_curtain.py` | 180 | 2D longitudinal-vertical curtain plots |
| `plot_w2_timeseries` | s11 | `tools/s11_output_analysis/plot_w2_timeseries.py` | 120 | Time series at specific locations |
| `calibrate_w2` | s12 | `tools/s12_calibration/calibrate_w2.py` | 130 | GLUE calibration (WSC, EXH2O, AX, CBHE, TSED) |
| `w2_to_cama_coupling` | s13 | `tools/s13_coupling/w2_to_cama_coupling.py` | 120 | W2 outflow to CaMa-Flood |
**Total**: 16 tools, 3,434 lines of validated Python code.
---
## Critical Domain Knowledge
These non-obvious facts cause **silent failures** if violated. Each has a diagnostic triplet.
### 1. Cloud cover is in TENTHS (0-10), NOT fraction (0-1) (dt_001)
CE-QUAL-W2 reads cloud cover as 0-10. If you pass 0-1 (fraction), the model sees near-clear sky always, resulting in excessive shortwave radiation and water temperatures 3-5 C too warm. This is the #1 silent error for CE-QUAL-W2. There is NO error message.
### 2. Dewpoint, not relative humidity (dt_002)
CE-QUAL-W2 expects dewpoint temperature (TDEW, deg C), not RH or VP directly.
Formula: `TDEW = (237.3 * ln(VP/0.6108)) / (17.27 - ln(VP/0.6108))` with VP in kPa.
VP must be in kPa. The forcing sources give specific humidity and pressure, not VP: `convert_met_to_w2` does `e = q p / (0.622 + 0.378 q)` first.
### 3. Julian day is DECIMAL, not integer (dt_005)
JDAY = day_of_year + hour/24 + minute/1440. So 1.0 = midnight Jan 1, 1.5 = noon Jan 1.
Integer JDAY shifts the diurnal radiation cycle and causes systematic temperature errors.
### 4. w2_con.npt uses 8-character fixed-width fields (dt_006)
This is the single most dangerous format requirement. Fortran reads by COLUMN POSITION.
Off-by-one alignment shifts ALL subsequent values silently. Every numeric field must be
exactly 8 characters, right-justified. Use `{:>8.2f}` in Python.
### 5. File paths must be < 72 characters (dt_008)
Fortran CHARACTER*72 path variables silently truncate longer paths. Use relative paths
or symlinks. `generate_w2_control.py` uses `os.path.basename()` to mitigate this.
### 6. Bottom elevations must be monotonically non-increasing downstream (dt_013)
If a downstream segment has a HIGHER bottom elevation than an upstream segment, water
gets trapped in a depression. The model runs but produces physically wrong longitudinal
gradients.
### 7. Constituent ON/OFF flags must match inflow file columns exactly (dt_015)
If the CONSTITU card has N constituents ON, the cin_br*.npt file must have exactly N
data columns (plus JDAY). A mismatch causes a Fortran column shift that silently
corrupts all constituent concentrations.
### 8. Avoid double-counting runoff in coupling (dt_021)
When using both CaMa-Flood main channel inflow AND distributed tributary inflow,
subtract the main inflow contributing area from the distributed tributary area.
Otherwise the reservoir receives double the water input.
---
## Coupling Points
| # | Source | Target | Variable | Tool |
|---|--------|--------|----------|------|
| 1 | CaMa-Flood | CE-QUAL-W2 | Upstream discharge | `convert_inflow_to_w2` |
| 3 | VIC (or any runoff result) | CE-QUAL-W2 | Local runoff as tributary inflow | `convert_inflow_to_w2` (real series). `generate_distributed_inflow --synthetic` writes a synthetic seasonal curve only; it reads no runoff result |
| 4 | CE-QUAL-W2 | CaMa-Flood | Dam release discharge | `w2_to_cama_coupling` |
| 5 | CE-QUAL-W2 | CaMa-Flood | Dam release temperature | `w2_to_cama_coupling` |
| 6 | SWAT+ | CE-QUAL-W2 | Upstream nutrient loading | (via cin_br*.npt) |
| 7 | GLM | CE-QUAL-W2 | 1D vs 2D comparison | (shared forcing + inflow) |
| 8 | CMIP6 | CE-QUAL-W2 | Future climate forcing | (delta-change on met files) |
### Auto-Selection: CE-QUAL-W2 vs GLM
```python
if reservoir_length_km / reservoir_width_km > 5:
recommend = "CE-QUAL-W2"
elif multiple_inflow_locations:
recommend = "CE-QUAL-W2"
elif multi_level_selective_withdrawal:
recommend = "CE-QUAL-W2"
else:
recommend = "GLM"
```
---
## Calibration Parameters (Priority Order)
| Parameter | Range | Controls | Sensitivity |
|-----------|-------|----------|-------------|
| WSC | 0.5 - 1.0 | Wind sheltering, surface mixing | HIGH |
| EXH2O | 0.1 - 1.0 m^-1 | Light extinction, thermocline depth | HIGH |
| AX | 0.1 - 10 m^2/s | Longitudinal mixing | MEDIUM |
| CBHE | 0.3 - 1.5 W/m^2/C | Bottom heat exchange | MEDIUM |
| TSED | 5 - 15 C | Sediment temperature | LOW |
---
## 11. Validated Results
**Validation status**: `binary_only` (pending source compilation and example validation). Do not report a numeric model skill verdict unless the actual CE-QUAL-W2 binary/package has run and the result has been scored against `docs/validation_convention.yaml`.
### Field Convention Bars
These bars restate the KI's validation convention facts. Direction is `minimize`, so lower RMSE is better. Every threshold below carries its convention citation keys; an unstated band is written as `no cited threshold`.
| Dag variable | Metric | Direction | Very good | Good | Satisfactory |
|--------------|--------|-----------|-----------|------|--------------|
| `outflow discharge` | no cited threshold | no cited threshold | no cited threshold | no cited threshold | no cited threshold |
| `water temperature` | RMSE | minimize | `<= 1.0` (`mi2019`; `shatwell2019`; `shabani2021`) | `<= 1.23` (`mi2019`; `shatwell2019`; `shabani2021`) | `<= 1.5` (`mi2019`; `shatwell2019`; `shabani2021`) |
| `dam release temperature` | RMSE | minimize | `<= 1.0` (`mi2019`; `shabani2021`) | `<= 1.23` (`mi2019`; `shabani2021`) | `<= 1.5` (`mi2019`; `shabani2021`) |
### Current Validated Runs
| Test basin / run | Headline variable | Achieved metric | Verdict |
|------------------|-------------------|-----------------|---------|
| Pending validation run | `outflow discharge` | no cited threshold | no cited threshold |
### Validation Procedure
1. Run `python preflight_check.py` in this KI directory before attempting validation.
2. Run the actual CE-QUAL-W2 binary/package through `tools/s10_execution/run_w2.py`; do not substitute formulas or simplified scripts.
3. Parse outputs with `tools/s11_output_analysis/parse_w2_output.py`.
4. Score against `docs/validation_convention.yaml`; for headline judgement, start from the dag rank-1 variable `outflow discharge`.
5. If scoring `water temperature` or `dam release temperature`, use the RMSE minimize bands above exactly.
---
## Quick Start (Idealized Reservoir)
```bash
source KISSPATH_PYTHON_ENV/bin/activate
cd KISSPATH_KI_ROOT/CE_QUAL_W2/knowledge_infrastructure
# 1. Build idealized grid (no DEM needed)
python tools/s1_bathymetry/build_reservoir_grid.py \
--idealized --reservoir_length_km 80 --max_depth 80 \
--surface_area_km2 745 --dam_elevation 170 \
--segment_length 2000 --layer_thickness 2.0 \
--output_dir /tmp/w2_test
# 2. Build the met file straight from the forcing source (China: cmfd)
python tools/s3_met_forcing/convert_met_to_w2.py \
--source cmfd \
--forcing_dir KISSPATH_DATA/forcing/Data_forcing_03hr_010deg \
--lat 32.54 --lon 111.51 --start_year 2005 --end_year 2010 \
--output /tmp/w2_test/met_wb1.npt
# 3. Generate inflow
python tools/s4_inflow/convert_inflow_to_w2.py \
--discharge_csv routing_output.csv \
--met_file /tmp/w2_test/met_wb1.npt \
--start_year 2005 --end_year 2010 \
--output_dir /tmp/w2_test
# 4. Configure outflow
python tools/s5_outflow/configure_w2_outflow.py \
--mode constant --outflow_m3s 50 --outlet_elevation 120 \
--start_jday 1 --end_jday 365 --output_dir /tmp/w2_test
# 5. Generate control file
python tools/s9_control_file/generate_w2_control.py \
--grid_json /tmp/w2_test/reservoir_grid.json \
--met_file /tmp/w2_test/met_wb1.npt \
--qin_files /tmp/w2_test/qin_br1.npt \
--year 2005 --output /tmp/w2_test/w2_con.npt
# 6. Run CE-QUAL-W2
python tools/s10_execution/run_w2.py --run_dir /tmp/w2_test
# 7. Analyze output
python tools/s11_output_analysis/plot_w2_curtain.py \
--run_dir /tmp/w2_test \
--grid_json /tmp/w2_test/reservoir_grid.json \
--output /tmp/w2_test/curtain.png
```
---
## Diagnostic Triplets
32 triplets across 8 failure domains. See `diagnostics/triplets.yaml` for full details.
| ID | Severity | Domain | Summary |
|----|----------|--------|---------|
| dt_001 | **silent** | unit_conversion | Cloud cover as fraction 0-1 instead of tenths 0-10 (3-5 C warm bias) |
| dt_002 | **silent** | unit_conversion | VP units wrong in dewpoint conversion (TDEW = -40 C) |
| dt_003 | **silent** | unit_conversion | Inflow discharge units (mm/day vs m^3/s, 10^5x error) |
| dt_004 | **silent** | unit_conversion | Wind direction degrees vs radians |
| dt_005 | **silent** | unit_conversion | Julian day integer vs decimal (diurnal cycle wrong) |
| dt_006 | **silent** | parameter_format | 8-char column misalignment in w2_con.npt |
| dt_007 | fatal | parameter_format | Bathymetry column widths wrong |
| dt_008 | fatal | path_resolution | File path exceeds Fortran CHARACTER*72 limit |
| dt_009 | fatal | dependency_mismatch | Input files shorter than simulation period |
| dt_010 | fatal | grid_geometry | Active segment has zero width at all layers |
| dt_011 | fatal | grid_geometry | CFL violation from short segments |
| dt_012 | fatal | grid_geometry | Invalid branch DHS reference |
| dt_013 | **silent** | grid_geometry | Bottom elevation increases downstream |
| dt_014 | fatal | grid_geometry | ELWS below bottom elevation at upstream segments |
| dt_015 | **silent** | dependency_mismatch | Constituent ON/OFF mismatch with inflow file columns |
| dt_016 | **silent** | silent_error | SOD too high — instant anoxia |
| dt_017 | **silent** | silent_error | Algal growth rate too high — unrealistic bloom |
| dt_018 | **silent** | unit_conversion | Outlet elevation in wrong reference frame |
| dt_019 | **silent** | unit_conversion | Outflow units wrong (reservoir drains/fills unrealistically) |
| dt_020 | **silent** | coupling | CaMa inflow at wrong segment |
| dt_021 | **silent** | coupling | Double-counting VIC + CaMa runoff |
| dt_022 | degraded | runtime | Timestep drops to < 1s (slow) |
| dt_023 | fatal | runtime | NaN from bathymetry gradient instability |
| dt_024 | fatal | runtime | NEGATIVE THICKNESS during drawdown |
| dt_025 | **silent** | silent_error | No output despite successful completion |
| dt_026 | fatal | dependency_mismatch | Met file made from VIC forcing files (route removed; use `--source`) |
| dt_027 | **silent** | silent_error | Old direct reader filled made-up values (TAIR 0, VP 0.8 kPa, wind 2.0, SW 0) |
| dt_028 | fatal | dependency_mismatch | NASA POWER named but had no reader |
| dt_029 | **silent** | dependency_mismatch | sys.path line could load an old ki_tools_common |
| dt_030 | degraded | dependency_mismatch | "load_daily_forcing, then the tool" recipe could not be followed |
| dt_031 | **silent** | parameter_format | Met file form for v5: CSV with `$`, PHI in radians, JDAY local time, SRO needs SROC ON |
| dt_032 | fatal | dependency_mismatch | run/parse tools knew only v4.x names (`w2_con.npt`, `tsr_*.opt`) |
**Silent error count**: 18/32 (56%) — consistent with lake model error rates.
---
## File Structure
```
models/CE_QUAL_W2/knowledge_infrastructure/
DISSECTION_PLAN.md # Original dissection plan
SKILL.md # This file (agent entry point)
knowledge_infrastructure.yaml # Schema-compliant package definition
tools/
s1_bathymetry/
build_reservoir_grid.py # DEM/idealized to segment-layer grid
s2_branch_topology/
build_branch_topology.py # Branch connectivity
s3_met_forcing/
convert_met_to_w2.py # cmfd/mswx/nasa_power to W2 v5 met (cloud 0-10!)
s4_inflow/
convert_inflow_to_w2.py # CaMa/VIC to W2 inflow
generate_distributed_inflow.py # Distributed tributary flow
s5_outflow/
configure_w2_outflow.py # Dam outlet configuration
s6_init_conditions/
build_init_conditions.py # 2D initial T/WQ
s7_hydraulic_params/
set_hydraulic_params.py # AX, DX, WSC auto-estimation
s8_wq_config/
configure_wq.py # Constituent activation + rates
s9_control_file/
generate_w2_control.py # w2_con.npt assembly
s10_execution/
run_w2.py # Run W2 with preflight checks
s11_output_analysis/
parse_w2_output.py # Parse fixed-width output files
plot_w2_curtain.py # 2D curtain plots
plot_w2_timeseries.py # Time series plots
s12_calibration/
calibrate_w2.py # GLUE-style calibration
s13_coupling/
w2_to_cama_coupling.py # W2 -> CaMa-Flood coupling
docs/
s0_configuration_skill.md
s1_bathymetry_skill.md
s3_met_forcing_skill.md
s9_control_file_skill.md
s10_execution_skill.md
s11_output_analysis_skill.md
diagnostics/
triplets.yaml # 32 diagnostic triplets
model/ce_qual_w2/
bin/w2_v5 # CE-QUAL-W2 executable (TO BE COMPILED)
src/ # Fortran source (TO BE CLONED)
examples/ # Reference examples
docs/ # User manual PDF
```
---
## Target Validation Reservoirs
| Reservoir | Length | Max Depth | Branches | Priority |
|-----------|--------|-----------|----------|----------|
| **Danjiangkou** (丹江口) | 80 km | 80 m | 2 (Han + Dan) | HIGH (S-N Water Transfer) |
| **Three Gorges** (三峡) | 660 km | 175 m | 1+ | MEDIUM (large, expensive) |
| **Miyun** (密云) | ~20 km | 60 m | 2 (Chao + Bai) | HIGH (compare with GLM) |
| **DeGray Lake** (Arkansas) | ~25 km | 60 m | 1 | HIGH (USACE reference example) |