**Package**: `hydrocraft-fsm2-snow` v1.0.0 **Model**: FSM2 v2.1.2 — Flexible Snow Model **Domain**: Cryosphere (snow accumulation, melt, forest canopy snow processes) **Author**: Richard Essery, School of GeoSciences, University of Edinburgh **Last updated**: 2026-03-26 **Stats**: 4 tools | 5 skill documents | 15+ diagnostic triplets **Validation status**: `example_validated` (Alptal, Switzerland, 2004-2005) ---
Scanned 9/11/2026
Install to Claude Code
npx -y skills add lzwei196/KISS---Knowledge-Infrastructure-for-Scientific-Simulation --skill FSM2 --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of FSM2?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/lzwei196-fsm2)More formats (shields.io, HTML) on the badges page.
> **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/` (4 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. |
| on ANY error, before debugging | `diagnostics/triplets.yaml` (25 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` (19 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-23 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/convert_forcing_to_fsm2.py` | `KISSPATH_PYTHON_ENV/bin/python {KI}/tools/convert_forcing_to_fsm2.py --help` |
| `tools/convert_soil_params.py` | `KISSPATH_PYTHON_ENV/bin/python {KI}/tools/convert_soil_params.py --help` |
| `tools/parse_fsm2_output.py` | `KISSPATH_PYTHON_ENV/bin/python {KI}/tools/parse_fsm2_output.py --help` |
| `tools/run_fsm2.py` | `KISSPATH_PYTHON_ENV/bin/python {KI}/tools/run_fsm2.py --help` |
*4 public tools; `_`-prefixed helpers and packaging files excluded.*
<!-- KI-TOOL-INDEX:END -->
# FSM2 v2.1.2 (Flexible Snow Model) — Knowledge Infrastructure
**Package**: `hydrocraft-fsm2-snow` v1.0.0
**Model**: FSM2 v2.1.2 — Flexible Snow Model
**Domain**: Cryosphere (snow accumulation, melt, forest canopy snow processes)
**Author**: Richard Essery, School of GeoSciences, University of Edinburgh
**Last updated**: 2026-03-26
**Stats**: 4 tools | 5 skill documents | 15+ diagnostic triplets
**Validation status**: `example_validated` (Alptal, Switzerland, 2004-2005)
---
## Data Preparation
### Forcing data
**Data Sources**: FSM2 is an energy-balance model and needs SUB-DAILY forcing, so
use `from ki_tools_common.load_forcing import load_hourly_forcing`, not the daily
loader:
```python
from ki_tools_common.load_forcing import load_hourly_forcing
d = load_hourly_forcing("nasa_power", lat, lon, 2001, 2019) # hourly, global
# d: dates, srad_wm2, lrad_wm2, precip_mm (mm per step), temp_c, shum_kgkg,
# wind_ms (10 m), wind2_ms (2 m), wind_height_m, pres_pa, timestep_seconds
```
- `nasa_power` — hourly, global, **starts 2001-01-01**. The only practical point
source for a multi-year FSM2 run outside China. Set the namelist `zU` to the
returned `wind_height_m` (see triplet T21 for why WS2M is not usable).
- `cmfd` / `mswx` — 3-hourly gridded (set `dt = 10800`). Extracting ONE point out
of MSWX is impractically slow: the annual files are chunked `(1, 1800, 3600)`
with gzip, so a single cell-year forces ~76 GB of decompression. Use them only
when you need whole fields, and read them with `engine='h5netcdf'` (triplet T23).
Neither source gives FSM2 what DRIV1D=1 asks for directly — all of them publish
specific humidity rather than RH, and total precipitation rather than separate
snowfall and rainfall. `tools/convert_forcing_to_fsm2.py --shum-col --precip-col`
closes both gaps using FSM2's own formulas (triplets T19, T20). **FSM2 does not
partition precipitation itself.**
**Observations**: the rank-1 binding is `snowcci_swe_nh` (ESA CCI Snow L3C daily
SWE v2.0) at `KISSPATH_DATA/benchmarks/snowcci_swe/` — 0.1° global lat/lon,
variable `swe` in **mm, which is kg m-2 1:1 with FSM2 `snw`**. Read triplet T24
before using it: negative values are flags, `0` is a real no-snow observation,
and the product only exists for the snow season (~215 files per year, no summer).
Point SWE records: `snotel` (US), `canswe_canada`, `canadian_snow_survey_historical`.
## Overview
This knowledge infrastructure enables autonomous simulation of snow accumulation and
melt using FSM2 (Flexible Snow Model), a multi-physics energy balance model. FSM2
extends the Factorial Snow Model (FSM; Essery, 2015) with forest canopy options and
multi-point capability. Physics options are selected at **compile time** via C
preprocessor defines, making it a "factorial" model — each combination of options
produces a distinct model configuration.
**What FSM2 does**:
- Multi-layer snowpack energy and mass balance
- Prognostic/diagnostic snow albedo
- Snow density evolution (fixed, age-compaction, overburden)
- Snow grain growth (temperature-dependent or temperature-gradient)
- Snow cover fraction parameterizations
- Canopy snow interception, unloading, and melt drip
- 1- or 2-layer forest canopy radiative transfer (Beer-Lambert or two-stream)
- Multi-layer soil heat conduction with freeze/thaw
- Snow hydraulics (free-draining, bucket, gravitational drainage)
- Turbulent exchange (neutral or stability-corrected)
- Solar position and SW partitioning (diffuse/direct)
- NetCDF or ASCII text output
- Multi-point simulations (ASCII mode)
**Key difference from other cryosphere models**: FSM2 is compile-time configurable
with 11 binary/ternary physics switches, allowing factorial experiments across
hundreds of model configurations from the same source code.
---
## Installation
### Build from Source
```bash
# Prerequisites: gfortran (GCC Fortran compiler)
# Optional: libnetcdff-dev for NetCDF output
cd /path/to/FSM2/source/repo
# ASCII output build (default)
bash compil.sh # produces ./FSM2
# NetCDF output build
bash compil_nc.sh # produces ./FSM2 (requires netCDF-Fortran)
```
### Binary
```
FSM2 executable: ./FSM2 (produced by compil.sh)
Source: src/*.F90 (22 Fortran source files)
Compiler: gfortran with -cpp -O3 flags
```
### Dependencies
```
gfortran (required)
libnetcdff (optional, for NetCDF output: PROFNC=1)
```
---
## Pipeline (7 stages)
| # | Stage | Tool(s) | Description |
|---|-------|---------|-------------|
| 1 | Configuration | (manual/namelist) | Select physics options in compil.sh |
| 2 | Met forcing | `convert_forcing_to_fsm2` | Convert global reanalysis → FSM2 ASCII format |
| 3 | Soil/site params | `convert_soil_params` | Derive soil properties from HWSD clay/sand fractions |
| 4 | Compilation | `compile_fsm2` | Build FSM2 binary with selected physics options |
| 5 | Namelist setup | (manual/tool) | Create namelist file with ¶ms, &gridpnts, etc. |
| 6 | Execution | `run_fsm2` | Run FSM2 with forcing and namelist |
| 7 | Output parsing | `parse_fsm2_output` | Extract ASCII output to CSV with headers |
---
## Compile-Time Physics Options (OPTS.h)
These are the preprocessor defines set in compil.sh before compilation:
| Option | Values | Description |
|--------|--------|-------------|
| `ALBEDO` | 1, 2 | Snow albedo: 1=diagnostic, 2=prognostic (decay+refresh) |
| `CANINT` | 1, 2 | Canopy interception: 1=linear, 2=exponential saturation |
| `CANMOD` | 1, 2 | Forest canopy layers: 1=single layer, 2=two layers |
| `CANRAD` | 1, 2 | Canopy radiation: 1=Beer-Lambert, 2=two-stream |
| `CANUNL` | 1, 2 | Canopy unloading: 1=exponential+melt, 2=temp+wind driven |
| `CONDCT` | 0, 1 | Snow conductivity: 0=fixed (kfix), 1=density-dependent |
| `DENSTY` | 0, 1, 2 | Snow density: 0=fixed, 1=age-compaction, 2=overburden |
| `EXCHNG` | 0, 1 | Turbulent exchange: 0=neutral, 1=stability-corrected |
| `HYDROL` | 0, 1, 2 | Snow hydrology: 0=free-drain, 1=bucket, 2=gravitational |
| `SGRAIN` | 1, 2 | Grain growth: 1=temperature, 2=temperature-gradient |
| `SNFRAC` | 1, 2, 3 | Snow cover fraction: 1=linear, 2=tanh, 3=asymptotic |
### Driving Data Options
| Option | Values | Description |
|--------|--------|-------------|
| `DRIV1D` | 1, 2 | Format: 1=FSM native, 2=ESM-SnowMIP |
| `SWPART` | 0, 1 | SW partition: 0=all diffuse, 1=solar position split |
| `ZOFFST` | 0, 1 | Height offset: 0=none, 1=above canopy |
### Output Options
| Option | Values | Description |
|--------|--------|-------------|
| `PROFNC` | 0, 1 | Output: 0=ASCII text files, 1=NetCDF |
---
## Input Format
### Meteorological Driving Data (DRIV1D=1, FSM native)
Space-delimited ASCII file. One row per timestep (default: hourly, dt=3600s).
| Column | Variable | Unit | Description |
|--------|----------|------|-------------|
| 1 | year | - | Year (integer) |
| 2 | month | - | Month 1-12 (integer) |
| 3 | day | - | Day 1-31 (integer) |
| 4 | hour | - | Hour of day (real, e.g. 1.0, 2.0) |
| 5 | SW | W/m² | Incoming shortwave radiation |
| 6 | LW | W/m² | Incoming longwave radiation |
| 7 | Sf | kg/m²/s | Snowfall rate |
| 8 | Rf | kg/m²/s | Rainfall rate |
| 9 | Ta | K | Air temperature |
| 10 | RH | % | Relative humidity (0-100) |
| 11 | Ua | m/s | Wind speed |
| 12 | Ps | Pa | Surface air pressure |
### ESM-SnowMIP Format (DRIV1D=2)
| Column | Variable | Unit | Description |
|--------|----------|------|-------------|
| 1-4 | year,month,day,hour | - | Date/time |
| 5 | SW | W/m² | Incoming shortwave radiation |
| 6 | LW | W/m² | Incoming longwave radiation |
| 7 | Rf | kg/m²/s | Rainfall rate |
| 8 | Sf | kg/m²/s | Snowfall rate |
| 9 | Ta | K | Air temperature |
| 10 | Qa | kg/kg | Specific humidity |
| 11 | RH | % | Relative humidity |
| 12 | Ua | m/s | Wind speed |
| 13 | Ps | Pa | Surface air pressure |
**Note**: For DRIV1D=1, RH is converted to specific humidity internally. For
DRIV1D=2, specific humidity is read directly but RH is also read.
### Namelist File
Read from stdin. Contains 7 Fortran namelist blocks, in this fixed order
(`¶ms`, `&gridpnts`, `&gridlevs`, `&drive`, `&veg`, `&initial`,
`&outputs`). An unwanted block is still written, empty — see triplet T06.
```fortran
¶ms ! Model parameters (override the FSM2_PARAMS defaults)
acn0 = 0.06
avg0 = 0.142
fcly = 0.04 ! soil clay fraction — from tools/convert_soil_params.py
fsnd = 0.87 ! soil sand fraction
/
&gridpnts ! Grid dimensions
Npnts = 2 ! Number of simulation points
Nsmax = 3 ! Max snow layers (default 3; Dzsnow must match if changed)
Nsoil = 4 ! Soil layers (default 4; Dzsoil must match if changed)
/
&gridlevs ! Layer configuration (usually defaults are fine)
Dzsnow = 0.1, 0.2, 0.4 ! Nsmax min snow-layer thicknesses (m)
Dzsoil = 0.1, 0.2, 0.4, 0.8 ! Nsoil soil-layer thicknesses (m)
fvg1 = 0.5 ! fraction of VAI in the upper canopy layer
zsub = 1.5 ! subcanopy diagnostic height (m)
/
&drive ! Driving data configuration
met_file = 'met_Alptal_0405.txt'
dt = 3600 ! Timestep (s). Default 3600; MUST equal the forcing interval.
lat = 47.05 ! Latitude (degrees, converted to radians internally)
noon = 12 ! Hour of SOLAR noon, in the met file's OWN time base.
! Only used when SWPART=1. For a met file stamped in UTC
! this is 12 - lon/15, NOT 12 (e.g. 10.224 at 26.63 E).
zT = 35 ! Temperature measurement height (m)
zU = 35 ! Wind measurement height (m) — must match the height the
! wind in the met file was actually measured/derived at
/
&veg ! Vegetation characteristics (per point)
alb0 = 0.15, 0.15 ! Snow-free ground albedo
vegh = 0.00, 25.0 ! Canopy height (m)
VAI = 0.00, 3.96 ! Vegetation area index
! alb0_file / vegh_file / VAI_file read the same per-point arrays from a file
/
&initial ! Initial conditions
fsat = 0.5, 0.5, 0.5, 0.5 ! Nsoil initial soil moisture, FRACTION of Vsat
Tprf = 285, 285, 285, 285 ! Nsoil initial soil temperatures (K).
! The 285 K default is a temperate value —
! set it near the site's annual mean ground
! temperature for cold-region runs.
start_file = 'none' ! restart from a previous run's dump file
/
&outputs ! Output configuration
runid = 'Alptal_' ! Prefix for output files
dump_file = 'dump' ! End-of-run state dump, written as <runid><dump_file>.
! Feed it back through &initial start_file to chain a
! spin-up run into the evaluation run.
/
```
**70-character limit (triplet T22).** `met_file`, `runid`, `dump_file`,
`alb0_file`, `vegh_file` and `VAI_file` are all `character(len=70)`. A longer
absolute path is silently truncated and FSM2 then fails to open a file that
plainly exists. Keep the met file in the run directory and use a short relative
name.
**FSM2 writes output EVERY timestep** — there is no output-frequency control.
Aggregate to the observation's frequency yourself (e.g. daily mean `snw`)
after `parse_fsm2_output.py`.
---
## Output Format
## 6. Output Description
**Source of truth**: `dag.yaml`. If this section and `dag.yaml` ever disagree,
the dag wins.
**Headline output** (`validation_rank: 1`):
> `snw` — Snow water equivalent (SWE) (`kg m-2`)
The dag's rank-1 output is `var='snw'`, `unit='kg m-2'`,
`description='Snow water equivalent (SWE)'`.
| Output variable (dag `var`) | Rank | Unit | Description / role |
|-----------------------------|------|------|--------------------|
| `snw` | 1 | `kg m-2` | Snow water equivalent (SWE) |
Other dag outputs listed by `dag.yaml`: `snd`, `svg`, `Tsrf`, `Tsoil`, `Tveg`,
`Melt`, `Roff`, `subl`, `H`, `LE`, `LWout`, `SWout`, `LWsub`, `SWsub`, `Tsub`,
`Usub`.
The ASCII file layout below documents the model output files used by the parser.
### ASCII Output (PROFNC=0)
Three output files per run, with prefix `runid`:
**`{runid}flux.txt`** — Energy fluxes:
```
year month day hour H LE LWout Melt Roff subl SWout
```
| Variable | Unit | Description |
|----------|------|-------------|
| H | W/m² | Sensible heat flux (positive upward) |
| LE | W/m² | Latent heat flux (positive upward) |
| LWout | W/m² | Outgoing longwave radiation |
| Melt | kg/m²/s | Surface melt rate |
| Roff | kg/m²/s | Runoff from snow |
| subl | kg/m²/s | Sublimation rate |
| SWout | W/m² | Outgoing shortwave radiation |
**`{runid}stat.txt`** — State variables:
```
year month day hour snd snw svg Tsoil(1:Nsoil) Tsrf Tveg(1:Ncnpy)
```
| Variable | Unit | Description |
|----------|------|-------------|
| snd | m | Snow depth |
| snw | kg/m² | Snow water equivalent (SWE) |
| svg | kg/m² | Snow mass on vegetation |
| Tsoil | K | Soil layer temperatures |
| Tsrf | K | Surface temperature |
| Tveg | K | Vegetation temperatures |
**`{runid}subc.txt`** — Subcanopy diagnostics:
```
year month day hour LWsub SWsub Tsub Usub
```
### Format specification
```fortran
100 format(3(i4),f8.3,*(e14.6))
```
- Date columns: 3 integers (i4) + 1 float (f8.3)
- Data columns: exponential notation (e14.6), space-separated
---
## 8. Unit Conversion Table
Exact input and output shapes live in `docs/format_spec.yaml`, projected from the
dag and diagnostics. The model-ready units below restate this KI's documented
FSM2 ASCII interface and the unit traps already called out for this pipeline.
| Variable | Source / interface unit | Model unit | Conversion | Type |
|----------|-------------------------|------------|------------|------|
| `SW` | `W/m²` | `W/m²` | `x1` | multiplicative |
| `LW` | `W/m²` | `W/m²` | `x1` | multiplicative |
| `Sf` | `kg/m²/s` | `kg/m²/s` | `x1` | multiplicative |
| `Rf` | `kg/m²/s` | `kg/m²/s` | `x1` | multiplicative |
| `Ta` | `K` | `K` | `x1` | multiplicative |
| `RH` (`DRIV1D=1`) | `%` | `%` | `x1` | multiplicative |
| `Qa` (`DRIV1D=2`) | `kg/kg` | `kg/kg` | `x1` | multiplicative |
| `Ua` | `m/s` | `m/s` | `x1` | multiplicative |
| `Ps` | `Pa` | `Pa` | `x1` | multiplicative |
| `lat` in namelist | degrees | degrees | converted internally to radians | angular |
| `dt` | `s` | `s` | `x1` | multiplicative |
| `Dzsnow` | `m` | `m` | `x1` | multiplicative |
| `Dzsoil` | `m` | `m` | `x1` | multiplicative |
| `fcly`, `fsnd` | fraction `0-1` | fraction `0-1` | `x1` | multiplicative |
### 8c. Sign Conventions and Output Units
| Variable | Convention in this model | Common alternative | Impact if wrong |
|----------|--------------------------|--------------------|-----------------|
| `H` | `W/m²`, sensible heat flux positive upward | opposite sign convention | Energy-balance metrics can be sign-flipped |
| `LE` | `W/m²`, latent heat flux positive upward | opposite sign convention | Latent heat and sublimation interpretation can be reversed |
| `Melt` | `kg/m²/s`, surface melt rate | accumulated depth per timestep | Magnitude error if treated as an accumulation |
| `Roff` | `kg/m²/s`, runoff from snow | accumulated depth per timestep | Magnitude error if treated as an accumulation |
| `subl` | `kg/m²/s`, sublimation rate | accumulated depth per timestep | Magnitude error if treated as an accumulation |
| `LWout`, `SWout` | `W/m²`, outgoing radiation | incoming radiation sign convention | Radiation balance can be inverted |
| `snw` | `kg m-2`, snow water equivalent (SWE) | depth in m or mm without density conversion | Rank-1 validation target becomes incomparable |
| `snd` | `m`, snow depth | SWE mass loading | Snow-depth validation target becomes incomparable |
| `svg` | `kg/m²`, snow mass on vegetation | canopy water depth in mm | Canopy snow-storage comparison can be mis-scaled |
## Unit Trap Table
| Variable | WRONG unit | CORRECT unit | Symptom if wrong |
|----------|-----------|--------------|------------------|
| Ta | °C | K | Massive negative energy balance, no melt |
| Ps | hPa/kPa | Pa | Humidity conversion fails, specific humidity ~0 |
| RH | fraction 0-1 | percent 0-100 | Extremely dry atmosphere, no LW emission |
| Sf, Rf | mm/h or mm/day | kg/m²/s | Snow accumulation 3600x or 86400x too large |
| lat (namelist) | radians | degrees | Wrong solar zenith, bad SW partition |
| lat (internal) | degrees | radians | (internal conversion: lat_rad = pi/180 * lat_deg) |
| SW | - | W/m² | Must be ≥ 0 |
| LW | - | W/m² | Must be > 0 (typically 100-400) |
| Ua | - | m/s | Clamped to min 0.1 internally |
| vegh | - | m | If 0 → no canopy processes |
| VAI | - | dimensionless | If 0 → open site (no forest) |
| Dzsnow | - | m | Layer thickness, not cm |
| Dzsoil | - | m | Layer thickness, not cm |
| fcly, fsnd | - | fraction 0-1 | Clay + sand ≤ 1 |
| dt | - | s | Timestep in seconds (default 3600) |
---
## Key Parameters and Defaults
| Parameter | Default | Unit | Description |
|-----------|---------|------|-------------|
| asmn | 0.5 | - | Minimum snow albedo (melting) |
| asmx | 0.85 | - | Maximum snow albedo (fresh) |
| rhof | 100 | kg/m³ | Fresh snow density |
| hfsn | 0.1 | m | Snow cover fraction depth scale |
| kfix | 0.24 | W/m/K | Fixed snow thermal conductivity |
| z0sn | 0.001 | m | Snow roughness length |
| z0sf | 0.1 | m | Snow-free roughness length |
| fcly | 0.3 | fraction | Soil clay fraction |
| fsnd | 0.6 | fraction | Soil sand fraction |
| Wirr | 0.03 | fraction | Irreducible liquid water content |
| svai | 4.4 | kg/m² | Snow capacity per unit VAI |
| kext | 0.5 | - | Canopy light extinction coefficient |
| cvai | 3.6e4 | J/K/m² | Vegetation heat capacity per VAI |
| Nsmax | 3 | - | Maximum snow layers |
| Nsoil | 4 | - | Number of soil layers |
| Dzsnow | 0.1, 0.2, 0.4 | m | Min snow layer thicknesses |
| Dzsoil | 0.1, 0.2, 0.4, 0.8 | m | Soil layer thicknesses |
---
## 11. Validated Results
### Test Site: Alptal, Switzerland
| Property | Value |
|----------|-------|
| Validation status | `example_validated` |
| Site | Alptal, Switzerland |
| Period | 2004-2005 |
| Headline dag variable | `snw` |
| Headline unit | `kg m-2` |
| Headline description | Snow water equivalent (SWE) |
### Performance Metrics -- judged against the field's bar, not intuition
**Source of truth**: `docs/validation_convention.yaml`. Null convention bands are
reported as `no cited threshold`; no threshold is inferred or substituted.
| Dag variable | Metric | Direction | Satisfactory band | Good band | Very good band | Citation keys |
|--------------|--------|-----------|-------------------|-----------|----------------|---------------|
| `snw` | `nse` | maximize | `0.0` | `0.64` | no cited threshold | `krinner2018`, `bams2021` |
| `snw` | `nrmse` | minimize | `1.0` | `0.6` | no cited threshold | `krinner2018`, `bams2021` |
| `snd` | `rmse` | minimize | `0.21` | `0.14` | no cited threshold | `mazzotti2020` |
| `snd` | `kge` | maximize | `0.54` | `0.54` | `0.8` | `mazzotti2020` |
| `svg` | `nse` | maximize | no cited threshold | no cited threshold | no cited threshold | none listed in convention |
### Data Replacement Tracking
| Component | Source | Status | Notes |
|-----------|--------|--------|-------|
| Forcing | Pipeline | documented | `convert_forcing_to_fsm2.py` prepares FSM2 ASCII meteorological forcing |
| Soil | Pipeline | documented | `convert_soil_params.py` derives FSM2 soil parameters from HWSD clay/sand inputs |
| Output parsing | Pipeline | documented | `parse_fsm2_output.py` parses ASCII output to CSV with headers |
## Tool Reference
| Tool | Purpose | Input | Output |
|------|---------|-------|--------|
| `convert_forcing_to_fsm2.py` | Convert ERA5/MSWX/NASA POWER/generic CSV → FSM2 met file | CSV with met variables | FSM2 ASCII met file |
| `convert_soil_params.py` | Derive FSM2 soil params from HWSD clay/sand | `--latlon LAT LON` (HWSD raster), `--fractions`, or `--texture` | Namelist ¶ms block |
| `run_fsm2.py` | Compile and execute FSM2 | Source dir + namelist | Output files + binary |
| `parse_fsm2_output.py` | Parse ASCII output → CSV | FSM2 output files | CSV with headers |
**`convert_forcing_to_fsm2.py` derivations** (added 2026-08-21; without them the
documented "reanalysis → FSM2" recipe cannot be completed with KI tools alone):
```bash
python tools/convert_forcing_to_fsm2.py forcing.csv run/met.txt \
--shum-col Qa # RH derived from SPECIFIC humidity with FSM2's own
# saturation formula, so q -> RH -> q is EXACT (T20)
--precip-col Pr # Sf/Rf split from TOTAL precipitation with FSM2's own
# ramp: all snow <= 0 degC, all rain >= +2 degC (T19)
```
The `hour` column is written with 3 decimals, not rounded, so a half-hour
interval centre (0.5, 1.5, …) survives into `SOLARPOS` when `SWPART=1`.
**`run_fsm2.compile_fsm2` mutates the source tree** — it overwrites `src/OPTS.h`
and drops the binary in the repo root. Copy `source/repo/src` into the run's own
work directory and compile there, or every other FSM2 run on the machine silently
inherits your physics options.
---
## References
- Essery (2015). A Factorial Snowpack Model (FSM 1.0). *Geoscientific Model Development*, **8**, 3867-3876.
- Essery, Mazzotti, Barr, Jonas, Quaife and Rutter (2025). A Flexible Snow Model (FSM 2.1.1) including a forest canopy. *Geoscientific Model Development*, **18**, 3583-3605.
- Stähli and Gustafsson (2006). The role of snow interception in winter-time radiation processes of a coniferous sub-alpine forest. *Hydrological Processes*, **23**, 2498-2512.
---
## Quick Start
```bash
# 1. Build FSM2
cd /path/to/FSM2/source/repo
bash compil.sh
# 2. Run with example data
./FSM2 < nlst_Alptal.txt
# 3. View results
# Output files: Alptal_flux.txt, Alptal_stat.txt, Alptal_subc.txt
```
## Forcing a point column from a reanalysis grid cell (PROPOSED protocol)
This protocol was MOVED to the waiting room pending validation — see
`docs/proposed_protocols.yaml` (id: pp_fsm2_esm_snowmip_forcing). Status: PROPOSED,
not validated by a clean run. The supporting tools (`--precip-scale`,
`--phase-logistic` on `tools/convert_forcing_to_fsm2.py`) are reviewer-approved and
inert by default; using them means following an UNPROVEN protocol — say so in your
report. Graduates back into this SKILL on one route='accept' run at a point-support
obs, or a second independent witness.
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!