Imported from lzwei196/KISS---Knowledge-Infrastructure-for-Scientific-Simulation (
models/Alpine3D/SKILL.md). Install upstream withnpx skills add lzwei196/KISS---Knowledge-Infrastructure-for-Scientific-Simulation --skill Alpine3D. Copyright stays with the author.
MANDATORY EXECUTION POLICY — READ BEFORE PROCEEDING
You MUST run the actual model binary or package described in this document. If the model fails to import, compile, or execute, you MUST:
- Check
diagnostics/triplets.yamlfor a matching error pattern- Try the fix suggested in the triplet's
remedysection- If still failing, report the error to the user with full details
You MUST NOT substitute a simplified Python formula, regression equation, or hand-coded approximation in place of the real model.
Before starting, run:
python preflight_check.py(in this KI directory) to verify that the model binary/package and required data are available.DEBUGGING PROTOCOL — When something goes wrong, follow this order:
- Check triplets —
diagnostics/triplets.yamlmay already cover this error- Read official docs — The model's own documentation for expected formats/units
- Find working examples — Check
outputs/or the model's shipped test data- Fix the tool — With knowledge of what "correct" looks like
Do NOT write custom debug scripts. The answers are in the docs and examples.
KI map — what to read, and when
| when you need | read | why |
|---|---|---|
| FIRST, always | preflight_check.py |
run it (python preflight_check.py): proves env/binary/data are usable and emits a machine-readable PREFLIGHT_REPORT= line. Do not debug a run that never had a healthy environment. |
| to run the pipeline stages | tools/ (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. |
| before running a stage | docs/s*_*.md (5 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 (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 (35 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-08-17 from the KI's actual contents — 9 components present. Refresh: python3 ki_tools_common/generate_skill_map.py --ki_dir <this KI>.
Executable tool index (projected — complete by construction)
Every public tool in this KI, by exact path. What each is FOR lives in the
human-written Tool Inventory above; --help on any of these prints its arguments.
| tool (exact path) | invocation |
|---|---|
tools/convert_forcing_to_smet.py |
KISSPATH_PYTHON_ENV/bin/python {KI}/tools/convert_forcing_to_smet.py --help |
tools/generate_sno_files.py |
KISSPATH_PYTHON_ENV/bin/python {KI}/tools/generate_sno_files.py --help |
tools/parse_alpine3d_output.py |
KISSPATH_PYTHON_ENV/bin/python {KI}/tools/parse_alpine3d_output.py --help |
tools/run_alpine3d.py |
KISSPATH_PYTHON_ENV/bin/python {KI}/tools/run_alpine3d.py --help |
4 public tools; _-prefixed helpers and packaging files excluded.
Alpine3D Knowledge Infrastructure
- Package: hydrocraft-alpine3d-snow
- Version: 1.0.0
- Model: Alpine3D (with MeteoIO + SNOWPACK)
- Domain: Cryosphere — spatially distributed snow, energy balance, and runoff in mountainous terrain
- Created: 2026-03-26
- Last updated: 2026-04-30
- Tools: 4 | Skill Documents: 6 | Diagnostic Triplets: 22 | Validation: T3 (3 SNOTEL sites, 5 WY each)
⛔ READ FIRST — the SNOTEL precipitation column is NOT cumulative (2026-08-09)
The NRCS SNOTEL daily export column
Precipitation Accumulation (in) Start of Day Values
is the DAILY INCREMENT, in inches, despite the word "Accumulation" in its name. It is not a water-year running total. Evidence at SNOTEL 590 (2005-10 → 2015-09): the raw column sums to 1069 mm/yr, its largest single value in ten years is 2.10 in (a water-year cumulative would have to exceed 40 in), and only 70% of day-to-day steps are non-negative.
Every Alpine3D run in this KI up to 2026-08-09 applied .diff().clip(lower=0)
to that column (t3_runs/build_site.py → build_snotel_forcing), which keeps
only the day-over-day increases and discards ~44% of the precipitation:
| site | raw column | after .diff() |
loss |
|---|---|---|---|
| 590 Lone Mountain, MT | 1069 mm/yr | 604 mm/yr | −43% |
| 828 Trial Lake, UT | 1106 | 669 | −40% |
| 637 Mores Creek Summit, ID | 1200 | 718 | −40% |
| 679 Paradise, WA | 3832 | 1740 | −55% |
This single data bug is the −40…−60% SWE PBIAS reported at every validated
site below, and the reason NSE stalled in the 0.31–0.39 band. The physical
explanations previously given for that bias — "the gauge ~1660 mm/yr cannot
build the observed 2.3 m pillow" at Paradise, "pillow over-read", "orographic
undercatch" — attribute a parsing bug to physics. Note that the dt_023
maritime remedy factor (×1.9) is close to the reciprocal of the Paradise loss
(3832/1740 = 2.2): it was numerically compensating for the differencing, not
correcting the gauge.
Correct handling: use the column AS IS, converting inches → mm
(convert_forcing_to_smet.py --precip-unit in). At a fresh continental site
with no precip factor at all this yields NSE ≈ 0.83 / PBIAS ≈ −10% on daily
SWE. Before re-deriving any SWE bias as a physical effect, check the forcing
total against the raw gauge.
Resolving a SNOTEL station (coordinates, elevation, timezone)
Do not hand-type site coordinates. The NRCS AWDB REST API answers directly and is reachable from this host:
curl -s "https://wcc.sc.egov.usda.gov/awdbRestApi/services/v1/stations?stationTriplets=590:MT:SNTL&activeOnly=false"
# -> name, latitude, longitude, elevation (FEET — multiply by 0.3048),
# dataTimeZone, beginDate/endDate
The station triplet is <id>:<STATE>:SNTL; the id is the filename stem of
KISSPATH_OBS/snotel/<id>_daily.csv and the state is
in that file's # SNOTEL <id>: <Name>, <ST> comment line.
Validated Test Cases (Tier-3, real DEM + real obs)
Located at KISSPATH_BINARIES/Alpine3D/t3_runs/:
⚠️ Every row in the table below was produced with the differenced (i.e. ~44% too dry) precipitation described above. Treat their NSE/PBIAS as a floor, not as Alpine3D's skill. The 2026-08-09 Lone Mountain row is the first run with correct precipitation.
| Site | SNOTEL | Climate | r | NSE | KGE | PBIAS | Period | precip |
|---|---|---|---|---|---|---|---|---|
| Lone Mountain, MT | 590 | continental | see below | see below | 2010-10 → 2020-09 (held out) | raw (correct) |
| Site | SNOTEL | Climate | r | NSE | KGE | Period |
|---|---|---|---|---|---|---|
| Trial Lake, UT | 828 | continental | 0.79 | +0.31 | +0.19 | 2014-10 → 2019-09 |
| Mores Creek Summit, ID | 637 | continental-W | 0.85 | +0.39 | +0.20 | 2014-10 → 2019-09 |
| Paradise, WA (Mt Rainier) | 679 | maritime | 0.62 | −0.22 | −0.11 | 2014-10 → 2019-09 (baseline, no remedy) |
| Paradise, WA (Mt Rainier) | 679 | maritime | 0.71 | +0.33 | +0.46 | 2006-01 → 2015-12 (dt_023 remedy, 1×1) |
Continental sites have positive skill against daily SNOTEL SWE. Maritime
sites (Paradise) fail two ways under single-station daily disaggregation:
(1) orographic precip is undercaught and the SNOTEL gauge under-reads heavy
wet snow (gauge ~1660 mm/yr cannot build the observed 2.3 m pillow), and
(2) the synthetic ±6 K diurnal TA cycle + high roughness drive excessive
turbulent melt, so the pack peaks in Jan–Mar and melts out months early
(Apr–Jun sim/obs SWE ratio ≈ 0.09). The undercatch_wmo filter (dt_020)
alone is far too weak. Maritime remedy (dt_023, added 2026-06-19): apply
a multiplicative precip undercatch factor (~1.9) via
convert_forcing_to_smet.py --precip-undercatch-factor, damp the diurnal TA
amplitude to ~2.5 K, lower ROUGHNESS_LENGTH to ~0.003 m, and raise the
PSUM_PH snow threshold to ~275.35 K. build_site.py now carries these as
per-site precip_factor/diurnal_amp/roughness/snow_thresh_K overrides
on Paradise_WA. Driver: t3_runs/build_site.py + validate_all.py.
Maritime remedy VERIFIED (2026-06-19, SNOTEL 679, WY2006–2015): the dt_023
overrides were tested on the full 2006-01-01..2015-12-31 daily SWE window and
PASS: NSE +0.33, r 0.71, KGE +0.46, PBIAS −40% (n=3651 days; cold
start 2005-10-01, scored from 2006-01-01). This overturns the −0.22 baseline.
The pack still under-accumulates (sim mean 511 vs obs 853 mm; the ×1.9 factor is
a floor, not a ceiling) but peak SWE (2442 vs 2764 mm) and timing (r 0.71) are now
good. Three new build_site.py knobs make this run reproducible and tractable:
- Period is now configurable per-site via
sim_start/sim_end/eval_start(was hardcoded WY15-19). Cold-start the autumn before the eval window;observed_swe.csvis filtered toeval_startso the spinup autumn is excluded from metrics. - Deep-maritime tractability: a 10-yr Paradise run at 11×11/15-min is
compute-bound (the correct 2+ m pillow grows the finite-element count, so
per-step cost balloons — ~5 h+ and climbing). Single-station point-SWE only
scores the POI cell, so
grid_n=1+calc_step=60+light_output=True(1×1 column, 1 SNOWPACK sub-step/hr, grids/prof off, daily TS) cuts it to ~15 min with no change to the scored center-cell SWE. The validated 11×11 config remains the reference for any spatial fidelity claim. - POI centering fix: the POI is now written at the centre of the centre cell
(
poi_e/poi_n), not the cell corner. A corner POI is on the domain edge and Alpine3D aborts withInvalidArgument: Invalid POI— fatal on a 1×1 grid. Harmless (+50 m, same cell) on larger grids. validate_all.pyPOI glob generalized from the hardcoded5_*index to*_<exp>.smet, so it finds the POI file for any grid size (1×1 →0_0_…).
Note: the build_site.py io.ini template has no [SnowpackAdvanced] section,
so ALPINE3D defaults FALSE (dt_016 warning at startup) — the run is effectively
point-mode per cell. For single-station single-POI SWE validation this is benign
(and consistent with the 1×1 reduction above), but a distributed/spatial run MUST
add [SnowpackAdvanced]\nALPINE3D = TRUE.
Data Preparation
Forcing data
Data Sources: Use from ki_tools_common.load_forcing import load_daily_forcing for CMFD/MSWX/NASA POWER.
Data Validation Reference: See data_ki/CMFD/SKILL.md for atmospheric forcing documentation.
See data_ki/SNOTEL/SKILL.md for snow observations.
See data_ki/BedMachine/SKILL.md for ice topography.
See data_ki/MEaSUREs/SKILL.md for ice velocity.
Overview
Alpine3D is a spatially distributed, three-dimensional model for analyzing and predicting snow-dominated surface processes in mountainous topography. It couples:
- MeteoIO — meteorological data I/O, quality filtering, temporal/spatial interpolation
- SNOWPACK — detailed multi-layer snow and soil physics (energy + mass balance)
- EnergyBalance — terrain-aware radiation transfer (shading, view factors, terrain reflection)
- SnowDrift — wind-driven snow erosion, saltation, suspension, and deposition (optional)
- Runoff — integrated surface/soil runoff from snowmelt and rainfall
The model reads meteorological station data (SMET format), a DEM (ARC grid), land-use grids, and initial snow profiles (.sno), then produces gridded fields of SWE, snow depth, temperature, radiation, and runoff at configurable time intervals.
Primary applications: snow water resources, avalanche forecasting, climate change impact assessment, permafrost studies, and ski resort management.
6. Output Description
This section restates the KI's dag facts. If this section and dag.yaml
disagree, dag.yaml wins.
Headline output (validation_rank: 1):
SWE— Snow water equivalent per pixel (bulk snowpack mass). (kg/m2)
Output variable (dag var) |
Rank | Unit | Dag description / scope |
|---|---|---|---|
| SWE | 1 | kg/m2 | Snow water equivalent per pixel (bulk snowpack mass). |
| HS | listed dag output | see dag.yaml |
other dag output |
| TSS | listed dag output | see dag.yaml |
other dag output |
| MS_SNOWPACK_RUNOFF | listed dag output | see dag.yaml |
other dag output |
| MS_SOIL_RUNOFF | listed dag output | see dag.yaml |
other dag output |
| RSNO | listed dag output | see dag.yaml |
other dag output |
| POI energy/mass-balance terms (.met) | listed dag output | see dag.yaml |
point time-series energy/mass-balance terms |
The model also writes gridded ARC outputs and POI .met time series as
described in "Output File Formats" below. For scoring and obs-binding, treat
SWE as the headline variable because the dag ranks it first.
8. Unit Conversion Table
Exact I/O shapes live in docs/format_spec.yaml; this table summarizes the
unit conversions and traps already documented in this KI body. Alpine3D uses
SI/MKSA units internally.
| Variable | Source unit / common incoming unit | Model unit | Conversion | Trap ID / note |
|---|---|---|---|---|
| Air temperature (TA) | Celsius (degC) | Kelvin (K) | +273.15 | dt_001 |
| Relative humidity (RH) | Percent (0-100) | Fraction (0-1) | divide by 100 | dt_002 |
| Wind speed (VW) | km/h | m/s | divide by 3.6 | dt_003 |
| Wind direction (DW) | radians | degrees from N (0-360) | multiply by 180/pi | dt_004 |
| Precipitation (PSUM) | m | kg/m2, equivalent to mm water | multiply by 1000 | dt_005 |
| NRCS SNOTEL precipitation | inches, daily increment | mm water | convert inches to mm with convert_forcing_to_smet.py --precip-unit in; do not apply .diff() |
2026-08-09 SNOTEL warning |
| Pressure (P) | hPa/mbar | Pa | multiply by 100 | dt_006 |
| Incoming shortwave radiation (ISWR) | MJ/m2/day | W/m2 | multiply by 11.574 | dt_007 |
| Incoming longwave radiation (ILWR) | MJ/m2/day | W/m2 | multiply by 11.574 | dt_008 |
| Snow height (HS) | cm | m | divide by 100 | dt_009 |
| Layer thickness (.sno) | cm or mm | m | divide by 100 or 1000 | dt_010 |
| Grain radius (rg in .sno) | um | mm | divide by 1000 | dt_011 |
| Soil density | g/cm3 | kg/m3 | multiply by 1000 | dt_012 |
| Geothermal heat (GEO_HEAT) | mW/m2 | W/m2 | divide by 1000 | dt_013 |
| Roughness length | mm | m | divide by 1000 | dt_014 |
| Timestep (CALCULATION_STEP_LENGTH) | seconds | minutes | divide by 60 | dt_015 |
Output units explicitly documented in this KI body:
| Output | Unit |
|---|---|
| SWE | kg/m2 |
| HS | m |
| TSS | K |
| MS_SNOWPACK_RUNOFF | kg/m2 |
| MS_SOIL_RUNOFF | kg/m2 |
| RSNO | kg/m3 |
| POI energy/mass-balance terms (.met) | see .met fields and dag.yaml |
11. Validated Results
This section restates the KI's validation facts and the convention bar. If this
section and docs/validation_convention.yaml disagree, the convention file
wins.
Performance Metrics — judged against the field's bar, not intuition
SWE is judged by nrmse with direction minimize.
| SWE convention band | Threshold | Citation |
|---|---|---|
| very_good | no cited threshold | esmsnowmip2018 |
| good | no cited threshold | esmsnowmip2018 |
| satisfactory | nrmse <= 1.0 | esmsnowmip2018 |
No nrmse achieved value is stated in the validated-run body below. Existing
validated SWE rows report r, NSE, KGE, and PBIAS; do not translate those
metrics into the convention bar without a sourced nrmse value.
Documented Validated SWE Runs
| Site | SNOTEL | Climate | r | NSE | KGE | PBIAS | Period | Precipitation note |
|---|---|---|---|---|---|---|---|---|
| Lone Mountain, MT | 590 | continental | see existing table below | see existing table below | 2010-10 to 2020-09 (held out) | raw (correct) | ||
| Trial Lake, UT | 828 | continental | 0.79 | +0.31 | +0.19 | 2014-10 to 2019-09 | differenced precipitation; floor | |
| Mores Creek Summit, ID | 637 | continental-W | 0.85 | +0.39 | +0.20 | 2014-10 to 2019-09 | differenced precipitation; floor | |
| Paradise, WA (Mt Rainier) | 679 | maritime | 0.62 | -0.22 | -0.11 | 2014-10 to 2019-09 | baseline, no remedy; differenced precipitation; floor | |
| Paradise, WA (Mt Rainier) | 679 | maritime | 0.71 | +0.33 | +0.46 | -40% | 2006-01 to 2015-12 | dt_023 remedy, 1x1 |
Rows marked "differenced precipitation" were produced before the 2026-08-09 SNOTEL precipitation correction documented above. Treat their NSE/PBIAS as a floor, not as Alpine3D's skill.
Installation
Dependencies
| Component | Minimum Version | Purpose |
|---|---|---|
| CMake | 3.1+ | Build system |
| C++ compiler | C++11 (g++/clang++) | Compilation |
| MeteoIO | 2.5.1+ | Data I/O and interpolation |
| SNOWPACK | 3.7+ | Snow physics library |
| OpenMP (optional) | — | Shared-memory parallelism |
| MPI (optional) | — | Distributed-memory parallelism |
| Doxygen (optional) | — | Documentation generation |
Build from Source
The source tree must have MeteoIO, SNOWPACK, and Alpine3D as siblings:
Source/
├── meteoio/
├── snowpack/
└── alpine3d/
Build sequence (MeteoIO → SNOWPACK → Alpine3D):
# 1. Build MeteoIO
cd Source/meteoio && mkdir build && cd build
cmake .. && make -j$(nproc) && sudo make install
# 2. Build SNOWPACK
cd ../../snowpack && mkdir build && cd build
cmake .. && make -j$(nproc) && sudo make install
# 3. Build Alpine3D
cd ../../alpine3d && mkdir build && cd build
cmake .. -DOPENMP=ON # add -DMPI=ON for cluster support
make -j$(nproc) && sudo make install
Binary Location
After build: alpine3d/build/bin/alpine3d
After install: /usr/local/bin/alpine3d or the GeoForge-managed location: KISSPATH_BINARIES/Alpine3D/source/repo/Source/alpine3d/bin/alpine3d
Test
cd alpine3d/tests/simple
PROG_ROOTDIR=../../build ./run_simple.sh
# Runs 3-month Dischma simulation, compares with reference output
Pipeline Stages
| # | Stage | Tool | Description |
|---|---|---|---|
| s0 | Configuration | — | Create working directory structure and io.ini |
| s1 | DEM preparation | — | Prepare ARC-format DEM and land-use grids |
| s2 | Meteorological forcing | convert_forcing_to_smet.py |
Convert global/reanalysis data to SMET format |
| s3 | Initial snow profiles | generate_sno_files.py |
Generate .sno files for each grid cell |
| s4 | Spatial interpolation config | — | Configure [Interpolations2D] in io.ini |
| s5 | Execution | run_alpine3d.py |
Run alpine3d binary with proper options |
| s6 | Output parsing | parse_alpine3d_output.py |
Extract gridded results to CSV/analysis format |
| s7 | Validation | — | Compare SWE/HS against observations |
Parallelism notes: Stages s2 and s3 can run in parallel. Stage s5 supports OpenMP (shared memory) and MPI (distributed memory) parallelism for the energy balance and snowpack computations.
Tools Reference
| Tool | Stage | Script | Lines | Purpose |
|---|---|---|---|---|
| convert_forcing_to_smet | s2 | tools/convert_forcing_to_smet.py |
~430 | Convert meteo data to SMET format with unit corrections |
| generate_sno_files | s3 | tools/generate_sno_files.py |
~250 | Create initial snow/soil profiles per pixel or per land-use class |
| run_alpine3d | s5 | tools/run_alpine3d.py |
~220 | Execution wrapper with config validation |
| parse_alpine3d_output | s6 | tools/parse_alpine3d_output.py |
~260 | Parse ARC grid outputs to CSV time series |
Options added 2026-08-09 (a full SNOTEL site can now be built with the KI tools
alone; t3_runs/build_site.py is no longer required):
| Tool | Option | Purpose |
|---|---|---|
| convert_forcing_to_smet | --precip-unit in |
inches → mm (NRCS SNOTEL, NWS COOP) |
| convert_forcing_to_smet | --daily-disaggregate |
daily records → 24 hourly SMET rows (Alpine3D cannot run on daily forcing) |
| convert_forcing_to_smet | --diurnal-amp |
half-amplitude of the synthetic diurnal TA cycle; 6 K continental, 2.5 K maritime (dt_023) |
| convert_forcing_to_smet | --precip-undercatch-factor |
multiplicative PSUM correction (dt_020/dt_023) — re-derive it after dt_024; a value >1.5 on SNOTEL forcing means the differencing bug is still present |
| generate_sno_files | --experiment / --naming / --landuse-code |
emit the .sno file name Alpine3D actually searches for (dt_025) |
| generate_sno_files | --epsg |
write easting/northing/epsg for a projected DEM instead of bogus lat/lon |
Reference end-to-end driver using only these tools:
KISSPATH_KI_ROOT/Alpine3D/run_and_score.py
(SNOTEL 590 Lone Mountain, MT; resumable at every stage).
Unit Trap Table
CRITICAL: Alpine3D uses SI/MKSA units internally. The most common cause of silent model failure is feeding data in wrong units. The table below documents every conversion trap:
| Variable | Alpine3D Unit | Common Source Unit | Conversion | Trap ID |
|---|---|---|---|---|
| Air temperature (TA) | Kelvin (K) | Celsius (°C) | +273.15 | dt_001 |
| Relative humidity (RH) | Fraction (0–1) | Percent (0–100) | ÷100 | dt_002 |
| Wind speed (VW) | m/s | km/h | ÷3.6 | dt_003 |
| Wind direction (DW) | Degrees from N (0–360) | Radians | ×(180/π) | dt_004 |
| Precipitation (PSUM) | kg/m² (mm water equiv.) | m | ×1000 | dt_005 |
| Pressure (P) | Pa | hPa/mbar | ×100 | dt_006 |
| Incoming SW radiation (ISWR) | W/m² | MJ/m²/day | ×11.574 (÷86400×1e6) | dt_007 |
| Incoming LW radiation (ILWR) | W/m² | MJ/m²/day | ×11.574 | dt_008 |
| Snow height (HS) | m | cm | ÷100 | dt_009 |
| Layer thickness (.sno) | m | cm or mm | ÷100 or ÷1000 | dt_010 |
| Grain radius (rg in .sno) | mm | µm | ÷1000 | dt_011 |
| Soil density | kg/m³ | g/cm³ | ×1000 | dt_012 |
| Geothermal heat (GEO_HEAT) | W/m² | mW/m² | ÷1000 | dt_013 |
| Roughness length | m | mm | ÷1000 | dt_014 |
| Timestep (CALCULATION_STEP_LENGTH) | minutes | seconds | ÷60 | dt_015 |
Configuration Reference (io.ini)
[General]
| Key | Type | Default | Description |
|---|---|---|---|
| BUFFER_SIZE | int | 370 | Data buffer size in days |
| BUFF_BEFORE | float | 1.5 | Buffer before start date (days) |
[Input]
| Key | Type | Default | Description |
|---|---|---|---|
| COORDSYS | string | — | Coordinate system (CH1903, UTM, etc.) |
| TIME_ZONE | float | 0 | Time zone offset from UTC |
| METEO | string | SMET | Meteorological input format |
| METEOPATH | path | — | Path to meteo files |
| STATION1..N | string | — | Station identifiers |
| SNOW | string | SMET | Snow profile format |
| SNOWPATH | path | — | Path to initial .sno files |
| DEM | string | ARC | DEM format |
| DEMFILE | path | — | Path to DEM grid |
| LANDUSE | string | ARC | Land use format |
| LANDUSEFILE | path | — | Path to land use grid |
| POI | string | SMET | Points of interest format |
| POIFILE | path | — | Path to POI file |
[Output]
| Key | Type | Default | Description |
|---|---|---|---|
| GRIDS_WRITE | bool | FALSE | Write gridded output |
| GRIDS_DAYS_BETWEEN | float | 1.0 | Grid output interval (days) |
| GRIDS_PARAMETERS | string | — | Space-separated list (HS SWE TA ISWR...) |
| GRID2D | string | ARC | Grid output format |
| GRID2DPATH | path | — | Grid output directory |
| TS_WRITE | bool | FALSE | Write time series at POIs |
| TS_DAYS_BETWEEN | float | 1.0 | Time series interval (days) |
| PROF_WRITE | bool | FALSE | Write snow profiles at POIs |
| PROF_DAYS_BETWEEN | float | 1.0 | Profile interval (days) |
| SNOW_WRITE | bool | FALSE | Write full snow state (for restart) |
| SNOW_DAYS_BETWEEN | float | — | Snow state interval (days) |
[Snowpack]
| Key | Type | Default | Description |
|---|---|---|---|
| CALCULATION_STEP_LENGTH | float | 15 | Timestep in minutes |
| ROUGHNESS_LENGTH | float | 0.002 | Surface roughness (m) |
| HEIGHT_OF_METEO_VALUES | float | 2.0 | Measurement height (m) |
| HEIGHT_OF_WIND_VALUE | float | 10.0 | Wind measurement height (m) |
| ATMOSPHERIC_STABILITY | string | MO_MICHLMAYR | Stability scheme |
| CANOPY | bool | FALSE | Enable canopy model |
| SNP_SOIL | bool | FALSE | Enable soil model |
| SOIL_FLUX | bool | TRUE | Enable soil heat flux |
| GEO_HEAT | float | 0.06 | Geothermal heat flux (W/m²) |
| SW_MODE | string | INCOMING | Shortwave mode (INCOMING/BOTH/REFLECTED) |
[SnowpackAdvanced]
| Key | Type | Default | Description |
|---|---|---|---|
| ALPINE3D | bool | TRUE | MUST be TRUE for Alpine3D |
| SNOW_EROSION | bool | TRUE | Enable wind erosion |
| WATERTRANSPORTMODEL_SNOW | string | BUCKET | Water transport model |
| THRESH_RAIN | float | 1.2 | Rain/snow threshold (K above freezing) |
| LB_COND_WATERFLUX | string | FREEDRAINAGE | Lower boundary condition |
[EBalance]
| Key | Type | Default | Description |
|---|---|---|---|
| TERRAIN_RADIATION | bool | TRUE | Enable terrain radiation effects |
| TERRAIN_RADIATION_METHOD | string | SIMPLE | Algorithm (SIMPLE/COMPLEX/HELBIG) |
[Interpolations2D] — Spatial Interpolation
Algorithms per variable: VAR::algorithms = ALG1 ALG2 ...
| Variable | Recommended Algorithms | Notes |
|---|---|---|
| TA | ODKRIG_LAPSE, IDW_LAPSE, AVG_LAPSE | Lapse rate ~-0.006 K/m |
| RH | LISTON_RH, IDW_LAPSE | Liston preserves dewpoint |
| PSUM | IDW_LAPSE, AVG_LAPSE | Fractional lapse ~0.0005/m |
| PSUM_PH | PPHASE | Threshold-based phase separation |
| VW, DW | LISTON_WIND | Terrain-adjusted wind |
| ISWR | IDW, AVG | Direct interpolation |
| ILWR | AVG_LAPSE | Rate ~-0.03125 W/m²/m |
| P | STD_PRESS | Barometric formula |
[Filters] — Input QC
Pattern: VAR::filterN = TYPE + VAR::argN::param = value
Common filters: min_max, rate, mad, undercatch_wmo
[Generators] — Gap Filling
Pattern: VAR::generatorN = TYPE
Common generators: ALLSKY_LW, CLEARSKY_LW, ISWR_ALBEDO
Command-Line Options
alpine3d [options]
Required:
-a, --startdate=YYYY-MM-DDTHH:MM Simulation start date
-z, --enddate=YYYY-MM-DDTHH:MM Simulation end date (or use --steps)
-i, --iofile=<file> Configuration file (default: io.ini)
Optional:
-n, --steps=<N> Number of timesteps (alternative to --enddate)
-b, --np-ebalance=<N> Workers for energy balance (default: 1)
-p, --np-snowpack=<N> Workers for snowpack (default: 1)
--enable-eb Enable energy balance module
--restart Restart from existing .sno files
-h, --help Display help
Input File Formats
SMET (Swiss Meteorological Exchange Text) — Meteo Data
SMET 1.1 ASCII
[HEADER]
station_id = WFJ2
station_name = Weissfluhjoch
latitude = 46.829897
longitude = 9.809315
altitude = 2540.0
nodata = -999
tz = 1
fields = timestamp TA RH VW DW ISWR ILWR PSUM HS
[DATA]
2014-10-01T00:00 277.25 0.85 3.2 270 0.0 285.0 0.0 0.0
2014-10-01T01:00 276.80 0.87 2.8 265 0.0 282.0 0.5 0.001
SNO (Initial Snow/Soil Profile)
The layer columns are parsed POSITIONALLY by
snowpack/plugins/SmetIO.cc::readSnowCover (~L417–457), so the fields line
must contain exactly these 19 tokens, in this order — including ne, the
number of finite elements per layer (use 1). Omitting ne shifts every later
column one position left, so CDot is read as the element count and the
profile initialises with zero elements.
Header key names are looked up literally: it is SoilAlbedo (not SoilAlb)
and CanopyLeafAreaIndex (not CanopyLAI).
Layers are read BOTTOM → TOP: the first [DATA] row is the deepest soil
layer. Put the thick layers first and the thin near-surface layers last, and
make temperature decrease from the first row to the last.
For a projected DEM, use easting / northing / epsg — SMET accepts that
pair as an alternative to latitude/longitude. Writing projected metres into
latitude/longitude makes MeteoIO reject the profile.
SMET 1.1 ASCII
[HEADER]
station_id = 0_0_my_experiment
easting = 466550.00
northing = 5013550.00
epsg = 32612
altitude = 2500
ProfileDate = 2014-10-01T00:00
nSnowLayerData = 0
nSoilLayerData = 3
SoilAlbedo = 0.2
BareSoil_z0 = 0.02
CanopyHeight = 0.00
CanopyLeafAreaIndex = 0.00
CanopyDirectThroughfall = 1.00
WindScalingFactor = 1.00
ErosionLevel = 0
TimeCountDeltaHS = 0.000000
fields = timestamp Layer_Thick T Vol_Frac_I Vol_Frac_W Vol_Frac_V Vol_Frac_S Rho_S Conduc_S HeatCapac_S rg rb dd sp mk mass_hoar ne CDot metamo
[DATA]
2014-10-01T00:00 1.0 282.15 0.0 0.15 0.35 0.50 1800 1.5 900 0.3 0.2 0.0 1.0 0 0.0 1 0.0 0.0
2014-10-01T00:00 0.5 281.15 0.0 0.20 0.30 0.50 1800 1.5 900 0.3 0.2 0.0 1.0 0 0.0 1 0.0 0.0
2014-10-01T00:00 0.5 280.15 0.0 0.25 0.25 0.50 1800 1.5 900 0.3 0.2 0.0 1.0 0 0.0 1 0.0 0.0
generate_sno_files.py emits all of the above correctly as of 2026-08-09; use
--naming landuse --experiment <EXPERIMENT> --landuse-code <code> --epsg <code>
for a single per-class profile, or --naming pixel for one file per cell.
ARC Grid (DEM, Land Use)
Standard ESRI ARC/INFO ASCII grid:
ncols 100
nrows 80
xllcorner 782000
yllcorner 180000
cellsize 25
NODATA_value -9999
1500 1502 1510 ...
POI (Points of Interest)
SMET 1.1 ASCII
[HEADER]
fields = easting northing altitude
[DATA]
785360 182255 2520
786100 181900 2350
Output File Formats
Gridded Output (ARC ASCII)
Written to GRID2DPATH at GRIDS_DAYS_BETWEEN intervals.
Filename pattern: YYYY-MM-DDThh.mm.ss_PARAM.asc
(e.g. 2014-12-23T12.00.00_SWE.asc) — MeteoIO's ARC2D plugin writes ISO-8601
dates with . substituted for the colons, which are illegal in many
filesystems. The compact YYYYMMDDHHMI_PARAM.asc form documented here before
2026-06-22 does not occur for SWE/HS/RSNO grids; a glob built on it matches
nothing. (parse_alpine3d_output.py accepts both.) Note that some
Alpine3D-internal grids — e.g. TSOIL1 — are written with the compact stamp,
so a directory listing can show both conventions side by side.
Grid NODATA_value is −999, not −9999.
Key output parameters:
- HS — Snow depth (m)
- SWE — Snow water equivalent (kg/m²)
- TA — Air temperature (K)
- TSS — Snow surface temperature (K)
- ISWR — Incoming shortwave radiation (W/m²)
- ILWR — Incoming longwave radiation (W/m²)
- MS_SNOWPACK_RUNOFF — Surface runoff (kg/m²)
- MS_SOIL_RUNOFF — Bottom runoff (kg/m²)
- RSNO — Snow mean density (kg/m³)
- ET — Evapotranspiration (kg/m²)
Time Series (.met SMET)
At each POI, written at TS_DAYS_BETWEEN intervals.
Contains full energy/mass balance terms.
Profiles (.pro)
Full snow stratigraphy at POI locations.
Critical Domain Knowledge
1. ALPINE3D = TRUE is mandatory (dt_016)
In [SnowpackAdvanced], ALPINE3D must be set to TRUE. If left at FALSE (Snowpack standalone
default), the model will run but produce incorrect spatial results because the Snowpack library
will not operate in distributed mode.
2. Temperature must be in Kelvin (dt_001)
All temperatures in SMET files must be in Kelvin. Feeding Celsius values creates temperatures near 0 K internally, causing immediate crashes or physically impossible surface energy balance.
3. Precipitation accumulation period must match timestep (dt_015)
PSUM::RESAMPLE1 = ACCUMULATE with PSUM::ARG1::PERIOD must match CALCULATION_STEP_LENGTH.
Mismatches cause either double-counting or loss of precipitation.
4. DEM and land-use grids must be co-registered (dt_017)
The DEM and land-use grids must have identical ncols, nrows, xllcorner, yllcorner, and cellsize. Mismatch causes a cryptic segmentation fault.
5. Coordinate system must be consistent (dt_018)
COORDSYS in [Input] and [Output] must be the same and must match the actual coordinate system
of the DEM file. Using WGS84 lat/lon with a projected DEM (or vice versa) causes all spatial
interpolation to fail silently.
6. SMET timestamps must be ISO 8601
Format: YYYY-MM-DDTHH:MM or YYYY-MM-DDTHH:MM:SS. Any deviation (e.g., spaces instead of T)
causes MeteoIO to fail to read the file.
7. Snow file naming convention (dt_024)
Alpine3D constructs the name it searches for in SnowpackInterface.cc
(~L1258–1260) and reads whichever exists, per pixel:
per-pixel (tried first) <ix>_<iy>_<EXPERIMENT>.sno e.g. 0_0_lone_mountain_mt.sno
per-class (fallback) <EXPERIMENT>_<landuse_code>.sno e.g. lone_mountain_mt_10851.sno
EXPERIMENTis[Output]::EXPERIMENTfrom io.ini — not the station id.ixis the column,iythe row counted from the BOTTOM (MeteoIO grids are south-up), so a 1×1 domain wants0_0_<EXPERIMENT>.sno.landuse_codeis the rounded value in the land-use grid (e.g. PREVAH 10851).
Any other name means Alpine3D finds no initial profile and aborts. Use
generate_sno_files.py --naming {pixel,landuse} --experiment <EXPERIMENT>.
8. Energy balance workers vs grid size
Setting --np-ebalance higher than the number of grid rows causes a crash. The number of
workers must be ≤ number of DEM rows.
9. Relative humidity range trap (dt_002)
RH in SMET must be 0–1 (fraction). Values 0–100 (percent) will be clipped to 1.0 by the min_max filter, making the entire domain saturated and producing unrealistic snowfall.
Calibration Parameters
| Parameter | Section | Range | Controls | Sensitivity |
|---|---|---|---|---|
| ROUGHNESS_LENGTH | Snowpack | 0.001–0.01 m | Turbulent exchange | High |
| GEO_HEAT | Snowpack | 0.0–0.1 W/m² | Basal melt rate | Medium |
| THRESH_RAIN | SnowpackAdvanced | 0.5–2.5 K | Rain/snow partitioning | High |
| HEIGHT_OF_METEO_VALUES | Snowpack | 1.5–10 m | Turbulent fluxes | Medium |
| ATMOSPHERIC_STABILITY | Snowpack | — | Stability correction | High |
| TA lapse rate | Interpolations2D | -0.003 to -0.009 K/m | Temperature field | High |
| PSUM lapse rate | Interpolations2D | 0.0001–0.001 /m | Precipitation gradient | High |
| TERRAIN_RADIATION_METHOD | EBalance | SIMPLE/COMPLEX/HELBIG | Radiation accuracy | Medium |
Quick Start
# 1. Build Alpine3D (from source directory)
cd Source/alpine3d && mkdir build && cd build
cmake .. -DOPENMP=ON && make -j$(nproc)
# 2. Create simulation directory
mkdir -p ~/sim/Dischma/{input/{meteo,surface-grids,snowfiles},output/{grids,snowfiles},setup}
# 3. Prepare forcing data
python tools/convert_forcing_to_smet.py \
--input era5_data.nc --output ~/sim/Dischma/input/meteo/ \
--stations WFJ2 DAV --start 2014-10-01 --end 2015-09-30
# 4. Generate initial snow profiles
python tools/generate_sno_files.py \
--dem ~/sim/Dischma/input/surface-grids/dem.asc \
--output ~/sim/Dischma/input/snowfiles/ \
--date 2014-10-01T00:00 --n-soil-layers 3
# 5. Run simulation
python tools/run_alpine3d.py \
--iofile ~/sim/Dischma/setup/io.ini \
--startdate 2014-10-01T01:00 --enddate 2015-09-30T00:00 \
--np-ebalance 4 --np-snowpack 4
# 6. Parse output
python tools/parse_alpine3d_output.py \
--grid-dir ~/sim/Dischma/output/grids/ \
--params SWE HS TA --output ~/sim/Dischma/results.csv
Diagnostic Triplets Summary
| ID | Severity | Domain | Summary |
|---|---|---|---|
| dt_001 | silent | unit_conversion | Temperature in °C instead of K |
| dt_002 | silent | unit_conversion | RH in % instead of fraction |
| dt_003 | silent | unit_conversion | Wind speed in km/h instead of m/s |
| dt_004 | silent | unit_conversion | Wind direction in radians |
| dt_005 | silent | unit_conversion | Precipitation in m instead of mm |
| dt_006 | silent | unit_conversion | Pressure in hPa instead of Pa |
| dt_007 | silent | unit_conversion | Radiation in MJ/m²/day instead of W/m² |
| dt_008 | silent | unit_conversion | ILWR in MJ/m²/day instead of W/m² |
| dt_009 | silent | unit_conversion | Snow height in cm instead of m |
| dt_010 | silent | unit_conversion | Layer thickness wrong units in .sno |
| dt_011 | silent | unit_conversion | Grain radius in µm instead of mm |
| dt_012 | silent | unit_conversion | Soil density in g/cm³ instead of kg/m³ |
| dt_013 | silent | unit_conversion | Geothermal heat in mW/m² |
| dt_014 | silent | unit_conversion | Roughness length in mm instead of m |
| dt_015 | degraded | parameter_format | Timestep/accumulation mismatch |
| dt_016 | silent | parameter_format | ALPINE3D = FALSE in SnowpackAdvanced |
| dt_017 | fatal | parameter_format | DEM/landuse grid mismatch |
| dt_018 | silent | parameter_format | Coordinate system mismatch |
File Structure
ki/
├── SKILL.md # This file
├── tools/
│ ├── convert_forcing_to_smet.py # Meteo data → SMET format
│ ├── generate_sno_files.py # Initial snow/soil profiles
│ ├── run_alpine3d.py # Execution wrapper
│ └── parse_alpine3d_output.py # Grid output → CSV
├── docs/
│ ├── s0_domain_setup.md # Domain and DEM preparation
│ ├── s2_meteorological_forcing.md # Forcing data preparation
│ ├── s3_initial_conditions.md # Initial snow/soil profiles
│ ├── s5_execution.md # Running Alpine3D
│ └── s6_output_analysis.md # Output parsing and analysis
└── diagnostics/
└── triplets.yaml # 18 symptom→diagnosis→remedy entries