> **MANDATORY EXECUTION POLICY** — READ BEFORE PROCEEDING >
Scanned 9/11/2026
Install to Claude Code
npx -y skills add lzwei196/KISS---Knowledge-Infrastructure-for-Scientific-Simulation --skill APSIM --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of APSIM?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/lzwei196-apsim)More formats (shields.io, HTML) on the badges page.
> **MANDATORY EXECUTION POLICY** — READ BEFORE PROCEEDING
>
<!-- 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/` (5 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` (8 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` (20 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` (18 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-17 from the KI's actual contents — 10 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/build_apsimx.py` | `KISSPATH_PYTHON_ENV/bin/python {KI}/tools/build_apsimx.py --help` |
| `tools/convert_met.py` | `KISSPATH_PYTHON_ENV/bin/python {KI}/tools/convert_met.py --help` |
| `tools/convert_soil.py` | `KISSPATH_PYTHON_ENV/bin/python {KI}/tools/convert_soil.py --help` |
| `tools/parse_output.py` | `KISSPATH_PYTHON_ENV/bin/python {KI}/tools/parse_output.py --help` |
| `tools/run_apsim.py` | `KISSPATH_PYTHON_ENV/bin/python {KI}/tools/run_apsim.py --help` |
*5 public tools; `_`-prefixed helpers and packaging files excluded.*
<!-- KI-TOOL-INDEX:END -->
## 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 CMFD unit documentation and known traps.
See `data_ki/HWSD/SKILL.md` for soil property documentation.
See `data_ki/FAOSTAT/SKILL.md` for crop yield observations.
See `data_ki/SPAM/SKILL.md` for gridded yield data.
> 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.
# APSIM Next Generation — Knowledge Infrastructure Skill Document
## 1. Quick Overview
APSIM (Agricultural Production Systems sIMulator) is a process-based crop and farming
systems simulator developed by the APSIM Initiative (Australia). It models crop growth,
soil water balance, nitrogen cycling, and management operations at field/point scale
with a daily timestep. APSIM Next Generation (ApsimX) is the C#/.NET 8.0 rewrite of
the classic Fortran APSIM, using JSON-based simulation files (.apsimx) and SQLite
output databases.
| Attribute | Value |
|--------------------|------------------------------------------------|
| **Language** | C# (.NET 8.0) |
| **Build system** | dotnet (MSBuild, ApsimX.sln) |
| **Input format** | JSON (.apsimx) + custom text weather (.met) |
| **Output format** | SQLite (.db), optional CSV export |
| **Timestep** | Daily |
| **Spatial scale** | Point / field (Zone-based, area in ha) |
| **Key crops** | Wheat, Maize, Canola, Sorghum, Barley, Soybean, Peanut, Sugarcane, Rice, Oats, Chickpea, Mungbean, and 30+ others |
| **Repository** | https://github.com/APSIMInitiative/ApsimX |
| **License** | MIT-like (see LICENSE.md) |
| **CLI binary** | `apsim` (assembly name from APSIM.Cli) |
## 2. Installation
### 2.1 From Source (Linux)
```bash
# Prerequisites: .NET 8.0 SDK, libsqlite3-dev
sudo apt-get install -y dotnet-sdk-8.0 libsqlite3-dev
# Clone and build
git clone https://github.com/APSIMInitiative/ApsimX.git
cd ApsimX
dotnet build -c Release -f net8.0
# The CLI binary is at:
# APSIM.Cli/bin/Release/net8.0/apsim
```
### 2.2 Docker
```bash
docker build -f Dockerfiles/release-dockerfile -t apsim .
docker run --rm -v $(pwd)/data:/data apsim run /data/simulation.apsimx
```
### 2.3 Pre-built Installers
Download from https://www.apsim.info/download-apsim/ for Windows/macOS/Linux.
## 3. Pipeline Stages
The APSIM modelling pipeline has seven stages:
| Stage | Name | Description | Tool |
|-------|---------------------|----------------------------------------------------------|-------------------------|
| S0 | Configuration | Define crop type, site, simulation period, management | — |
| S1 | Domain Setup | Select soil profile, crop cultivar, zone properties | `convert_soil.py` |
| S2 | Data Preparation | Convert weather forcing to .met format | `convert_met.py` |
| S3 | Simulation Assembly | Build .apsimx JSON file from components | `build_apsimx.py` |
| S4 | Execution | Run APSIM via CLI | `run_apsim.py` |
| S5 | Output Parsing | Extract results from SQLite .db to CSV | `parse_output.py` |
| S6 | Validation | Compare simulated vs observed, compute metrics | (manual / scripts) |
## 4. Critical Domain Knowledge
### 4.1 Weather (.met) File Format — UNIT TRAPS
The .met file is a custom APSIM text format. **Critical units:**
| Column | Required | Units | Common Source Units | Conversion |
|----------|----------|----------------|------------------------|-----------------------------|
| `year` | YES | integer year | — | — |
| `day` | YES | day-of-year | date string | Convert date → DOY (1-366) |
| `radn` | YES | MJ/m^2 | W/m² (CMFD/ERA5) | W/m² × 0.0864 = MJ/m²/day |
| `maxt` | YES | °C | K (ERA5/CMFD) | K − 273.15 = °C |
| `mint` | YES | °C | K | K − 273.15 = °C |
| `rain` | YES | mm/day | mm/3hr (CMFD) | Sum 8 intervals per day |
| `pan` | optional | mm/day | — | — |
| `vp` | optional | hPa | kPa (ERA5) | kPa × 10 = hPa |
| `wind` | optional | m/s | — | — |
| `co2` | optional | ppm | — | — |
**CRITICAL: Radiation must be MJ/m²/day, NOT W/m².** Supplying W/m² (typically 100-400)
instead of MJ/m² (typically 5-30) will cause massively inflated biomass production.
This is the #1 unit trap. See diagnostic triplet dt_001.
**CRITICAL: The header line must use parenthesized units exactly as APSIM expects:**
```
year day radn maxt mint rain pan vp code
() () (MJ/m^2) (oC) (oC) (mm) (mm) (hPa) ()
```
**CRITICAL: `tav` and `amp` in the header are REQUIRED metadata.**
- `tav` = annual average ambient temperature (°C)
- `amp` = annual amplitude in mean monthly temperature (°C)
- These drive the soil temperature model. If missing, APSIM crashes silently or
produces unrealistic soil temperatures. See dt_002.
### 4.2 Soil Parameters — Layer-Based Arrays
APSIM soils are specified as arrays indexed by layer. All layers must have the
same number of elements (same number of layers).
| Parameter | Units | Description | Typical Range |
|------------------|---------|---------------------------------|----------------------|
| `Thickness` | mm | Layer depth | 100–300 |
| `BD` | g/cc | Bulk density | 1.0–1.8 |
| `AirDry` | mm/mm | Air-dry water content | 0.01–0.15 |
| `LL15` | mm/mm | Lower limit (wilting point) | 0.05–0.25 |
| `DUL` | mm/mm | Drained upper limit (field cap) | 0.15–0.45 |
| `SAT` | mm/mm | Saturation | 0.30–0.55 |
| `KS` | mm/day | Saturated hydraulic conductivity| 1–500 |
**CRITICAL ordering: AirDry ≤ LL15 ≤ DUL ≤ SAT.** Violation crashes the water
balance model. See dt_003.
**Crop-specific parameters (SoilCrop):**
| Parameter | Units | Description | Typical Range |
|-----------|--------------|------------------------------------|---------------|
| `LL` | mm/mm | Crop lower limit (extraction limit)| ≥ LL15 |
| `KL` | /day | Root water uptake rate | 0.01–0.10 |
| `XF` | dimensionless| Root exploration factor | 0.0–1.0 |
**Water balance parameters (SoilWater / WaterBalance):**
| Parameter | Units | Description |
|---------------|-------|------------------------------------------|
| `SummerU` | mm | Stage 1 evaporation limit (summer) |
| `SummerCona` | mm/d^0.5 | Stage 2 evaporation coefficient (summer) |
| `WinterU` | mm | Stage 1 evaporation limit (winter) |
| `WinterCona` | mm/d^0.5 | Stage 2 evaporation coefficient (winter) |
| `Salb` | 0-1 | Bare soil albedo |
| `CN2Bare` | — | SCS curve number (bare soil) |
| `SWCON` | 0-1 | Soil water conductivity (per layer) |
| `DiffusConst` | — | Diffusivity constant |
| `DiffusSlope` | — | Diffusivity slope |
### 4.3 Simulation JSON Structure (.apsimx)
The .apsimx file is a nested JSON tree. Each node has `$type`, `Name`, and `Children`:
```
Simulations
├── DataStore ($type: Models.Storage.DataStore)
└── Simulation ($type: Models.Core.Simulation)
├── Clock (Start, End dates)
├── Summary (logging)
├── Weather (FileName → .met path)
├── MicroClimate (light interception)
└── Zone (Area in ha)
├── Soil (Physical, Chemical, Organic, WaterBalance, SoilCrop)
├── Plant / Crop (cultivar, phenology, organs)
├── Manager (sowing rules, fertilizer, irrigation)
└── Report (VariableNames[], EventNames[])
```
**CRITICAL: `%root%` in file paths is replaced with the directory containing the
.apsimx file at runtime.** Relative paths resolve from this root. See dt_004.
### 4.4 Crop Sowing Parameters
Sowing is triggered by a Manager script calling `[Crop].Sow(...)`:
| Parameter | Units | Description |
|-----------------|---------|--------------------------------------|
| `Population` | /m² | Plant population density |
| `Depth` | mm | Sowing depth |
| `Cultivar` | string | Cultivar name (must match available) |
| `RowSpacing` | mm | Row spacing |
| `MaxCover` | 0-1 | Maximum canopy cover |
| `BudNumber` | integer | Number of buds (tuber crops) |
**CRITICAL: Population is plants per m², NOT per hectare.** 100 plants/m² = 1,000,000
plants/ha. Supplying 100,000 (intended as plants/ha) as plants/m² gives 10^9 plants/ha.
See dt_005.
### 4.5 Key Output Variables
Variables are addressed by model path in Report definitions:
| Variable Path | Units | Description |
|------------------------------------|--------|---------------------------------|
| `[Clock].Today` | date | Simulation date |
| `[Wheat].Grain.Wt` | g/m² | Grain dry weight |
| `[Wheat].AboveGround.Wt` | g/m² | Above-ground biomass |
| `[Wheat].Leaf.LAI` | m²/m² | Leaf area index |
| `[Wheat].Phenology.Stage` | code | Phenological stage number |
| `[Wheat].Phenology.CurrentPhaseName`| string | Phase name |
| `[Wheat].Root.RootingDepth` | mm | Rooting depth |
| `[Soil].Water.SW` | mm/mm | Volumetric soil water (array) |
| `[Soil].Water.ESW` | mm | Extractable soil water (array) |
| `[Soil].SoilWater.Runoff` | mm | Surface runoff |
| `[Soil].SoilWater.Drainage` | mm | Deep drainage |
| `[Soil].SoilWater.Es` | mm | Soil evaporation |
| `[Weather].Rain` | mm | Daily rainfall |
| `[Weather].MaxT` | °C | Maximum temperature |
| `[Weather].MinT` | °C | Minimum temperature |
| `[Weather].Radn` | MJ/m² | Solar radiation |
**CRITICAL: Biomass outputs (Wt) are in g/m², NOT kg/ha.** To convert:
kg/ha = g/m² × 10. Grain yield of 300 g/m² = 3000 kg/ha = 3.0 t/ha. See dt_006.
## 6. Output Description
**Source of truth: `dag.yaml`.** The dag defines what this KI predicts. If this
section and `dag.yaml` disagree, `dag.yaml` wins.
**Headline output** (the dag's `validation_rank: 1` variable):
> `Grain.Wt` — Crop grain dry weight (yield) in the plant/crop biomass medium; g/m^2 x10 = kg/ha, /100 = t/ha. (`g/m^2`)
| Output variable (dag `var`) | Rank | Unit | Description / dag status |
|-----------------------------|------|------|--------------------------|
| `Grain.Wt` | 1 | `g/m^2` | Crop grain dry weight (yield) in the plant/crop biomass medium; g/m^2 x10 = kg/ha, /100 = t/ha. |
| `AboveGround.Wt` | dag output | see `dag.yaml` | Listed by the dag as an APSIM output. |
| `Leaf.LAI` | dag output | see `dag.yaml` | Listed by the dag as an APSIM output. |
| `Phenology.Stage / flowering & maturity date` | dag output | see `dag.yaml` | Listed by the dag as an APSIM output. |
| `Root.RootingDepth` | dag output | see `dag.yaml` | Listed by the dag as an APSIM output. |
| `Soil.Water.SW / ESW` | dag output | see `dag.yaml` | Listed by the dag as an APSIM output. |
| `SoilWater.Drainage / Runoff / Es` | dag output | see `dag.yaml` | Listed by the dag as an APSIM output. |
### 4.6 Fertiliser Application
Fertiliser is applied via Manager script or Operations list:
| Parameter | Units | Description |
|-----------|--------|------------------------------------------|
| `Amount` | kg/ha | Application amount |
| `Depth` | mm | Application depth in soil |
| `Type` | enum | NO3N, NH4N, UreaN, etc. |
### 4.7 Irrigation
| Parameter | Units | Description |
|-----------|-------|--------------------------------------------|
| `Amount` | mm | Irrigation depth |
| `Depth` | mm | Depth of application in soil |
| `Duration`| min | Duration of irrigation event |
| `Efficiency`| 0-1 | Irrigation efficiency |
### 4.8 Phenology and Thermal Time
APSIM crops use thermal time (degree-days) to drive phenological development:
```
ThermalTime = max(0, (Tmax + Tmin)/2 - Tbase)
```
Where Tbase varies by crop (e.g., Wheat ~0°C, Maize ~8°C, Sorghum ~11°C).
Key phenological stages (wheat example):
1. Sowing → Germination
2. Germination → Emergence
3. Emergence → Terminal Spikelet
4. Terminal Spikelet → Flowering
5. Flowering → Start Grain Fill
6. Start Grain Fill → End Grain Fill
7. End Grain Fill → Maturity
8. Maturity → Harvest Ripe
### 4.9 Canopy Cover Calculation
```
CoverGreen = 1 - exp(-ExtinctionCoeff × LAI / MaxCover)
CoverTotal = 1 - (1 - CoverGreen) × (1 - CoverDead)
```
## 5. Unit Trap Table
This table documents the most dangerous unit mismatches when preparing APSIM inputs
from global datasets (ERA5, CMFD, MSWX, SoilGrids, HWSD):
| ID | Variable | APSIM Unit | Common Source | Source Unit | Conversion Factor | Severity |
|-----|-------------|--------------|---------------|-----------------|-------------------------------|----------|
| U01 | Radiation | MJ/m²/day | ERA5/CMFD | W/m² (instant) | × 0.0864 (÷ 11.574) | FATAL |
| U02 | Temperature | °C | ERA5 | K | − 273.15 | FATAL |
| U03 | Vapor press | hPa | ERA5 | kPa | × 10 | degraded |
| U04 | Rainfall | mm/day | CMFD | mm/3hr | Sum 8 intervals | FATAL |
| U05 | Biomass out | g/m² | Literature | kg/ha | ÷ 10 (APSIM→lit) or × 10 | silent |
| U06 | Population | plants/m² | Agronomic | plants/ha | ÷ 10000 | FATAL |
| U07 | Soil water | mm/mm (vol) | SoilGrids | % (v/v) | ÷ 100 | FATAL |
| U08 | Thickness | mm | HWSD | cm | × 10 | FATAL |
| U09 | Bulk density| g/cc | SoilGrids | kg/m³ | ÷ 1000 | FATAL |
| U10 | KS | mm/day | Literature | cm/hr | × 240 | degraded |
| U11 | Row spacing | mm | Agronomic | cm | × 10 | degraded |
| U12 | Sowing depth| mm | Agronomic | cm | × 10 | degraded |
| U13 | Root depth | mm | Literature | cm or m | × 10 or × 1000 | silent |
| U14 | CO2 | ppm | — | µmol/mol | 1:1 (same) | none |
| U15 | Wind speed | m/s | ERA5 | m/s | 1:1 (check u/v components) | degraded |
## 8. Unit Conversion Table
This unit table restates the KI's pipeline unit conversions and the dag's headline
output conversion. It is the quick-check table; detailed I/O shapes remain in
`docs/format_spec.yaml`, and the headline output identity remains in `dag.yaml`.
| Variable | Source unit / common source | Model or reported unit | Conversion | Type |
|----------|-----------------------------|------------------------|------------|------|
| Radiation (`radn`) | W/m² (ERA5/CMFD instant) | MJ/m²/day | × 0.0864 (÷ 11.574) | multiplicative |
| Temperature (`maxt`, `mint`) | K (ERA5/CMFD) | °C | − 273.15 | additive |
| Vapor pressure (`vp`) | kPa (ERA5) | hPa | × 10 | multiplicative |
| Rainfall (`rain`) | mm/3hr (CMFD) | mm/day | Sum 8 intervals | aggregation |
| `Grain.Wt` | `g/m^2` | kg/ha | x10 | multiplicative |
| `Grain.Wt` | `g/m^2` | t/ha | /100 | multiplicative |
| Population | plants/ha | plants/m² | ÷ 10000 | multiplicative |
| Soil water | % (v/v) | mm/mm | ÷ 100 | multiplicative |
| Thickness | cm | mm | × 10 | multiplicative |
| Bulk density | kg/m³ | g/cc | ÷ 1000 | multiplicative |
| KS | cm/hr | mm/day | × 240 | multiplicative |
| Row spacing | cm | mm | × 10 | multiplicative |
| Sowing depth | cm | mm | × 10 | multiplicative |
| Root depth | cm or m | mm | × 10 or × 1000 | multiplicative |
| CO2 | µmol/mol | ppm | 1:1 (same) | identity |
| Wind speed | m/s | m/s | 1:1 (check u/v components) | identity |
## 6. Tool Reference
| Tool | Stage | Purpose |
|-----------------------|-------|------------------------------------------------|
| `convert_met.py` | S2 | Convert global forcing (NetCDF) → APSIM .met |
| `convert_soil.py` | S1 | Convert HWSD/SoilGrids → APSIM soil JSON |
| `build_apsimx.py` | S3 | Assemble .apsimx simulation file from parts |
| `run_apsim.py` | S4 | Execute APSIM CLI with preflight checks |
| `parse_output.py` | S5 | Extract SQLite .db results → CSV |
All tools follow the validate→process→validate pattern:
1. `validate_inputs()` — check files exist, units correct, ranges valid
2. `process()` — core transformation logic
3. `validate_outputs()` — verify output quality and physical constraints
## 7. Execution
### 7.1 Basic Run
```bash
# Run a single simulation
apsim run Wheat.apsimx
# Run with CSV export
apsim run Wheat.apsimx --csv
# Run specific simulations by name regex
apsim run Wheat.apsimx --simulation-names "Dalby.*"
# Run single-threaded (useful for debugging)
apsim run Wheat.apsimx --single-threaded
# Verbose output
apsim run Wheat.apsimx --verbose
```
### 7.2 Edit Before Run
```bash
# Override parameters via config file
apsim run Wheat.apsimx --edit config.txt
```
Config file format (one override per line):
```
[Simulation].Clock.Start = 2000-01-01
[Simulation].Clock.End = 2005-12-31
[Simulation].Zone.Weather.FileName = /data/weather/site.met
```
### 7.3 Output Access
After running, output is in `{filename}.db` (SQLite):
```bash
sqlite3 Wheat.db "SELECT * FROM Report LIMIT 10;"
```
Or use `--csv` flag to auto-export to `{filename}.Report.csv`.
## 8. Validation Metrics for Crop Models
| Metric | Formula / Description | Grading source |
|--------|------------------------------------------------|-------------------|
| RMSE | √(mean((sim-obs)²)) | no cited threshold in the provided `Grain.Wt` convention |
| nRMSE | RMSE / mean(obs) × 100 | use Section 11 / `docs/validation_convention.yaml` |
| R² | Coefficient of determination | no cited threshold in the provided `Grain.Wt` convention |
| PBIAS | 100 × Σ(sim-obs) / Σ(obs) | choose by dag obs-shape rules; no `Grain.Wt` convention bar stated here |
| d | Willmott index of agreement | no cited threshold in the provided `Grain.Wt` convention |
| EF | Nash-Sutcliffe model efficiency (=NSE) | use Section 11 / `docs/validation_convention.yaml` |
Common validation targets:
- **Grain yield** (t/ha): Primary metric for crop models
- **Biomass** (t/ha): Total above-ground dry matter
- **Phenology** (days): Flowering date, maturity date
- **LAI** (m²/m²): Peak LAI timing and magnitude
- **Soil water** (mm): Profile soil water content over time
### 8.1 Obs-shape selection - REQUIRED before computing any metric
`dag.yaml` `outputs[Grain.Wt].observability.comparable_obs_shapes` declares THREE
mutually exclusive comparison modes. Pick by the OBSERVATION's spatial support,
not by the simulation's.
| Obs example | obs_shape | comparison_mode | determining_metric | detrending_options |
|---|---|---|---|---|
| One field trial, one season | `point_snapshot` | `scalar_comparison` | `pbias` | `none` |
| One field trial, many seasons | `point_time_series` | `time_series_comparison` | `pbias` | `none` |
| GDHY / SPAM / FAOSTAT / any gridded, district or national yield | `regional_aggregate_time_series` | `aggregate_trend_comparison` | `pbias` | `none`, `linear_residual`, `decadal_mean` |
**A 0.5-degree GDHY cell, a SPAM pixel, a district series and a national FAOSTAT
series are ALL `regional_aggregate_time_series`.** They average thousands of
fields and carry a secular technology/management trend that a
constant-management, weather-driven point simulation cannot and must not
reproduce. If `resolved_obs.granularity == "grid"`, or the obs is a statistical
aggregate of any kind, the obs_shape is `regional_aggregate_time_series` - full
stop. Declaring it `point_time_series` silently selects the wrong metric family.
### 8.2 Mandatory detrending for `regional_aggregate_time_series`
`ki_tools_common.metrics.all_metrics` alone (raw NSE / r / KGE) is **NOT** valid
for this obs shape. Also call `ki_tools_common.metrics.trend_metrics(obs, sim)`
(`metrics.py:492`), which implements the dag `trend_match` family with
`linear_residual` detrending:
python
from ki_tools_common.metrics import all_metrics, trend_metrics
m = all_metrics(obs, sim) # magnitude_accuracy: PBIAS, RMSE
m.update(trend_metrics(obs, sim)) # trend_match: r_detr, r_firstdiff, slope_ratio
| Metric | Meaning | Use |
|---|---|---|
| `pbias` | determining metric; magnitude vs the aggregate | grade only against the applicable convention bar |
| `r_detr` | interannual skill after removing each series own linear trend | trend diagnostic |
| `r_firstdiff` | year-over-year change skill (trend-free by construction) | trend diagnostic |
| `slope_ratio` | sim trend / obs trend; near 0 is EXPECTED under constant management | report, do not score |
| `r` (raw), `nse` | NOT skill metrics for this shape | report as `r_raw` for transparency only |
**Do NOT truncate the record to a "management-consistent" sub-window in place of
detrending.** Truncation is not one of the dag `detrending_options`, it discards
data, and it moves PBIAS without removing the trend it was chosen to handle. A
sub-window may be reported as an ADDITIONAL diagnostic, clearly labelled, never
as a substitute.
**Worked example (GDHY wheat, Balcarce AR, 1984-2016, n=33).** Raw `r = -0.19`
looked like an anti-correlated model. `trend_metrics` on the SAME pairs gives
`r_detr = +0.14`, `r_firstdiff = +0.33`, `slope_obs = +0.074 t/ha/yr`,
`slope_sim = -0.042 t/ha/yr`. The negative raw r was entirely an artefact of the
obs technology trend. The only real model finding is `pbias = +40.8%`, i.e. the
attainable-vs-actual yield gap of triplet dt_019 - handle that with
region-matched management (dt_019), not with a metric change.
### 8.3 Calibration / validation split
`period_calibration` and `period_validation` must be DISJOINT. Emitting the same
window twice, so that `nse_val` is bit-identical to `nse_cal`, reports no
independent information. For an uncalibrated forward run, either split the
seasons into two disjoint periods or declare a single period and omit the
`_cal` / `_val` fields.
## 11. Validated Results
No achieved calibration, validation, or full-period metric values are stated in
the provided KI facts. Grade `Grain.Wt` runs against `docs/validation_convention.yaml`;
do not substitute remembered crop-model thresholds.
### Performance Metrics — convention bars for `Grain.Wt`
Each row below restates one convention entry for `Grain.Wt`. For minimize metrics,
lower values are better; for maximize metrics, higher values are better.
| Variable | Metric | Direction | Very good band | Good band | Satisfactory band |
|----------|--------|-----------|----------------|-----------|-------------------|
| `Grain.Wt` | `nrmse` | minimize | <= 10 (`zhao2012`, `brown2018`) | <= 20 (`zhao2012`, `brown2018`) | <= 30 (`zhao2012`, `brown2018`) |
| `Grain.Wt` | `ef` | maximize | >= 1.0 (`brown2018`) | no cited threshold | >= 0.0 (`brown2018`) |
| `Grain.Wt` | `nrmse` | minimize | <= 10 (`zhao2012`, `brown2018`) | <= 20 (`zhao2012`, `brown2018`) | <= 30 (`zhao2012`, `brown2018`) |
| `Grain.Wt` | `nse` | maximize | >= 0.75 (`pasley2023`) | >= 0.65 (`pasley2023`) | >= 0.5 (`pasley2023`) |
| `Grain.Wt` | `nrmse` | minimize | <= 10 (`zhao2012`, `lu2022`) | <= 20 (`zhao2012`, `lu2022`) | <= 30 (`zhao2012`, `lu2022`) |
| `Grain.Wt` | `nse` | maximize | >= 0.75 (`pasley2023`) | >= 0.65 (`pasley2023`) | >= 0.5 (`pasley2023`) |
### Data Replacement Tracking
| Component | Source | Status | Notes |
|-----------|--------|--------|-------|
| Forcing | Pipeline | Pending validation result in this document | Use `preflight_check.py` before any run. |
| Soil | Pipeline | Pending validation result in this document | Use KI soil conversion tools and `docs/format_spec.yaml`. |
| APSIM execution | Actual APSIM binary/package | Required | Do not substitute a simplified formula or approximation. |
| Observations | Observation-binding workflow | Pending validation result in this document | Choose obs shape from the dag before computing metrics. |
## 9. File Structure
```
project/
├── simulation.apsimx # Main simulation file (JSON)
├── weather/
│ └── site.met # Weather forcing data
├── simulation.db # Output database (SQLite, auto-created)
├── simulation.db-wal # Write-ahead log (temp, auto-managed)
├── simulation.db-shm # Shared memory (temp, auto-managed)
└── simulation.Report.csv # CSV export (if --csv flag used)
```
## 10. Diagnostic Triplets Summary
The most dangerous silent errors in APSIM modelling:
1. **dt_001 — Radiation in W/m² instead of MJ/m²/day**: Biomass explodes to
unrealistic values. Tool `convert_met.py` auto-converts and validates range.
2. **dt_002 — Missing tav/amp in .met header**: Soil temperature model fails
silently, producing unrealistic soil temperatures that affect germination
and root growth.
3. **dt_003 — Soil water limits not ordered (AirDry ≤ LL15 ≤ DUL ≤ SAT)**:
Water balance crashes or produces negative water content.
4. **dt_005 — Plant population in wrong units**: Supplying plants/ha as
plants/m² gives 10,000× too many plants.
5. **dt_006 — Biomass output confusion (g/m² vs kg/ha)**: Factor of 10
error in reported yield.
6. **dt_010 — Wrong cultivar name**: Simulation crashes with unhelpful error.
Must match exactly from available cultivar list.
7. **dt_013 — .met file path not resolved**: %root% macro not properly
expanded, or relative path broken by directory change.
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!