Installs into .claude/skills of the current project.
Are you the author of BIOME BGC?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/lzwei196-biome-bgc)
> **MANDATORY EXECUTION POLICY** — READ BEFORE PROCEEDING
>
> You MUST run the **actual model binary or package** described in this document.
> If the model fails to import, compile, or execute, you MUST:
> 1. Check `diagnostics/triplets.yaml` for a matching error pattern
> 2. Try the fix suggested in the triplet's `remedy` section
> 3. If still failing, report the error to the user with full details
>
> You MUST NOT substitute a simplified Python formula, regression equation,
> or hand-coded approximation in place of the real model. Doing so produces
> scientifically invalid results and defeats the purpose of the KI.
>
> Before starting, run: `python preflight_check.py` (in this KI directory)
> **HWSD soil lookup:** Use `from ki_tools_common.soil_utils import lookup_hwsd` to get sand/silt/clay/OC/pH for any lat/lon. Returns texture class and Saxton-Rawls hydraulic properties.
> **Weather comes straight from the data source:** `tools/convert_forcing_to_bgc.py --source cmfd|mswx|nasa_power --lat <lat> --lon <lon> --start_year <y0> --end_year <y1> --output <met>` reads the product itself through `ki_tools_common.load_forcing.load_daily_forcing` and writes the met file plus `<met>.summary.json`. Do NOT call the loader yourself and do NOT build the met file from another model's input files. `--source fluxnet --forcing_file <site>/FULLSET_DD.csv` is the tower route. `--source` has no default.
> **HWSD soil lookup:** Use `from ki_tools_common.soil_utils import lookup_hwsd` to get sand/silt/clay/OC/pH for any lat/lon. Returns texture class and Saxton-Rawls hydraulic properties.
> 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/` (6 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` (3 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` (33 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. |
*Projected 2026-10-03 from the KI's actual contents — 9 components present. Refresh: `python3 ki_tools_common/generate_skill_map.py --ki_dir <this KI>`.*
<!-- KI-MAP:END -->
<!-- KI-TOOL-INDEX:BEGIN (projected by generate_skill_map.py — the discoverability contract: every public tool, exact path; PURPOSE stays human-authored elsewhere) -->
### Executable tool index (projected — complete by construction)
Every public tool in this KI, by exact path. What each is FOR lives in the
human-written Tool Inventory above; `--help` on any of these prints its arguments.
| tool (exact path) | invocation |
|---|---|
| `tools/convert_forcing_to_bgc.py` | `KISSPATH_PYTHON_ENV/bin/python {KI}/tools/convert_forcing_to_bgc.py --help` |
| `tools/generate_site_ini.py` | `KISSPATH_PYTHON_ENV/bin/python {KI}/tools/generate_site_ini.py --help` |
| `tools/parse_bgc_output.py` | `KISSPATH_PYTHON_ENV/bin/python {KI}/tools/parse_bgc_output.py --help` |
| `tools/run_bgc.py` | `KISSPATH_PYTHON_ENV/bin/python {KI}/tools/run_bgc.py --help` |
| `tools/run_bgc_spinup.py` | `KISSPATH_PYTHON_ENV/bin/python {KI}/tools/run_bgc_spinup.py --help` |
| `tools/select_ecophysiology.py` | `KISSPATH_PYTHON_ENV/bin/python {KI}/tools/select_ecophysiology.py --help` |
*6 public tools; `_`-prefixed helpers and packaging files excluded.*
<!-- KI-TOOL-INDEX:END -->
---
## Data Preparation
### Forcing data
**Data Sources**: this KI's own tool reads the source and writes the met file in one step:
```bash
# gridded / point product at the site (shared loader inside the tool)
python tools/convert_forcing_to_bgc.py --source nasa_power \
--lat <lat> --lon <lon> --start_year <y0> --end_year <y1> --output metdata/site.mtc43
python tools/convert_forcing_to_bgc.py --source cmfd --forcing_dir <CMFD store> \
--lat <lat> --lon <lon> --start_year <y0> --end_year <y1> --output metdata/site.mtc43 # China only
python tools/convert_forcing_to_bgc.py --source mswx --forcing_dir KISSPATH_FORCING \
--lat <lat> --lon <lon> --start_year <y0> --end_year <y1> --output metdata/site.mtc43 # global, slow
# flux tower with its own weather
python tools/convert_forcing_to_bgc.py --source fluxnet --forcing_file <site>/FULLSET_DD.csv \
--lat <lat> --start_year <y0> --end_year <y1> --output metdata/site.mtc43
```
- `--source` is required. cmfd / mswx / nasa_power go through `ki_tools_common.load_forcing.load_daily_forcing` inside the tool; you do not call the loader yourself.
- The tool takes Tmax, Tmin, precipitation (mm/day -> cm/day), specific humidity and the source's OWN surface pressure (-> VPD in Pa), and the 24-h mean shortwave (-> daylight average); Feb-29 is dropped.
- Nothing is filled. A missing or non-finite value, an uneven time axis or a period the source does not fully cover stops the tool (exit 2) and no file is written (dt_029).
- It also writes `<output>.summary.json` (source, point, period, mean annual precipitation, mean temperature). Compare these two numbers with the site's climate before running the model.
- cmfd: always pass `--forcing_dir` (e.g. `KISSPATH_DATA/forcing/Data_forcing_03hr_010deg`); about 50 s per year for one point. mswx: about 15 min per year (5 variables, about 3 min each), read one file at a time (dt_033). nasa_power: network, about 2 s per year, daily data from 1981.
- The met file is never built from another model's input files (dt_028).
**Data Validation Reference**: See `data_ki/CMFD/SKILL.md` for CMFD unit documentation and known traps.
---
# BIOME-BGC 4.2 -- Knowledge Infrastructure
> **Model**: BIOME-BGC 4.2 (University of Montana NTSG)
> **Domain**: Terrestrial biogeochemistry -- daily C/N/water cycling for forests, grasslands, shrublands
> **Pipeline stages**: 8 | **Tools**: 6 | **Skill documents**: 3 | **Diagnostic triplets**: 25
> **Binary**: `KISSPATH_BINARIES/biome-bgc/bgc-src/bgc`
> **Status**: binary_only (bundled example data validated 2026-03-24)
## 1. Model Identity
| Property | Value |
|----------|-------|
| Full name | BIOME-BGC 4.2 |
| Version | 4.2 |
| Language | C (ANSI C89) |
| License | Copyrighted, free download from NTSG, no redistribution |
| Repository | https://www.umt.edu/numerical-terradynamic-simulation-group/project/biome-bgc.php |
| Primary domain | Terrestrial biogeochemistry |
| Spatial mode | 0-D point; gridded coverage is one execution per cell |
| Temporal mode | Daily, 365-day year |
## Bundled Example Sanity Check -- Missoula ENF
| Metric | Value | Expected Range | Status |
|--------|-------|---------------|--------|
| Spinup convergence | 2112 model years | 3000-6000 (with acceleration) | OK |
| Spinup residual | -0.001184 | < 0.01 | OK |
| Wall time (spinup) | 0.5 s | 1-5 min | OK |
| Vegetation C | 10.44 kgC/m2 | 5-25 | OK |
| Soil C | 10.19 kgC/m2 | 5-20 | OK |
| Total C | 23.86 kgC/m2 | 15-40 | OK |
| Max LAI | 1.44-1.71 m2/m2 | 1-8 | OK (cold start) |
| Litter C | 3.23 kgC/m2 | 0.5-5 | OK |
| Soil mineral N | 0.000018 kgN/m2 | >0 | OK |
**Test conditions**: Missoula MT, 46.8N, 977m, ENF, 1950-1993, 44 years, CO2=294.8 ppm.
## 2. What This Model Does
BIOME-BGC simulates daily carbon, nitrogen, and water fluxes through natural terrestrial ecosystems. It is the complement to LDNDC in HydroCraft: LDNDC handles cropland/managed systems, BIOME-BGC handles forests, grasslands, and shrublands. Together they enable full-basin carbon budgets.
**Core capabilities**:
- GPP, NPP, NEE, ecosystem respiration (FLUXNET-compatible)
- Vegetation, litter, and soil organic carbon pools (CENTURY-style 4+4 pools)
- Penman-Monteith ET, soil water balance, snow dynamics
- Deciduous/evergreen phenology (temperature + photoperiod driven)
- Multi-century spinup to steady-state (required for realistic soil C/N initialization)
- 7 plant functional types: ENF, EBF, DNF, DBF, SHRUB, C3GRASS, C4GRASS
**What BIOME-BGC adds beyond LDNDC**:
- Forest carbon cycling (designed for forests, not crops)
- Built-in spinup mode (1000-6000 model years)
- CENTURY-style SOM decomposition (4 litter + 4 soil pools)
- Farquhar photosynthesis (mechanistic, not empirical)
## 3. Input Requirements
**Exact shapes live in `docs/format_spec.yaml`** (projected from `dag.yaml` + `diagnostics/triplets.yaml`; regenerate it, never hand-edit it). This section explains intent and traps; the spec file is the contract.
### 3.1 Meteorological Forcing
| Variable | Unit model expects | Model input format | Source dataset | Source unit | Conversion |
|----------|-------------------|--------------------|----------------|-------------|------------|
| Tmax | deg C | daily met file column 3 | CMFD/MSWX/NASA POWER (shared loader) or FLUXNET tower | deg C | none (the loader already gives deg C) |
| Tmin | deg C | daily met file column 4 | CMFD/MSWX/NASA POWER (shared loader) or FLUXNET tower | deg C | none (the loader already gives deg C) |
| Tday | deg C | daily met file column 5 | computed | deg C | `Tmin + 0.45*(Tmax-Tmin)` |
| prcp | cm/day | daily met file column 6 | CMFD/MSWX/NASA POWER (shared loader) or FLUXNET tower | mm/day | divide by 10 |
| VPD | Pa | daily met file column 7 | computed from the source's specific humidity and the source's own surface pressure (cmfd/mswx/nasa_power), or tower VPD (fluxnet, hPa) | kg/kg + Pa, or hPa | `es(Tday) - q*P/(0.622+0.378*q)`; tower hPa x 100 |
| srad | W/m2 **daylight average** (`metv.swavgfd`) | daily met file column 8 | CMFD/MSWX/NASA POWER/FLUXNET | W/m2 **24-h mean** | multiply by 86400/daylen (energy-conserving; done by `convert_forcing_to_bgc.py`, dt_027) |
| daylen | s | daily met file column 9 | computed from latitude and day-of-year | s | compute astronomically |
| atmospheric CO2 | ppm | constant or annual file in CO2_CONTROL | user/scenario | ppm | none |
| atmospheric N deposition | kgN/m2/yr | constant, ramped, or annual file | user/scenario | kgN/m2/yr | none |
### 3.2 Static Inputs
| Input | Source | Tool that prepares it |
|-------|--------|----------------------|
| Site latitude/elevation/soil depth | user or gridded domain metadata | `tools/generate_site_ini.py` |
| Sand/silt/clay fractions | HWSD lookup or supplied soil database | `ki_tools_common.soil_utils.lookup_hwsd`, then `tools/generate_site_ini.py` |
| Land cover / PFT | AVHRR land-cover class or explicit PFT | `tools/select_ecophysiology.py` |
| Ecophysiology parameters | PFT defaults in `lib/bgc_utils.py` or site-specific `.epc` | `tools/select_ecophysiology.py` |
| Spinup restart endpoint | BIOME-BGC spinup run | `tools/run_bgc_spinup.py` |
### 3.3 Configuration Files
| File | Format | Notes |
|------|--------|-------|
| `.ini` | keyword-section text | Sequential `scan_value()` format; generate with `tools/generate_site_ini.py`, do not hand-edit. |
| `.epc` | line-ordered ecophysiology text | PFT-specific parameter file generated by `tools/select_ecophysiology.py`. |
| `.mtc43` / met file | space-separated daily meteorological columns | 365-day years; `Tday` is required for column alignment even though the model discards it. |
| `.endpoint` | binary restart | Must be written and read by the same compatible BIOME-BGC binary. |
## 4. Build Instructions
The KI uses the compiled binary at `KISSPATH_BINARIES/biome-bgc/bgc-src/bgc`. Before any run, execute `python preflight_check.py` in this KI directory.
```bash
cd KISSPATH_BINARIES/biome-bgc/bgc-src
./bgc
```
Known build issue from `diagnostics/triplets.yaml`: `dt_001` covers modern compiler failures from C99/C11 strict mode on ANSI C89 code; use the pre-compiled binary when available.
## 5. Execution
Use the wrappers in `tools/` for generated inputs, spinup, normal execution, and parsing. A normal scientific run requires spinup first; do not cold-start production carbon-flux analysis.
## Pipeline
| Stage | Name | Tools | Skill Doc |
|-------|------|-------|-----------|
| S1 | Site Definition (.ini) | `generate_site_ini.py` | `docs/s1_site_definition_skill.md` |
| S2 | Ecophysiology (.epc) | `select_ecophysiology.py` | -- |
| S3 | Meteorological Forcing | `convert_forcing_to_bgc.py` | `docs/s3_meteorological_forcing_skill.md` |
| S4 | CO2 Concentration | (set in .ini or co2.txt) | -- |
| S5 | Spinup Execution | `run_bgc_spinup.py` | `docs/s5_spinup_strategy_skill.md` |
| S6 | Normal Execution | `run_bgc.py` | -- |
| S7 | Output Analysis | `parse_bgc_output.py` | -- |
| S8 | Comparison with VIC results (optional) | (orchestrate per-cell; BIOME-BGC input is never made from VIC input files) | -- |
## 6. Output Description
**Source: `dag.yaml`. If this section and the dag disagree, `dag.yaml` wins.**
**Headline output** (the dag's `validation_rank: 1` variable; the one this model is judged by):
> `daily_gpp` -- Gross primary production (sunlit + shaded canopy photosynthesis) (`kgC/m2/d`)
| Output variable (dag `var`) | rank | Emitted in | Unit | Description |
|-----------------------------|------|------------|------|-------------|
| `daily_gpp` | 1 | `.dayout` (binary, optional `.dayout.ascii`); annual summary as ann NPP/NPB text | `kgC/m2/d` | Gross primary production (sunlit + shaded canopy photosynthesis) |
| `daily_npp` | 2 | `.dayout`; annual ann NPP (`gC/m2/yr`) in text summary | `kgC/m2/d` | Terrestrial ecosystem net primary production = GPP - maintenance - growth respiration |
| `ET` | 3 | `.dayout` / annual ann ET text summary | `kgH2O/m2/d` | Evapotranspiration (Penman-Monteith): transpiration + canopy + bare-soil evaporation |
| `daily_nee` | 4 | `.dayout`; annual NBP in text summary | `kgC/m2/d` | Net ecosystem exchange = NEP - fire losses (negative = sink) |
| `daily_nep` | 5 | `.dayout` | `kgC/m2/d` | Net ecosystem production = NPP - heterotrophic respiration |
| `daily_hr` | 6 | `.dayout` | `kgC/m2/d` | Soil and litter heterotrophic (decomposition) respiration |
| `proj_lai` | 7 | `.dayout` (binary index 509); annual max LAI in text summary | `m2/m2` | Vegetation canopy projected leaf area index |
| `vegc` | 8 | `.annout` (annual index 636) | `kgC/m2` | Total vegetation carbon stock |
| `soilc` | 9 | `.annout` (annual index 638) | `kgC/m2` | Soil organic carbon stock (4 CENTURY-style SOM pools) |
| `soilw` | 10 | `.dayout` (binary index 20) | `kgH2O/m2` | Single-bucket soil water content |
| `net_nmin` | 11 | `.dayout` | `kgN/m2/d` | Soil daily net nitrogen mineralization |
Other dag outputs, in dag terms: `daily_npp`, `daily_nee`, `daily_nep`, `daily_hr`, `proj_lai`, `vegc`, `soilc`, `soilw`, `net_nmin`, `ET`.
## 7. Tool Inventory
| Tool | Script Path | Purpose |
|------|------------|---------|
| `generate_site_ini` | `tools/generate_site_ini.py` | Generate .ini file from structured inputs (keyword-section format) |
| `select_ecophysiology` | `tools/select_ecophysiology.py` | Map AVHRR land cover to PFT and generate .epc file |
| `convert_forcing_to_bgc` | `tools/convert_forcing_to_bgc.py` | Build the BGC met file straight from the source: `--source cmfd\|mswx\|nasa_power` (shared loader, at `--lat/--lon`) or `--source fluxnet` (tower file); mm->cm, VPD in Pa from humidity + source pressure, daylen, daylight-average shortwave, 365-day years; refuses gaps/NaN |
| `run_bgc_spinup` | `tools/run_bgc_spinup.py` | Execute spinup with -u flag, monitor convergence |
| `run_bgc` | `tools/run_bgc.py` | Execute normal simulation with -m -a flags |
| `parse_bgc_output` | `tools/parse_bgc_output.py` | Parse ASCII output, compute annual C budgets, physical checks |
## Shared Library
`lib/bgc_utils.py` provides:
- `compute_daylength(lat, yday)` -- astronomical day length in seconds
- `compute_vpd_from_tmin_tmax_q(tmin, tmax, q, pressure)` -- VPD in Pa
- `compute_tday(tmin, tmax)` -- daytime average temperature
- `mm_to_cm()`, `kpa_to_pa()`, `kelvin_to_celsius()` -- unit converters
- `EPC_DEFAULTS` -- complete parameter library for 7 PFTs
- `avhrr_to_pft(avhrr_class, latitude)` -- land cover mapping
- `STANDARD_DAILY_OUTPUT`, `STANDARD_ANNUAL_OUTPUT` -- output variable sets
## 8. Unit Conversion Table
**Critical**: this table documents the unit conversions the KI pipeline must preserve. The model-facing input units are sourced from `dag.yaml` / `docs/format_spec.yaml`, and the silent failure modes are cross-checked against `diagnostics/triplets.yaml`.
| Variable | Source unit | Model unit | Factor / operation | Type |
|----------|-------------|------------|--------------------|------|
| Precipitation (`prcp`) | `mm/day` | `cm/day` | divide by 10 | multiplicative |
| Temperature (`Tmax`, `Tmin`) | `K` when source is Kelvin | `deg C` | subtract 273.15 | additive |
| Daytime temperature (`Tday`) | `deg C` computed from Tmax/Tmin | `deg C` | `Tmin + 0.45*(Tmax-Tmin)` | derived |
| VPD | `kPa` | `Pa` | multiply by 1000 | multiplicative |
| Shortwave radiation (`srad`) | `W/m2` 24-h mean | `W/m2` daylight average | multiply by `86400 / daylen` (dt_027; `--srad_is_daylight_avg` only for MTCLIM-style input) | multiplicative, day-varying |
| Day length (`daylen`) | latitude + day-of-year | `s` | compute with `compute_daylength()` | derived |
| CO2 | `ppm` | `ppm` | no conversion; do not use mole fraction or ppb | identity |
| N deposition | `kgN/m2/yr` | `kgN/m2/yr` | no conversion | identity |
| Soil depth | `cm` if source provides centimeters | `m` | divide by 100 | multiplicative |
| Soil texture | `%` | `%` | sand + silt + clay must sum to 100 | validation |
| GPP/NPP/NEE/NEP/HR outputs | `kgC/m2/d` | `kgC/m2/d` | no conversion for dag variables | identity |
| `ET` output | `kgH2O/m2/d` | `kgH2O/m2/d` | no conversion for dag variable | identity |
### 8c. Sign Conventions and Output Units
| Variable | Convention in this model | Common alternative | Impact if wrong |
|----------|--------------------------|--------------------|-----------------|
| `daily_gpp` | positive carbon uptake through photosynthesis, `kgC/m2/d` | negative uptake convention in some flux datasets | sign-flipped GPP skill and carbon budget |
| `daily_nee` | negative = sink, `kgC/m2/d` | positive uptake convention | source/sink verdict reverses |
| `daily_nep` | NPP - heterotrophic respiration, `kgC/m2/d` | NEE-style atmospheric exchange sign | NEP and NEE are conflated |
| `ET` | diagnostic evapotranspiration, `kgH2O/m2/d` | hydrology-owned ET in VIC | double-counting ET in water balance |
| `soilw` | single-bucket soil water content, `kgH2O/m2` | multi-layer VIC soil moisture | false layer-by-layer comparison |
**Output unit verification checklist:**
- Read `dag.yaml` before deciding what a variable means.
- Print the first 10 parsed values and check order of magnitude.
- For `daily_gpp`, verify units remain `kgC/m2/d` before comparing against tower or gridded GPP products.
- For `ET`, treat BIOME-BGC ET as diagnostic when VIC owns hydrology.
- For carbon exchange, confirm sign convention before computing bias metrics.
## EPC Parameter Library
7 PFTs with complete parameter sets in `lib/bgc_utils.py`:
| PFT | Ecosystem | SLA (m2/kgC) | Leaf C:N | Stomatal Cond (m/s) |
|-----|-----------|-------------|----------|-------------------|
| ENF | Evergreen Needleleaf Forest | 12 | 42 | 0.003 |
| EBF | Evergreen Broadleaf Forest | 12 | 42 | 0.005 |
| DNF | Deciduous Needleleaf Forest | 30 | 24 | 0.005 |
| DBF | Deciduous Broadleaf Forest | 30 | 24 | 0.005 |
| SHRUB | Evergreen Shrub | 12 | 42 | 0.003 |
| C3GRASS | C3 Grassland | 45 | 24 | 0.005 |
| C4GRASS | C4 Grassland | 45 | 24 | 0.005 |
## 9. Diagnostic Triplets (Top 5)
The full corpus is `diagnostics/triplets.yaml`; check it before debugging. These are the most likely silent or fatal failures for BIOME-BGC runs:
| # | Error | Diagnosis | Remedy |
|---|-------|-----------|--------|
| 1 | `dt_007`: GPP and NPP approximately 10x higher than expected for the biome | Precipitation provided in `mm/day` instead of `cm/day` | Divide precipitation by 10 when converting from mm to cm; use `convert_forcing_to_bgc.py`. |
| 2 | `dt_008`: stomata always fully open, transpiration unrealistically high (`>10 mm/day`) | VPD provided in `kPa` instead of `Pa` | Multiply VPD by 1000 to convert kPa to Pa. |
| 3 | `dt_009`: GPP=0 every day but LAI grows normally from initial conditions | Day length column is 0, missing, or extremely small | Compute day length from latitude and day-of-year using the astronomical formula. |
| 4 | `dt_004`: model completes but fluxes have wrong magnitude or sign | Extra blank line or missing keyword shifts `.ini` parameters by one line | Regenerate `.ini` with `generate_site_ini.py`; never hand-edit. |
| 5 | `dt_012`: spinup never converges, SOC keeps increasing after 6000 model years | N deposition set to modern value instead of pre-industrial for spinup | Use pre-industrial N deposition (`0.0001-0.0002 kgN/m2/yr`) for spinup. |
## Critical Domain Knowledge
### 1. Precipitation in cm, NOT mm (dt_007)
BIOME-BGC expects precipitation in **cm/day**. CMFD/MSWX/NASA POWER/FLUXNET provide mm. Forgetting to divide by 10 gives 10x too much water. The model runs fine -- GPP/NPP will just be 10x too high. **This is the #1 silent error.**
### 2. VPD in Pa, NOT kPa (dt_008)
VPD must be in **Pascals** (typical: 200-4000 Pa). If provided in kPa (0.2-4.0), stomata stay fully open, transpiration is unrealistic. No error message.
### 3. Day length must be computed (dt_009)
Day length in seconds is NOT available from CMFD/MSWX. It must be computed from latitude and day-of-year using the astronomical formula in `bgc_utils.compute_daylength()`. If day length is 0, GPP will be 0.
### 3b. Shortwave is the DAYLIGHT average, not the 24-h mean (dt_027)
Met column 8 is `metv.swavgfd`, "daylight avg shortwave flux density" (`bgc_struct.h`, users guide met-file item 8; MTCLIM writes exactly that — the bundled Missoula file has ~470 W/m² in July). Every daily forcing product (CMFD/MSWX/NASA POWER/FLUXNET `SW_IN_F`) is a 24-h mean. Feeding it unchanged delivers only daylen/86400 of the day's energy (≈0.65 in June, ≈0.3 in December at 55°N) and biases GPP/LAI/ET low by 30-60% with the seasonal timing intact. `convert_forcing_to_bgc.py` now scales by `86400/daylen`; pass `--srad_is_daylight_avg` only for MTCLIM-style input. (VPD column 7 is likewise the daylight average; FLUXNET `VPD_F` is a 24-h mean and is currently used as-is — smaller, opposite-sign effect.)
### 4. INI file is keyword-section based (dt_004)
The .ini file uses KEYWORD markers (MET_INPUT, RESTART, TIME_DEFINE, etc.) followed by sequential values read by `scan_value()`. An extra or missing line shifts ALL subsequent parameters. **Never hand-edit .ini files. Always use generate_site_ini.py.**
### 5. Spinup is mandatory (dt_012-dt_016)
BIOME-BGC REQUIRES multi-century spinup to initialize soil C/N pools. Without spinup, NEE is dominated by initial condition artifacts. Typical spinup: 1000-6000 model years (1-5 minutes wall time). The model recycles met data automatically.
Spinup durations by biome:
- Grassland: 1000-1500 years
- Deciduous forest: 2000-3000 years
- Evergreen forest: 3000-6000 years
- Boreal forest: 4000-6000 years
### 6. Double-counting ET with VIC (dt_020)
Both VIC and BIOME-BGC compute ET. When coupled, VIC owns the water balance. BIOME-BGC ET is **diagnostic only** -- do NOT add it to discharge calculations.
### 7. Forest vs Crop domains (dt_021)
BIOME-BGC is for AVHRR classes 1-10 (natural vegetation) only. Cropland (class 11) must use LDNDC or DSSAT. Running BIOME-BGC on cropland produces nonsensical results.
### 8. File paths in the .ini are length-limited — use SHORT RELATIVE paths (dt_026)
The binary copies every .ini file name into a fixed C buffer with an unbounded `fscanf("%s")`: the output prefix into `char outprefix[100]` (`pointbgc_struct.h`), met/epc/restart/co2 names into `char name[128]` (`ini.h`). An output prefix of 100+ characters (any deep absolute path) overflows **silently**: the model prints `Opened binary daily output file in write mode`, exits 0, and writes **no output files**. Always pass a short prefix relative to the run directory (e.g. `--output_prefix outputs/normal`) and run the model with `run_bgc.py --workdir <run dir>`; `generate_site_ini.py` now rejects over-long paths and `run_bgc.py` now fails when a run produced no output. The spinup restart file is exactly `sizeof(restart_data_struct)` = 584 bytes in this build (same as the bundled `restart/enf_test1.endpoint`) — a 584-byte endpoint is complete, not truncated.
## Running a Simulation
### Quick workflow (point mode):
```bash
BGC=KISSPATH_BINARIES/biome-bgc/bgc-src/bgc
TOOLS=KISSPATH_KI_ROOT/BIOME_BGC/knowledge_infrastructure/tools
# 1. Select PFT and generate .epc
python $TOOLS/select_ecophysiology.py --pft ENF --output epc/site.epc
# 2. Build the met file straight from the weather source (no default source;
# cmfd/mswx: add --forcing_dir <store>; flux tower: --source fluxnet --forcing_file <FULLSET_DD.csv>)
python $TOOLS/convert_forcing_to_bgc.py \
--source nasa_power --lat 46.8 --lon -114.0 \
--start_year 2000 --end_year 2010 \
--output metdata/site.mtc43
cat metdata/site.mtc43.summary.json # mean annual precipitation / temperature: must fit the site
# 3. Generate spinup .ini
python $TOOLS/generate_site_ini.py \
--met_file metdata/site.mtc43 --epc_file epc/site.epc \
--output_prefix outputs/spinup --lat 46.8 --elevation 977 \
--soil_depth 1.0 --sand 30 --silt 50 --clay 20 \
--start_year 2000 --n_met_years 11 --mode spinup \
--output spinup.ini
# 4. Run spinup (1-5 minutes, DO NOT interrupt)
python $TOOLS/run_bgc_spinup.py --bgc_binary $BGC --ini_file spinup.ini
# 5. Generate normal .ini (reads restart from spinup)
python $TOOLS/generate_site_ini.py \
--met_file metdata/site.mtc43 --epc_file epc/site.epc \
--output_prefix outputs/normal --lat 46.8 --elevation 977 \
--soil_depth 1.0 --sand 30 --silt 50 --clay 20 \
--start_year 2000 --n_met_years 11 --mode normal \
--read_restart --output normal.ini
# 6. Run normal simulation
python $TOOLS/run_bgc.py --bgc_binary $BGC --ini_file normal.ini
# 7. Parse output
python $TOOLS/parse_bgc_output.py \
--daily_file outputs/normal.dayout.ascii \
--start_year 2000 --n_years 11 \
--output_csv outputs/annual_summary.csv
```
### Bundled example test:
```bash
cd KISSPATH_BINARIES/biome-bgc/bgc-src
# Spinup (writes restart/enf_test1.endpoint)
./bgc -u -s ini/enf_test1_spinup.ini
# Normal run (reads restart, writes outputs)
./bgc -m -a ini/enf_test1.ini
```
Expected results for Missoula ENF (44 years, 1950-1993):
- GPP: 800-1500 gC/m2/yr
- NEE: -50 to -300 gC/m2/yr (sink)
- Max LAI: 3-6 m2/m2
- Soil C: 5-15 kgC/m2
## Error Handling
See `diagnostics/triplets.yaml` for 33 diagnostic triplets covering:
- Unit conversion errors (dt_005-dt_010) -- **7 silent errors**, most dangerous
- INI format errors (dt_002-dt_004, dt_011)
- Spinup convergence issues (dt_012-dt_016)
- Coupling pitfalls (dt_020-dt_025)
- Weather-source route (dt_028-dt_033): no VIC forcing files, `--source` required, source pressure for VPD, gaps/NaN refused, MSWX read one file at a time
- Parameter selection (dt_017-dt_019)
## 10. Coupling Interfaces
### Inbound (other models -> BIOME-BGC)
- none for weather: the met file is built from the data source itself (CMFD / MSWX / NASA POWER through the shared loader, or a FLUXNET tower file) by `convert_forcing_to_bgc.py`, never from another model's input files
- **HWSD soil** -> sand/silt/clay/depth in .ini SITE section
- **AVHRR land cover** -> `select_ecophysiology.py` -> PFT + .epc
### Outbound (BIOME-BGC -> other models)
- **Cell-level GPP/NPP/NEE/SOC** -> aggregate with LDNDC for basin C budget
- **LAI time series** -> optional update to VIC vegetation parameters
- **GPP spatial field** -> compare with MODIS MOD17A2
## 11. Validated Results
### Test Site: Missoula ENF Bundled Example
| Property | Value |
|----------|-------|
| Location | Missoula MT, 46.8N |
| Elevation | 977 m |
| Period | 1950-1993 |
| Duration | 44 years |
| PFT | ENF |
| CO2 | 294.8 ppm |
| Validation status | binary_only sanity check; field pass/fail must use `docs/validation_convention.yaml` |
### Performance Metrics -- judged against the field's bar, not intuition
**Source: `docs/validation_convention.yaml`. A metric value without the field's pass-band is not a verdict. Null convention bands are written as `no cited threshold`; do not substitute guesses.**
Headline variable from `dag.yaml`: `daily_gpp` (`kgC/m2/d`), Gross primary production (sunlit + shaded canopy photosynthesis).
| Dag variable | Obs shape | Metric | Direction | Bar (convention, cited) |
|--------------|-----------|--------|-----------|--------------------------|
| `daily_gpp` | point_time_series | `nse` | maximize | satisfactory >= 0.5 (`hamm2018`) |
| `daily_gpp` | spatial_time_series | `r2` | maximize | satisfactory >= 0.5, good >= 0.7, very_good >= 0.8 (`stocker2020`) |
| `daily_gpp` | spatial_time_series | `pbias` | zero_centered | satisfactory absolute PBIAS <= 20% (`turner2006`) |
| `daily_npp` | point_time_series | `nrmse` | minimize | very_good <= 0.13, good <= 0.26, satisfactory <= 0.53 (`turner2006`) |
Other convention examples that have null bands in `docs/validation_convention.yaml` must be reported as `no cited threshold`, for example `daily_nep` point-time-series `nse`, `daily_hr` point-time-series `nse`, `vegc` `pbias`, `soilc` `pbias`, and `net_nmin` `nse`.
### Data Replacement Tracking
| Component | Source | Status | Notes |
|-----------|--------|--------|-------|
| Forcing | CMFD / MSWX / NASA POWER / FLUXNET tower | Available | `tools/convert_forcing_to_bgc.py --source cmfd\|mswx\|nasa_power --lat --lon ...` (the tool calls the shared loader itself) or `--source fluxnet --forcing_file`. |
| Soil | HWSD or supplied soil database | Available | Use `lookup_hwsd`; sand/silt/clay must sum to 100. |
| Land cover | AVHRR or explicit PFT | Available | BIOME-BGC is for natural vegetation classes 1-10. |
| Initial conditions | Spinup endpoint | Required | Multi-century spinup is mandatory for realistic NEE. |
| Validation convention | `docs/validation_convention.yaml` | Available | Per-variable metrics, directions, pass-bands, and citations. |
## 12. Parameter Selection by Region
Use these as physically informed starting points when no site-specific calibration exists; do not treat them as calibration results.
| Climate / Region | Key parameters | Rationale |
|---|---|---|
| Evergreen needleleaf forest | ENF PFT defaults; SLA 12 m2/kgC; leaf C:N 42; stomatal conductance 0.003 m/s | Matches bundled Missoula ENF example and cold/temperate forest use. |
| Deciduous broadleaf forest | DBF PFT defaults; SLA 30 m2/kgC; leaf C:N 24; stomatal conductance 0.005 m/s | Higher SLA and lower leaf C:N than ENF reflect deciduous canopy traits. |
| Grassland | C3GRASS or C4GRASS PFT defaults; SLA 45 m2/kgC; leaf C:N 24; stomatal conductance 0.005 m/s | Use C3/C4 choice by climate/ecoregion; spinup is shorter than forest. |
| Tropical natural vegetation | Prefer EBF for evergreen broadleaf tropical forest rather than temperate DBF/ENF defaults | Avoid false temperate phenology behavior and underpredicted tropical GPP. |
---
*Built using the Knowledge Dissection Toolkit v1.0 (Zhang et al., Nature, under review).*
*Part of the HydroCraft multi-model simulation platform by the Jianyun Zhang Research Group, Hohai University.*