> **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.
>
>
> 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/` (7 tools; the REAL engine is `tools/run_hms_engine.py`) | 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` (7 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` (22 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-06 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/calibrate_hms.py` (**SURROGATE, not HEC-HMS**) | `KISSPATH_PYTHON_ENV/bin/python {KI}/tools/calibrate_hms.py --help` |
| `tools/convert_forcing_to_hms.py` | `KISSPATH_PYTHON_ENV/bin/python {KI}/tools/convert_forcing_to_hms.py --help` |
| `tools/convert_soil_to_hms.py` | `KISSPATH_PYTHON_ENV/bin/python {KI}/tools/convert_soil_to_hms.py --help` |
| `tools/parse_hms_output.py` | `KISSPATH_PYTHON_ENV/bin/python {KI}/tools/parse_hms_output.py --help` |
| `tools/run_hec_hms.py` (**SURROGATE, not HEC-HMS**) | `KISSPATH_PYTHON_ENV/bin/python {KI}/tools/run_hec_hms.py --help` |
| `tools/run_hms_engine.py` (**default run route: the REAL engine**) | `KISSPATH_PYTHON_ENV/bin/python {KI}/tools/run_hms_engine.py --help` |
| `tools/validate_hms.py` | `KISSPATH_PYTHON_ENV/bin/python {KI}/tools/validate_hms.py --help` |

*7 public tools; `_`-prefixed helpers and packaging files excluded.*
<!-- KI-TOOL-INDEX:END -->

# HEC-HMS (Hydrologic Engineering Center — Hydrologic Modeling System) — Knowledge Infrastructure

**Package**: `hydrocraft-hec-hms` v1.0.0
**Model**: HEC-HMS 4.14 (USACE Hydrologic Engineering Center), official Linux build, run headless by `tools/run_hms_engine.py`
**Created by**: Jianyun Zhang Research Group, Hohai University
**Last updated**: 2026-03-28
**Stats**: current tool, stage-doc and triplet counts: the KI map at the top of this file
**Validation status (2026-10-06)**: the REAL engine reproduces the USACE castro sample exactly (runs Current + Future, 54/54 element peaks/volumes/peak times; Outlet peak 540.2749050 cfs) and, for run Current, scores NSE 0.858 / KGE 0.811 / r 0.933 / PBIAS +0.98 % against the observed Castro Valley outlet flow of 16 Jan 1973 (USACE parameters, no calibration by us; see Validation).

---

## Data Preparation

### Forcing data

**Data Sources**: `convert_forcing_to_hms.py` reads daily CMFD NetCDFs with declared units and geographic coordinates in declared degrees; partial-day values (time bounds/steps not a whole day) and values outside NetCDF valid_range/valid_min/valid_max fail. It requires an explicit `--pet_csv` (`date,pet_mm`, mm/day); there is no automatic Hargreaves fallback or assumed temperature range. Missing required dates/active cells fail before writing. Temperature is required by default; `--temperature_mode unused` explicitly omits it only for a model with no temperature-dependent process. See [S4](docs/s4_forcing_conversion_skill.md).

**Data Validation Reference**: See `data_ki/CMFD/SKILL.md` for CMFD unit documentation and known traps.
See `data_ki/HWSD/SKILL.md` for soil property documentation.
See `data_ki/ObservedQ/SKILL.md` for observed discharge data.


## Overview

**Real engine**: `tools/run_hms_engine.py` runs the official USACE HEC-HMS 4.14 binary headless (Jython API: `Project.open` -> `computeRun`) on an HMS project folder, checks the run log and a fresh results file, exports DSS series to CSV and can compare against reference results. This is the model of record.

**SURROGATE**: `tools/run_hec_hms.py` (and `calibrate_hms.py`, which calls it) is a HydroCraft Python stand-in for SCS-CN + SCS UH + Linear Reservoir + Muskingum. It is NOT HEC-HMS; never report its output as a HEC-HMS result. It stays only for the legacy daily CSV workflow (Quick Start steps 1-6). The forcing/soil tools (`convert_*_to_hms.py`) still write CSV/JSON for the surrogate; they do not yet write HMS `.basin`/`.met`/`.gage`/DSS files, so a new basin for the real engine needs an HMS project built outside these tools (open gap).

**What HEC-HMS does**: Event-based and continuous rainfall-runoff simulation. Computes:
- Precipitation losses (infiltration) via SCS Curve Number, Green-Ampt, or Initial+Constant
- Direct runoff transform via SCS Unit Hydrograph, Clark UH, Snyder UH, or ModClark
- Channel routing via Muskingum, Muskingum-Cunge, Modified Puls, or Lag
- Baseflow via Linear Reservoir, Recession, or Constant Monthly
- Snowmelt via Temperature Index or Energy Budget
- Evapotranspiration via Monthly Average, Priestley-Taylor, or Gridded
- Reservoir routing via specified release, elevation-storage-discharge

**Key difference from other HydroCraft models**: HEC-HMS is a lumped/semi-distributed event or continuous model. Unlike VIC (gridded, physically-based) or SWAT (HRU-based), HEC-HMS operates on subbasins connected by channel reaches. Each subbasin is treated as a lumped unit. The model is excellent for flood forecasting and design storm analysis but does not resolve spatial variability within subbasins.

---

## Installation

### Official USACE executable (required for HEC-HMS results)

```
HEC-HMS 4.14:    KISSPATH_HOME/engine_builds_20261006/HEC_HMS/HEC-HMS-4.14/ (bundled JRE)
Headless launcher (KI binary): KISSPATH_HOME/engine_builds_20261006/HEC_HMS/run_hms_headless.sh
                 (same java call as hec-hms.sh + -Djava.awt.headless=true, no DISPLAY check; env HMS_ENGINE overrides)
Build log:       KISSPATH_HOME/engine_builds_20261006/HEC_HMS/BUILD_LOG.md
Source:          https://github.com/HydrologicEngineeringCenter/hec-downloads/releases/download/1.0.47/HEC-HMS-4.14-linux64.tar.gz
License:         US Government Public Domain (binary distribution)
KI run tool:     python3 tools/run_hms_engine.py --project <p>.hms --run <Run> --workdir <dir>
Official samples: HEC-HMS-4.14/samples.zip (castro, river_bend, tenk, tifton)
```

### Python SURROGATE (HydroCraft, not HEC-HMS)

```
Tools:           ki/tools/*.py
Dependencies:    numpy, pandas, xarray, netCDF4, geopandas, shapely, matplotlib, scipy
Python:          3.10+
```

The official HEC-HMS distribution is available without charge from USACE. The HydroCraft Python stand-in is a SURROGATE; no equivalence with the real engine has been shown.

### Test example (real engine — official castro sample)

```
mkdir -p /tmp/hms_samples && cd /tmp/hms_samples && unzip -q KISSPATH_HOME/engine_builds_20261006/HEC_HMS/HEC-HMS-4.14/samples.zip 'samples/castro/*'
python3 tools/run_hms_engine.py --project /tmp/hms_samples/samples/castro/castro.hms \
  --run Current --run Future --workdir /tmp/hms_castro_run --export Outlet:FLOW \
  --reference_results /tmp/hms_samples/samples/castro/results
# expect exit 0, reference n_mismatch 0, Outlet peak 540.2749050 cfs
```

### Surrogate test example (NOT HEC-HMS)

```
python3 ki/tools/run_hec_hms.py \
  --basin_shp /path/to/basin.shp \
  --forcing_dir /path/to/forcing/ \
  --soil_file /path/to/soil.img \
  --landcover_file /path/to/landcover.tif \
  --start_date 1980-01-01 --end_date 1990-12-31 \
  --output_dir /path/to/output/
```

---

## Pipeline

| # | Stage | Tool | Parallel | Notes |
|---|-------|------|----------|-------|
| 0 | Configuration | — | — | Set basin, period, resolution, paths |
| 1 | Domain Setup | — | — | Define subbasin(s) from DEM + shapefile |
| 2 | Soil Parameters | `convert_soil_to_hms.py` | Yes | HWSD → SCS CN, Soil Group, Green-Ampt params |
| 3 | Land Cover | `convert_soil_to_hms.py` | Yes | AVHRR → CN adjustment by land use |
| 4 | Forcing Conversion | `convert_forcing_to_hms.py` | Yes | CMFD/MSWX → HEC-HMS precipitation + PET |
| 5 | Model Parameters | — | — | Set loss, transform, routing, baseflow methods |
| 6 | Model Execution (REAL engine) | `run_hms_engine.py` | No | HEC-HMS 4.14 headless on an HMS project; log + fresh-results checks; DSS export to CSV |
| 6s | Model Execution (SURROGATE) | `run_hec_hms.py` | No | Python stand-in, NOT HEC-HMS (legacy daily CSV workflow only) |
| 7 | Output Parsing | `run_hms_engine.py` exports / `parse_hms_output.py` (surrogate CSV) | No | Discharge time series to CSV |
| 8 | Routing (optional) | — | — | Channel routing already integrated in run |

---

## Output Description

This section restates the KI dag output facts. If this body and `dag.yaml` ever
disagree, `dag.yaml` is the source of truth.

**Headline output** (`validation_rank: 1` in `dag.yaml`):

> `discharge` — Simulated routed streamflow at the basin (or subbasin) outlet. (`m^3/s`)

| Output variable (dag `var`) | Validation role | Unit | Description |
|-----------------------------|-----------------|------|-------------|
| `discharge` | rank 1 output | `m^3/s` | Simulated routed streamflow at the basin (or subbasin) outlet. |

Other dag outputs present in this KI: `runoff_depth`, `loss`, `subbasin_outflow`.

---

## Unit Table / Unit Conversion Table

Exact I/O shapes live in `docs/format_spec.yaml`; this table is the body-level
unit checklist for the HEC-HMS pipeline and known unit traps.

| Variable | Source or pipeline unit | Model/output unit | Conversion or check |
|----------|-------------------------|-------------------|---------------------|
| Precipitation | CMFD `mm/day` | daily HEC-HMS timestep `mm` | Use daily value directly for daily runs; for sub-daily runs divide by the number of timesteps per day. |
| Temperature | CMFD `K` | HEC-HMS `Celsius` | Subtract `273.15`. |
| PET | forcing converter output `mm` | HEC-HMS daily PET depth | Confirm daily-vs-monthly before execution; see `dt_118`. |
| Basin area | `km2` | `km2` in runoff-to-discharge conversion | Do not pass `m2` or acres where `area_km2` is expected. |
| Runoff depth | model intermediate/output `runoff_depth` | depth variable | Listed as a dag output; consult `dag.yaml` for its authoritative unit. |
| Loss | model intermediate/output `loss` | loss variable | Listed as a dag output; consult `dag.yaml` for its authoritative unit. |
| Subbasin outflow | model output `subbasin_outflow` | routed flow variable | Listed as a dag output; consult `dag.yaml` for its authoritative unit. |
| Discharge | routed streamflow | `m^3/s` | Rank-1 dag output: `discharge`. |

---

## Tools Reference

| # | Tool | Stage | Lines | Purpose |
|---|------|-------|-------|---------|
| 1 | `convert_forcing_to_hms.py` | s4 | ~450 | CMFD/MSWX NetCDF → daily precip (mm) + PET (mm) |
| 2 | `convert_soil_to_hms.py` | s2-s3 | ~350 | HWSD + AVHRR → SCS Curve Number + soil properties |
| 3 | `run_hms_engine.py` | s6 | ~400 | REAL engine: run HEC-HMS 4.14 headless, check log/results, export DSS, compare to reference |
| 3s | `run_hec_hms.py` | s6 | ~430 | SURROGATE (not HEC-HMS): Python SCS-CN + SCS UH + Muskingum + baseflow |
| 4 | `parse_hms_output.py` | s7 | ~250 | Parse output CSV → standardized discharge CSV |
| 5 | `validate_hms.py` | s7 | ~350 | Compare simulated vs observed Q, compute metrics |
| 6 | `calibrate_hms.py` | s5 | ~290 | SURROGATE calibration (GLUE-style) of `run_hec_hms.py`; not HEC-HMS parameters |

---

## Skill Knowledge

| # | Document | Stage | Knowledge Type |
|---|----------|-------|----------------|
| 1 | [`docs/s0_configuration_skill.md`](docs/s0_configuration_skill.md) | s0 | procedural |
| 2 | [`docs/s1_domain_setup_skill.md`](docs/s1_domain_setup_skill.md) | s1 | procedural |
| 3 | [`docs/s2_soil_parameters_skill.md`](docs/s2_soil_parameters_skill.md) | s2-s3 | evaluative |
| 4 | [`docs/s4_forcing_conversion_skill.md`](docs/s4_forcing_conversion_skill.md) | s4 | evaluative |
| 5 | [`docs/s5_model_execution_skill.md`](docs/s5_model_execution_skill.md) | s5-s6 | procedural |
| 6 | [`docs/s6_output_analysis_skill.md`](docs/s6_output_analysis_skill.md) | s7 | evaluative |
| 7 | [`docs/s7_calibration_skill.md`](docs/s7_calibration_skill.md) | s5 | evaluative |

---

## Critical Domain Knowledge

These are non-obvious facts that cause silent failures. Each is indexed as `dt_NNN` and cross-referenced in the diagnostic triplets.

### dt_101: Precipitation units — CMFD mm/day vs HEC-HMS mm or mm/hr

**CRITICAL**. CMFD provides precipitation in mm/day. HEC-HMS internal calculations
use mm for event mode or mm/hr for continuous mode. If running at daily timestep,
total precipitation per timestep is the daily value directly (mm). If running at
sub-daily timestep (e.g., hourly), divide by 24: `P_hr = P_day / 24`.

**Trap**: If you pass mm/day values to a sub-daily model step without dividing by the
number of timesteps per day, total volume will be N_timesteps × too large.

### dt_102: Temperature units — CMFD Kelvin vs HEC-HMS Celsius

**CRITICAL**. CMFD temperature is in Kelvin. HEC-HMS (and the SCS methods) expect
Celsius for snowmelt and PET calculations. Always subtract 273.15.

**Trap**: Passing Kelvin to Hargreaves PET yields wildly wrong evapotranspiration.

### dt_103: Curve Number must be 30-100 range

SCS Curve Number valid range is 30-100. Values below 30 indicate unrealistic zero
runoff. Values above 98 indicate impervious surface. CN=100 means all precipitation
becomes runoff (no loss).

**Trap**: If soil group lookup returns 0 or NaN, the CN calculation produces zero
runoff — the hydrograph is flat.

### dt_104: SCS Initial Abstraction ratio — 0.2 vs 0.05

The classic SCS-CN method uses Ia = 0.2 * S. Recent USDA research (Woodward et al. 2003)
recommends Ia = 0.05 * S for better fit. HEC-HMS 4.x allows configuring this ratio.

**Trap**: Using 0.2 in wet climates significantly underestimates runoff for small storms.
Using 0.05 in arid regions may overestimate runoff.

### dt_105: Unit Hydrograph peak time — Tc vs Tp

SCS UH uses time to peak Tp = 0.6 * Tc + D/2 where Tc is time of concentration
and D is the computational interval. If D is too large relative to Tp, the UH
is poorly defined.

**Trap**: Setting D > 0.29 * Tp causes the discrete UH to miss the peak. Rule of
thumb: D <= Tp/5.

### dt_106: Muskingum K and X constraints

Muskingum routing requires: 0 <= X <= 0.5 (typically 0.0-0.3) and K > 0.
Additionally, the Courant condition requires: 2KX <= dt <= 2K(1-X).

**Trap**: X > 0.5 causes negative routing coefficients → negative flows.
K too small relative to dt causes numerical instability.

### dt_107: Baseflow recession constant — Ratio vs Rate

HEC-HMS linear reservoir baseflow uses a recession constant (ratio, dimensionless,
0 < k < 1). This is NOT a rate constant. Values close to 1.0 mean slow recession
(persistent baseflow). Values close to 0.0 mean fast recession.

**Trap**: Confusing recession ratio with hydraulic conductivity (which has units) gives
completely wrong baseflow magnitudes.

### dt_108: Basin area units — km² vs m² vs acres

HEC-HMS project files can use different unit systems (SI metric, US customary).
When converting runoff depth (mm) to discharge (m³/s):
`Q = runoff_mm * area_km2 * 1000 / (dt_seconds)`

**Trap**: Using area in m² instead of km² gives 10^6 too large discharge.
Using area in acres without conversion gives wrong results entirely.

### dt_109: DSS file time conventions — midnight issues

HEC-DSS stores time as 2400 for midnight, not 0000 of the next day. When converting
to pandas datetime, 2400 must map to 00:00 of the next day.

**Trap**: Off-by-one-day errors in the entire time series if midnight convention is wrong.

---

## Validation

### Real engine — Castro Valley, California (official USACE sample, 2026-10-06)

| Item | Value |
|------|-------|
| Project | `castro` from HEC-HMS 4.14 `samples.zip` (Castro Valley Urban Study), run `Current` (basin `Castro 1`, met `GageWts`, control `Jan73`) |
| Event | 16 Jan 1973 03:00-12:55, 5-min step; Fire Dept. 10-min rain gage (Thiessen weights) |
| Methods | Initial+Constant loss, Snyder UH, Recession baseflow, Modified Puls reach routing (as shipped) |
| Area | 5.51 mi2 (14.27 km2) at junction `Outlet` |
| Observation | gage `Out`, `castro.dss /CASTRO VALLEY/OUTLET/FLOW//10MIN/OBS/` (CFS), paired at the 60 shared 10-min times |
| Official example | runs Current + Future match the USACE-shipped `RUN_*.results` exactly (54/54 values) |
| Scored (run Current, m3/s) | NSE 0.858, KGE 0.811, r 0.933, PBIAS +0.98 % (ki_tools_common.metrics.all_metrics) |
| HMS's own statistics (5-min, HMS-interpolated obs) | NSE 0.863, r 0.935, PBIAS 1.03 %, modified KGE 0.806 |
| Water balance (KDT run 2026-10-06) | P 39.2 mm = loss 18.4 + excess 20.8 mm; outlet 20.97 mm; closes to 0.0 mm |
| Caveat | parameters are the USACE ones, probably fitted to this same storm, so this is not an independent test |
| Reproduce | `run_hms_engine.py --project castro/castro.hms --run Current --workdir OUT --export Outlet:FLOW --export_dss "castro.dss::/CASTRO VALLEY/OUTLET/FLOW//10MIN/OBS/"`; pair `OUT/exports/Current_Outlet_FLOW.csv` with `dss_CASTRO_VALLEY_OUTLET_FLOW_10MIN_OBS.csv` on `value_m3s` at the 60 shared times; `ki_tools_common.metrics.all_metrics(obs, sim)` (re-checked 2026-10-06: NSE 0.85793) |

---

## Validation Status

Run results (site, period, scores) are not kept here. They live in the run records
(`test_runs` in hydrocraft.db and each run's own result files).

This section restates the KI's validation convention facts. The convention file
wins if this body ever disagrees with `docs/validation_convention.yaml`.

**Rank-1 judged output**: `discharge` (`m^3/s`) — Simulated routed streamflow at the basin (or subbasin) outlet.

| Dag variable | Metric | Direction | Very good band | Good band | Satisfactory band | Convention cite |
|--------------|--------|-----------|----------------|-----------|-------------------|-----------------|
| `discharge` | `nse` | maximize | `>= 0.8` (`moriasi2015`) | `>= 0.7` (`moriasi2015`) | `>= 0.5` (`moriasi2015`) | `moriasi2015` |
| `discharge` | `pbias` | zero_centered | `abs(PBIAS) <= 5.0` (`moriasi2015`) | `abs(PBIAS) <= 10.0` (`moriasi2015`) | `abs(PBIAS) <= 15.0` (`moriasi2015`) | `moriasi2015` |
| `runoff_depth` | `nse` | maximize | `>= 0.8` (`moriasi2015`) | `>= 0.7` (`moriasi2015`) | `>= 0.5` (`moriasi2015`) | `moriasi2015` |
| `runoff_depth` | `pbias` | zero_centered | `abs(PBIAS) <= 5.0` (`moriasi2015`) | `abs(PBIAS) <= 10.0` (`moriasi2015`) | `abs(PBIAS) <= 15.0` (`moriasi2015`) | `moriasi2015` |
| `loss` | `pbias` | zero_centered | no cited threshold | no cited threshold | no cited threshold | none |

Real-engine metric values come from `run_hms_engine.py` exports scored with
`ki_tools_common.metrics` (Castro Valley, above). This section records the cited pass-bands used
to judge those values.

The models DB still stores SURROGATE metrics for HEC-HMS. `knowledge_infrastructure.yaml`
lists them only under `validation.surrogate_metrics_not_hec_hms`; its `validation.metrics` hold the
real-engine score. Regenerating the manifest from the DB
would project the surrogate metrics again -- re-check it after any regeneration.

---

## Calibration Parameters

| Parameter | Range | Default | Sensitivity | Notes |
|-----------|-------|---------|-------------|-------|
| CN (Curve Number) | 30-100 | From HWSD+AVHRR | **Highest** | Controls total runoff volume |
| Ia ratio | 0.05-0.20 | 0.05 | High | Initial abstraction / S |
| Tp (time to peak) | 1-720 hr | From Tc | High | Controls peak timing |
| K (Muskingum K) | 0.5-240 hr | From reach length | Medium | Routing delay |
| X (Muskingum X) | 0.0-0.5 | 0.2 | Low | Routing attenuation |
| k_base (recession) | 0.5-0.99 | 0.95 | Medium | Baseflow persistence |
| q_base_init | 0-1000 m³/s | 50 | Low | Initial baseflow |

**Priority order for calibration**: CN > Ia_ratio > k_base > Tp > K > X > q_base_init

---

## HEC-HMS File Formats

### Project Structure (native HEC-HMS)

```
project.hms          # Project definition (name, description, units)
basin.basin          # Basin model: subbasins, reaches, junctions, reservoirs
met.met              # Meteorologic model: method, gauges, gridded data
control.control      # Control specifications: start/end dates, timestep
timeseries.gage      # Time series data specifications
data.dss             # Binary time series storage (HEC-DSS format)
run.run              # Simulation run configuration
```

### Basin File Format (.basin)

```
Basin: MyBasin
  Last Modified Date: 28 March 2026
  Last Modified Time: 10:00:00
  Unit System: Metric
End:

Subbasin: Sub1
  Canvas X: 100
  Canvas Y: 200
  Area: 500.0
  Downstream: J1
  Loss Rate Method: SCS Curve Number
  Curve Number: 75
  Transform Method: SCS Unit Hydrograph
  Lag Time: 3.5
  Baseflow Method: Linear Reservoir
  GW1 Initial Discharge: 10.0
  GW1 Recession Constant: 0.95
  GW1 Ratio to Peak: 0.1
End:

Junction: J1
  Canvas X: 150
  Canvas Y: 300
  Downstream: R1
End:

Reach: R1
  Canvas X: 200
  Canvas Y: 400
  Downstream: Outlet
  Route Method: Muskingum
  Muskingum K: 24.0
  Muskingum X: 0.2
  Muskingum Steps: 4
End:
```

### Meteorologic Model File (.met)

```
Meteorologic Model: MyMet
  Unit System: Metric
  Precipitation Method: Specified Hyetograph
  Evapotranspiration Method: Monthly Average
End:

Subbasin: Sub1
  Gage: Precip_Gage1
  ET Gage: ET_Gage1
End:
```

### Control Specifications (.control)

```
Control: SimControl
  Start Date: 01 January 1980
  Start Time: 00:00
  End Date: 31 December 1990
  End Time: 24:00
  Time Interval: 1440
End:
```

---

## Coupling Points

| # | Integration | Direction | Format | Notes |
|---|-------------|-----------|--------|-------|
| 1 | VIC → HEC-HMS | Input | NetCDF → CSV | VIC runoff as additional inflow |
| 2 | CMFD → HEC-HMS | Input | NetCDF → CSV | Precipitation + temperature |
| 3 | MSWX → HEC-HMS | Input | NetCDF → CSV | Alternative global forcing |
| 4 | HEC-HMS → CaMa-Flood | Output | CSV → NetCDF | Subbasin outflow for routing |
| 5 | HWSD → HEC-HMS | Input | Raster → CN table | Soil hydraulic groups |
| 6 | AVHRR → HEC-HMS | Input | Raster → CN table | Land cover classification |

---

## Data Requirements

| Data | Source | Status | Path |
|------|--------|--------|------|
| Precipitation | CMFD 0.25° | Available | `KISSPATH_ROOT/.../huai/Data_forcing_01dy_025deg/` |
| Temperature | CMFD 0.25° | Available | Same as above |
| Soil | HWSD China | Available | `KISSPATH_ROOT/.../soil/HWSD_China_Geo.img` |
| Land Cover | AVHRR 1km | Available | `KISSPATH_ROOT/.../landcover/AVHRR_1km_LANDCOVER_1981_1994.GLOBAL.tif` |
| DEM | SRTM 90m | Available | `KISSPATH_ROOT/.../dem/china_dem_90m/china_dem_90m.tif` |
| Basin Shape | basin outline shapefile | per basin | `/path/to/basin.shp` |
| Observation | outlet gauge record | per basin | `/path/to/obs.txt` |

---

## Quick Start

### 0. Real engine (model of record)
```bash
python3 tools/run_hms_engine.py --project /path/<project>.hms --run <RunName> \
  --workdir ./hms_run --export <OutletElement>:FLOW \
  [--export_dss "<file>.dss::/A/B/FLOW//10MIN/OBS/"] [--reference_results <dir>]
# outputs: hms_run/exports/*.csv (value + value_m3s for CFS flow), hms_run/hms_engine_run.json
```

Steps 1-6 below are the SURROGATE daily-CSV workflow (NOT HEC-HMS).

### 1. Convert forcing data
```bash
python3 ki/tools/convert_forcing_to_hms.py \
  --forcing_dir KISSPATH_FORCING/huai/Data_forcing_01dy_025deg/ \
  --basin_shp /path/to/basin.shp \
  --start_date 1980-01-01 --end_date 1990-12-31 \
  --pet_csv /path/to/source_documented_daily_pet.csv \
  --output_dir ./forcing_out/
```

### 2. Convert soil parameters
```bash
python3 ki/tools/convert_soil_to_hms.py \
  --soil_file KISSPATH_STATIC/HWSD_China_Geo.img \
  --landcover_file KISSPATH_DATA/landcover/AVHRR_1km_LANDCOVER_1981_1994.GLOBAL.tif \
  --basin_shp /path/to/basin.shp \
  --output_file ./soil_params.json
```

### 3. Run model
```bash
python3 ki/tools/run_hec_hms.py \
  --forcing_csv ./forcing_out/basin_avg_forcing.csv \
  --soil_params ./soil_params.json \
  --basin_area_km2 121330 \
  --start_date 1980-01-01 --end_date 1990-12-31 \
  --output_csv ./sim_discharge.csv
```

### 4. Parse output
```bash
python3 ki/tools/parse_hms_output.py \
  --input_csv ./sim_discharge.csv \
  --output_csv ./discharge_daily.csv
```

### 5. Validate
```bash
python3 ki/tools/validate_hms.py \
  --sim_csv ./discharge_daily.csv \
  --obs_file /path/to/obs.txt \
  --start_date 1981-01-01 --end_date 1990-12-31 \
  --output_figure ./validation.png
```

### 6. Calibrate
```bash
python3 ki/tools/calibrate_hms.py \
  --forcing_csv ./forcing_out/basin_avg_forcing.csv \
  --obs_file /path/to/obs.txt \
  --basin_area_km2 121330 \
  --start_date 1981-01-01 --end_date 1990-12-31 \
  --n_samples 500 \
  --output_params ./calibrated_params.json
```

---

## Diagnostic Triplets Summary

| ID | Domain | Severity | Symptom (brief) |
|----|--------|----------|-----------------|
| dt_101 | unit_conversion | silent | Precip mm/day passed to sub-daily step without division |
| dt_102 | unit_conversion | silent | Temperature in Kelvin instead of Celsius |
| dt_103 | parameter_range | silent | CN outside 30-100 range |
| dt_104 | parameter_choice | medium | Ia ratio 0.2 vs 0.05 mismatch |
| dt_105 | numerical | medium | UH interval D > 0.29*Tp |
| dt_106 | numerical | error | Muskingum X > 0.5 or K < dt |
| dt_107 | parameter_units | silent | Recession constant confused with rate |
| dt_108 | unit_conversion | silent | Area km² vs m² in runoff conversion |
| dt_109 | format | medium | DSS midnight 2400 vs 0000 convention |
| dt_110 | unit_conversion | silent | Radiation W/m² vs MJ/m²/day for PET |
| dt_111 | parameter_range | silent | Green-Ampt suction head unrealistic |
| dt_112 | format | error | Missing basin-met-control linkage |
| dt_113 | numerical | medium | Negative runoff from CN < storm depth |
| dt_114 | unit_conversion | silent | Runoff mm to m³/s conversion error |
| dt_115 | runtime | error | Simulation period outside forcing period |
| dt_116 | parameter_range | medium | Lag time unrealistic for basin size |
| dt_117 | data_quality | medium | Missing precipitation days filled with zero |
| dt_118 | unit_conversion | silent | PET in mm/month vs mm/day |
| dt_119 | runtime | silent | Real engine exits 0 although HMS refused the run (errors only in the log) |
| dt_120 | format | medium | DSS catalog E part `5Minute` vs `5MIN` — pathname match fails |
| dt_121 | unit_conversion | silent | English-unit projects: CFS / AC-FT / MI2 / IN vs m3/s |
| dt_122 | runtime | error | hec-hms.sh needs DISPLAY/Xvfb; use the headless launcher |

---

## SCS Curve Number Method — Core Algorithm

The SCS-CN method is the default loss method in HEC-HMS:

```
S = 25400/CN - 254           # Maximum retention (mm)
Ia = lambda_ia * S            # Initial abstraction (mm), lambda_ia = 0.05 or 0.2
if P <= Ia:
    Q = 0                     # No runoff
else:
    Q = (P - Ia)² / (P - Ia + S)  # Runoff depth (mm)
Loss = P - Q                  # Total infiltration (mm)
```

### SCS Unit Hydrograph — Core Algorithm

```
Tp = 0.6 * Tc + D/2          # Time to peak (hr)
Qp = 2.08 * A / Tp           # Peak discharge (m³/s per mm of excess rain)
                              # A in km², Tp in hours
# Dimensionless UH ordinates (SCS):
t/Tp:  0.0  0.1  0.2  0.3  0.4  0.5  0.6  0.7  0.8  0.9  1.0
Q/Qp:  0.0  0.03 0.10 0.19 0.31 0.47 0.66 0.82 0.93 0.99 1.00
t/Tp:  1.1  1.2  1.3  1.4  1.5  1.6  1.8  2.0  2.2  2.4  2.6
Q/Qp:  0.99 0.93 0.86 0.78 0.68 0.56 0.39 0.28 0.207 0.147 0.107
t/Tp:  2.8  3.0  3.5  4.0  4.5  5.0
Q/Qp:  0.077 0.055 0.025 0.011 0.005 0.0
```

### Muskingum Routing — Core Algorithm

```
C1 = (dt - 2*K*X) / (2*K*(1-X) + dt)
C2 = (dt + 2*K*X) / (2*K*(1-X) + dt)
C3 = (2*K*(1-X) - dt) / (2*K*(1-X) + dt)
# C1 + C2 + C3 = 1.0 (conservation check)
O[t+1] = C1*I[t+1] + C2*I[t] + C3*O[t]
```

---

## File Structure

```
ki/
├── SKILL.md                          # This file
├── knowledge_infrastructure.yaml     # Formal schema
├── tools/
│   ├── convert_forcing_to_hms.py    # CMFD/MSWX → HEC-HMS forcing
│   ├── convert_soil_to_hms.py       # HWSD + AVHRR → CN + soil params
│   ├── run_hms_engine.py            # REAL engine: HEC-HMS 4.14 headless + checks + DSS export
│   ├── run_hec_hms.py               # SURROGATE (not HEC-HMS): Python SCS-CN + SCS UH + routing
│   ├── parse_hms_output.py          # Extract discharge CSV
│   ├── validate_hms.py              # Compare sim vs obs, compute metrics
│   └── calibrate_hms.py            # Automated GLUE-style calibration
├── docs/
│   ├── s0_configuration_skill.md
│   ├── s1_domain_setup_skill.md
│   ├── s2_soil_parameters_skill.md
│   ├── s4_forcing_conversion_skill.md
│   ├── s5_model_execution_skill.md
│   ├── s6_output_analysis_skill.md
│   └── s7_calibration_skill.md
├── diagnostics/
│   └── triplets.yaml
└── workflow/
    └── workflow.md
```
