**Package**: hydrocraft-landlab v1.0.0 **Model**: Landlab 2.10.1 (Python library with Cython extensions) **Domain**: Geomorphology, hydrology, stratigraphy, glaciology **Created by**: Landlab Development Team (CU Boulder, U Washington, Tulane) **Last updated**: 2026-04-29 **Stats**: 7 tools | 5 skill documents | 22 diagnostic triplets | ~2,200 lines of validated code **Validation status**: production_validated (5 test cases: Whipple & Tucker 1999 analytical + SPACE binary Qs-Q + SPACE steady-...
Scanned 9/11/2026
Install to Claude Code
npx -y skills add lzwei196/KISS---Knowledge-Infrastructure-for-Scientific-Simulation --skill Landlab --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Landlab?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/lzwei196-landlab)More formats (shields.io, HTML) on the badges page.
> **MANDATORY EXECUTION POLICY** — READ BEFORE PROCEEDING
>
> You MUST run the **actual model binary or package** described in this document.
> If the model fails to import, compile, or execute, you MUST:
> 1. Check `diagnostics/triplets.yaml` for a matching error pattern
> 2. Try the fix suggested in the triplet's `remedy` section
> 3. If still failing, report the error to the user with full details
>
> You MUST NOT substitute a simplified Python formula, regression equation,
> or hand-coded approximation in place of the real model.
>
>
> Before starting, run: `python preflight_check.py` (in this KI directory)
> to verify that the model binary/package and required data are available.
>
> **DEBUGGING PROTOCOL** — When something goes wrong, follow this order:
> 1. **Check triplets** — `diagnostics/triplets.yaml` may already cover this error
> 2. **Read official docs** — The model's own documentation for expected formats/units
> 3. **Find working examples** — Check `outputs/` or the model's shipped test data
> 4. **Fix the tool** — With knowledge of what "correct" looks like
>
> Do NOT write custom debug scripts. The answers are in the docs and examples.
<!-- KI-MAP:BEGIN (projected by generate_skill_map.py — edit the KI, not this table) -->
## KI map — what to read, and when
| when you need | read | why |
|---|---|---|
| FIRST, always | `preflight_check.py` | run it (`python preflight_check.py`): proves env/binary/data are usable and emits a machine-readable `PREFLIGHT_REPORT=` line. Do not debug a run that never had a healthy environment. |
| to run the pipeline stages | `tools/` (8 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` (22 entries) | symptom → diagnosis → remedy for this model's known failure modes. Check here FIRST; the answer usually exists. Never renumber or rewrite entries. |
| to know what an output IS | `dag.yaml` | the model's identity: every output's medium, units, `validation_rank` (1 = the headline variable) and observability. Scoring and obs-binding read THIS — when asked 'what does this model predict', the dag is the answer, not a guess. |
| when building inputs / parsing outputs | `docs/format_spec.yaml` | exact I/O shapes + `known_issues`, projected from dag + triplets. Regenerate with `ki_tools_common/generate_format_spec.py` after changing either — never hand-edit. |
| to judge a run's skill | `docs/validation_convention.yaml` | how this model's field judges it validated: per-`dag_variable` metrics, directions and CITED pass-bands. A run is graded against these, not against intuition. |
| for claims and thresholds | `docs/gathered_papers.json` (22 papers) + `docs/papers_index.md` | the literature this KI is judged by; each entry's `text_path` is fetched full text in the central paper cache. `role: benchmark` marks the model's own skill paper. |
| for a machine-readable summary | `knowledge_infrastructure.yaml` | the manifest (package, pipeline, validation tier, counts) — projected by `ki_tools_common/generate_ki_manifest.py`; regenerate after structural changes, never hand-edit. |
*Projected 2026-08-17 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_dem_to_grid.py` | `KISSPATH_PYTHON_ENV/bin/python {KI}/tools/convert_dem_to_grid.py --help` |
| `tools/convert_soil_params.py` | `KISSPATH_PYTHON_ENV/bin/python {KI}/tools/convert_soil_params.py --help` |
| `tools/dissect_atchafalaya_ssc_q_surrogate.py` | `KISSPATH_PYTHON_ENV/bin/python {KI}/tools/dissect_atchafalaya_ssc_q_surrogate.py --help` |
| `tools/dissect_loess_plateau_sediment_yield.py` | `KISSPATH_PYTHON_ENV/bin/python {KI}/tools/dissect_loess_plateau_sediment_yield.py --help` |
| `tools/dissect_loess_plateau_slope_area.py` | `KISSPATH_PYTHON_ENV/bin/python {KI}/tools/dissect_loess_plateau_slope_area.py --help` |
| `tools/dissect_space_ssc_q_rating.py` | `KISSPATH_PYTHON_ENV/bin/python {KI}/tools/dissect_space_ssc_q_rating.py --help` |
| `tools/parse_landlab_output.py` | `KISSPATH_PYTHON_ENV/bin/python {KI}/tools/parse_landlab_output.py --help` |
| `tools/run_landlab.py` | `KISSPATH_PYTHON_ENV/bin/python {KI}/tools/run_landlab.py --help` |
*8 public tools; `_`-prefixed helpers and packaging files excluded.*
<!-- KI-TOOL-INDEX:END -->
# Landlab v2.10 — Earth Surface Dynamics Modeling Toolkit
**Package**: hydrocraft-landlab v1.0.0
**Model**: Landlab 2.10.1 (Python library with Cython extensions)
**Domain**: Geomorphology, hydrology, stratigraphy, glaciology
**Created by**: Landlab Development Team (CU Boulder, U Washington, Tulane)
**Last updated**: 2026-04-29
**Stats**: 7 tools | 5 skill documents | 22 diagnostic triplets | ~2,200 lines of validated code
**Validation status**: production_validated (5 test cases: Whipple & Tucker 1999 analytical + SPACE binary Qs-Q + SPACE steady-state concavity + Loess Plateau SRTM real-DEM slope-area + Loess Plateau stream power SY + P export)
---
## 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 meteorological forcing documentation.
See `data_ki/USGS_Sediment/SKILL.md` for suspended sediment observations.
## Applicability
**This KI is for landscape evolution and sediment-yield benchmarks only.** It does NOT produce a daily discharge hydrograph and is NOT applicable to gauge-discharge tests (e.g. Bengbu, Wangjiaba). The `surface_water__discharge` field from `FlowAccumulator` is steady-state Q = drainage_area * uniform_runoff_rate, not a time series. Validation cases supported: slope-area, concavity, sediment yield, Whipple-Tucker analytical, SPACE Qs-Q. For streamflow benchmarks use a hydrologic KI (PIHM, mHM, VIC, etc.).
## Overview
Landlab is an open-source Python package for numerical modeling of Earth surface
dynamics. Unlike compiled, monolithic landscape evolution models, Landlab is a
**component-based framework**: users compose simulations by coupling reusable
process components (erosion, diffusion, flow routing, weathering, etc.) on a
shared model grid. The grid stores spatially distributed fields (elevation,
drainage area, soil depth) at nodes, links, patches, cells, corners, and faces.
Landlab supports six grid types: `RasterModelGrid` (uniform rectangular),
`HexModelGrid` (hexagonal cells), `VoronoiDelaunayGrid` (irregular Voronoi),
`FramedVoronoiGrid`, `RadialModelGrid`, `NetworkModelGrid`, and
`IcosphereGlobalGrid`. All grids implement the same field-storage API, so
components are grid-agnostic.
There are 60+ process components covering: flow routing (D8, D-infinity, MFD,
priority-flood), stream-power erosion, hillslope diffusion, SPACE bedrock-
alluvium erosion, overland flow (kinematic wave, de Almeida), flexural isostasy,
tectonic faulting, soil production (exponential weathering), fire, vegetation
dynamics, species evolution, tidal flow, marine sediment transport, and more.
## Installation
### From PyPI (recommended)
```bash
python -m venv venv && source venv/bin/activate
pip install landlab
```
### From source (development)
```bash
git clone https://github.com/landlab/landlab.git
cd landlab
pip install -e ".[dev,testing]"
```
### Dependencies
- **Core**: numpy>=1.20, scipy, matplotlib, xarray>=0.16, pandas, pyyaml, netcdf4, bmipy, rich-click
- **Build**: cython, setuptools, wheel (Cython extensions for performance)
- **Optional**: pyshp (shapefiles), statsmodels, gflex (flexure)
### Verify installation
```python
import landlab; print(landlab.__version__)
from landlab import RasterModelGrid
mg = RasterModelGrid((10, 10), xy_spacing=100.0)
print(f"Grid OK: {mg.number_of_nodes} nodes")
```
---
## Pipeline (7 stages)
| Stage | ID | Tool | Description |
|-------|----|------|-------------|
| 1. Grid Setup | s1_grid | — | Create model grid with DEM or synthetic topography |
| 2. Input Preparation | s2_input | `convert_dem_to_grid.py` | Load DEM, set boundary conditions, add fields |
| 3. Soil/Parameter Setup | s3_params | `convert_soil_params.py` | Map HWSD or other soil data to grid fields |
| 4. Component Assembly | s4_assembly | — | Instantiate and couple Landlab components |
| 5. Execution | s5_run | `run_landlab.py` | Time-stepping loop with run_one_step() |
| 6. Output Extraction | s6_output | `parse_landlab_output.py` | Extract fields to CSV/NetCDF, compute metrics |
| 7. Diagnostics | s7_diag | — | Validate results against analytical solutions |
### Parallelism
- Stages 2 and 3 can run in parallel (independent input preparation)
- Stage 4 depends on both 2 and 3
- Stages 5–7 are sequential
---
## Execution Model
Landlab is a **Python API**, not a CLI binary. Simulations are Python scripts:
```python
from landlab import RasterModelGrid
from landlab.components import FlowAccumulator, StreamPowerEroder, LinearDiffuser
# 1. Create grid
mg = RasterModelGrid((50, 50), xy_spacing=100.0)
z = mg.add_zeros("topographic__elevation", at="node")
z += mg.node_x * 0.01 + np.random.rand(mg.number_of_nodes)
# 2. Set boundary conditions
mg.set_closed_boundaries_at_grid_edges(True, False, True, False)
# 3. Instantiate components
fa = FlowAccumulator(mg, flow_director="FlowDirectorD8")
sp = StreamPowerEroder(mg, K_sp=1e-5, m_sp=0.5, n_sp=1.0)
ld = LinearDiffuser(mg, linear_diffusivity=0.01)
# 4. Time loop
dt = 1000.0 # years
for t in range(2000):
fa.run_one_step()
sp.run_one_step(dt)
ld.run_one_step(dt)
# 5. Output
from landlab.io import write_esri_ascii
write_esri_ascii("output.asc", mg, "topographic__elevation")
```
### BMI Interface
Components can also be driven via the Basic Modeling Interface (BMI):
```python
from landlab.bmi import wrap_as_bmi
BmiSPE = wrap_as_bmi(StreamPowerEroder)
model = BmiSPE()
model.initialize("config.yaml")
model.update()
model.finalize()
```
---
## 6. Output Description
Source of truth: `dag.yaml`. The DAG wins over this body if they ever disagree.
**Headline output** (`validation_rank: 1`):
> `sediment_yield` — Annual catchment sediment yield via detachment-limited stream power (E = K_sp A^m S^n converted by bulk density). (`t/km^2/yr`)
| Output variable (dag `var`) | Rank | Unit | Emitted in | Description |
|-----------------------------|------|------|------------|-------------|
| `sediment_yield` | 1 | `t/km^2/yr` | derived metric (detachment-limited stream-power export) | Annual catchment sediment yield via detachment-limited stream power (E = K_sp A^m S^n converted by bulk density). |
| `sediment__outflux (SPACE Qs)` | 2 | `m^3/s (or kg/s)` | Landlab grid field at_node (SPACE / ErosionDeposition) | Sediment flux out of a node; the Qs in the SPACE Qs-Q rating relationship. |
| `surface_water__discharge (steady-state)` | 3 | `m^3/s` | Landlab grid field at_node (FlowAccumulator) | Steady-state discharge Q = drainage_area * runoff_rate from FlowAccumulator; NOT a hydrograph time series. |
| `topographic__elevation` | 4 | `m` | Landlab grid field at_node; written to ESRI ASCII (.asc) / NetCDF (.nc) | Final land-surface elevation of the evolved landscape (node field). |
| `concavity (theta)` | 5 | `dimensionless` | derived metric from slope-area regression on the grid | Land-surface/channel-topography slope-area concavity index theta = -d(logS)/d(logA); at steady state expected to approach m_sp/n_sp. |
| `steepness (ks)` | 6 | `dimensionless (channel steepness index)` | derived metric from slope-area regression | Channel steepness index from the slope-area regression. |
| `relief` | 7 | `m` | derived metric over core nodes | Max-minus-min elevation over core nodes; landscape relief at steady state. |
| `soil__depth` | 8 | `m` | Landlab grid field at_node | Regolith/soil thickness above bedrock (node field). |
The other DAG outputs are: `topographic__elevation`, `concavity (theta)`, `steepness (ks)`, `relief`, `sediment__outflux (SPACE Qs)`, `soil__depth`, and `surface_water__discharge (steady-state)`.
---
## 8. Unit Table
Source of truth: `dag.yaml` and `docs/format_spec.yaml`. Landlab is unit-agnostic, so the tools and user scripts must keep the unit system consistent.
| Variable / quantity | Source unit handled by this KI | Model or DAG unit | Conversion / invariant | Where checked or used |
|---------------------|--------------------------------|-------------------|------------------------|-----------------------|
| `sediment_yield` | derived from detachment-limited stream-power export | `t/km^2/yr` | E = K_sp A^m S^n converted by bulk density | DAG rank-1 output; `tools/dissect_loess_plateau_sediment_yield.py` |
| `topographic__elevation` | DEM elevation in meters; feet are a known trap | `m` | feet to meters: multiply by 0.3048 | `convert_dem_to_grid.py`; dt_007 |
| `xy_spacing` / grid geometry | projected metric spacing required | `m` | reproject geographic degrees to a metric CRS before loading | `convert_dem_to_grid.py`; dt_004 |
| `water__unit_flux_in` | rainfall / runoff from forcing data | `m/s` | mm/hr to m/s: divide by 3,600,000; mm/day to m/s: divide by 86,400,000; m/yr to m/s: divide by 31,557,600 | `FlowAccumulator`; dt_001 |
| `surface_water__discharge (steady-state)` | derived steady-state accumulated runoff | `m^3/s` | Q = drainage_area * runoff_rate | `FlowAccumulator`; DAG rank-3 output |
| `soil__depth` | HWSD-style soil depth commonly in cm | `m` | cm to m: divide by 100 | `convert_soil_params.py`; dt_006 |
| `K_sp` | literature or calibrated stream-power erodibility | `length^(1-2*m_sp) / time` | units depend on `m_sp` and `n_sp`; at `m_sp=0.5`, `n_sp=1.0`, K has units `1/yr` when dt is in years | `StreamPowerEroder`; dt_003 |
| `linear_diffusivity` | literature or calibrated diffusivity | `m^2/yr` when dt is in years | m^2/s to m^2/yr: multiply by 3.156e7 | `LinearDiffuser`; dt_002 |
| `dt` | user-chosen timestep | `yr` for landscape evolution, `s` for overland flow | dt must share the time unit of K, diffusivity, uplift, and other rates | `run_landlab.py`; dt_005 |
| `uplift_rate` | scalar or field forcing | `m/yr` | applied as `z[core_nodes] += uplift_rate * dt` when dt is in years | user execution loop |
| `sediment__outflux (SPACE Qs)` | SPACE / ErosionDeposition node flux | `m^3/s (or kg/s)` | compare Qs directly against Q; do not fit SSC = Qs/Q when testing SPACE Qs-Q | DAG rank-2 output; dt_020 |
| `rainfall__daily_depth` | SoilMoisture rainfall can arrive in m or m/s | `mm` at cell | m to mm: multiply by 1000; m/s to mm/day: multiply by 86,400,000 | SoilMoisture; dt_008 |
---
## 11. Validated Results
Source of truth for validation bars: `docs/validation_convention.yaml`. Null convention bands are written as `no cited threshold`; no uncited threshold is substituted.
### Performance Metrics -- judged against the field's bar
| DAG variable | Headline metric | Direction | Convention bar, cited | KI result currently documented |
|--------------|-----------------|-----------|------------------------|-------------------------------|
| `sediment_yield` | `pbias` | zero_centered | very good: `|PBIAS| <= 10` (moriasi2015); good: `|PBIAS| <= 15` (moriasi2015); satisfactory: `|PBIAS| <= 20` (moriasi2015) | Loess Plateau sediment-yield run reports `8390.6 t/km^2/yr` against `2,000-10,000 t/km^2/yr`; the body does not state a PBIAS value. |
| `topographic__elevation` | `csi` | maximize | very good: no cited threshold; good: no cited threshold; satisfactory: no cited threshold; cites: `[]` | Whipple & Tucker steady-state run reports slope-area `r^2 = 0.9978`, relief `810.2 m`, and wall time `2.2 s`; no CSI value is stated. |
| `concavity (theta)` | `pbias` | zero_centered | very good: no cited threshold; good: no cited threshold; satisfactory: no cited threshold; cites: `[]` | SPACE steady-state run reports theta `0.464`; Loess Plateau slope-area run reports theta `0.1631`; Whipple & Tucker run reports theta `0.488` against expected `0.500`. |
| `steepness (ks)` | `pbias` | zero_centered | very good: no cited threshold; good: no cited threshold; satisfactory: no cited threshold; cites: `[]` | The DAG exposes `steepness (ks)` as a derived slope-area metric; this body does not state a validated ks value. |
### Documented validation cases
| Case | Configuration source | Reported result |
|------|----------------------|-----------------|
| SPACE Binary -- Atchafalaya Qs-Q + Concavity (2026-04-29) | `tools/dissect_atchafalaya_ssc_q_surrogate.py`; outputs under `outputs/landlab_atchafalaya_ssc_q/` | `b_sim = 0.897`, log-log `r = 0.978`, theta `0.464`, obs `r = 0.809`; all listed as PASS in this body. |
| Loess Plateau Real-DEM Slope-Area (2026-04-29) | `tools/dissect_loess_plateau_slope_area.py` | theta `0.1631`, binned `R^2 = 0.8659`, channel nodes `12,315`; all listed as PASS in this body. |
| Loess Plateau Sediment Yield + Particulate-P Export (2026-04-29) | `tools/dissect_loess_plateau_sediment_yield.py`; outputs under `outputs/landlab_loess_sediment/` | sediment yield `8390.6 t/km^2/yr`; particulate-P export `134.25 kg/ha/yr`; both listed as PASS in this body. |
| Whipple & Tucker (1999) Steady-State Test (2026-03-25) | Python Landlab component run described below | slope-area `r^2 = 0.9978`, theta `0.488`, relief `810.2 m`, wall time `2.2 s`; listed as PASS where thresholds are stated in this body. |
### Data replacement tracking
| Component | Source | Status | Notes |
|-----------|--------|--------|-------|
| Forcing | CMFD/MSWX/NASA POWER via `ki_tools_common.load_forcing.load_daily_forcing` where meteorological forcing is needed | Available | Landscape-evolution sediment-yield runs commonly use uniform runoff_rate, not a daily hydrograph. |
| Soil | HWSD or other soil data via `convert_soil_params.py` | Available | Convert depth from cm to m before Landlab fields. |
| DEM / topography | SRTM/ASTER/LiDAR through `convert_dem_to_grid.py` | Validated in Loess Plateau cases | DEM must be projected in meters and NoData must be closed. |
| Initial conditions | Landlab grid fields (`topographic__elevation`, `soil__depth`, `bedrock__elevation`) | Available | Field locations and units must match component expectations. |
---
## Grid Field System
Fields are stored at 7 locations on the grid:
| Location | Description | Access Pattern |
|----------|-------------|----------------|
| `node` | Grid vertices (most common) | `grid.at_node["field_name"]` |
| `link` | Edges connecting nodes | `grid.at_link["field_name"]` |
| `patch` | Polygons in primal mesh | `grid.at_patch["field_name"]` |
| `cell` | Polygons in dual mesh | `grid.at_cell["field_name"]` |
| `corner` | Vertices in dual mesh | `grid.at_corner["field_name"]` |
| `face` | Edges in dual mesh | `grid.at_face["field_name"]` |
| `grid` | Scalar (whole-grid) values | `grid.at_grid["field_name"]` |
### Standard Variable Names and Units
| Variable | Units | Location | Description |
|----------|-------|----------|-------------|
| `topographic__elevation` | m | node | Land surface elevation |
| `drainage_area` | m^2 | node | Contributing upstream area |
| `surface_water__discharge` | m^3/s | node | Volumetric water discharge |
| `water__unit_flux_in` | m/s | node | Rainfall rate (depth/time) |
| `topographic__steepest_slope` | - | node | Max downhill gradient |
| `flow__receiver_node` | - | node | Downstream neighbor ID |
| `flow__upstream_node_order` | - | node | Topological sort order |
| `flow__link_to_receiver_node` | - | node | Link ID to receiver |
| `soil__depth` | m | node | Regolith/soil thickness |
| `soil_production__rate` | m/yr | node | Bedrock-to-soil conversion rate |
| `bedrock__elevation` | m | node | Bedrock surface elevation |
| `hillslope_sediment__unit_volume_flux` | m^2/s | link | Diffusive sediment flux |
| `topographic__gradient` | - | link | Surface slope on links |
| `sediment__influx` | m^3/s | node | Incoming sediment rate |
| `sediment__outflux` | m^3/s | node | Outgoing sediment rate |
| `surface_water__depth` | m | node | Water depth on surface |
| `rainfall__daily_depth` | mm | cell | Daily precipitation |
| `vegetation__cover_fraction` | - | cell | Fractional veg cover |
---
## Unit Trap Table
These are the most dangerous unit mismatches in Landlab workflows:
| # | Variable | Expected | Common Mistake | Effect | Triplet |
|---|----------|----------|----------------|--------|---------|
| 1 | `water__unit_flux_in` | m/s | mm/hr or m/yr | Discharge 1000x–31.5M× wrong | dt_001 |
| 2 | `linear_diffusivity` | m^2/yr (if dt in yr) | m^2/s | Hillslopes flatten instantly | dt_002 |
| 3 | `K_sp` (erodibility) | depends on m,n | wrong exponent combo | Erosion rate orders of magnitude off | dt_003 |
| 4 | `xy_spacing` | m | km or degrees | Area, slope, flux all wrong | dt_004 |
| 5 | `dt` (timestep) | must match K units | yr vs s mismatch | All rates wrong | dt_005 |
| 6 | `soil__depth` | m | cm | Weathering rate, SPACE output wrong | dt_006 |
| 7 | `topographic__elevation` | m | ft or cm | Slope calc wrong → erosion wrong | dt_007 |
| 8 | `rainfall__daily_depth` | mm | m | Soil moisture 1000× off | dt_008 |
| 9 | `roughness` (Manning n) | s/m^(1/3) | 1/n (Chezy) | Velocity inverted | dt_009 |
| 10 | DEM nodata value | -9999 | 0 or NaN | Flow routes into nodata cells | dt_010 |
---
## Key Components Reference
### Flow Routing
| Component | Method | Key Params |
|-----------|--------|------------|
| `FlowAccumulator` | Wraps director + accumulation | `flow_director`, `runoff_rate` |
| `FlowDirectorD8` | Steepest single-direction (D8) | — |
| `FlowDirectorDINF` | D-infinity (two receivers) | — |
| `FlowDirectorMFD` | Multiple flow directions | `partition_method` |
| `DepressionFinderAndRouter` | Fill/route pits | `routing` |
| `PriorityFloodFlowRouter` | Priority-flood (Barnes 2014) | `flow_metric`, `suppress_out` |
### Erosion & Sediment Transport
| Component | Equation | Key Params |
|-----------|----------|------------|
| `StreamPowerEroder` | E = K A^m S^n | `K_sp`, `m_sp`, `n_sp`, `threshold_sp` |
| `ErosionDeposition` | Davy & Lague (2009) ξ-q | `K`, `v_s`, `m_sp`, `n_sp` |
| `Space` | Bedrock + alluvium layers | `K_sed`, `K_br`, `v_s`, `H_star` |
| `LinearDiffuser` | q = -D ∇z | `linear_diffusivity` |
| `DepthDependentDiffuser` | Nonlinear depth-dep. | `linear_diffusivity`, `soil_transport_decay_depth` |
| `TaylorNonlinearHillslopeFlux` | Taylor expansion nonlinear | `critical_slope`, `nterms` |
### Weathering & Soil Production
| Component | Equation | Key Params |
|-----------|----------|------------|
| `ExponentialWeatherer` | P = P0 exp(-H/H*) | `soil_production_maximum_rate`, `soil_production_decay_depth` |
| `DepthDependentTaylorDiffuser` | Coupled soil transport | `soil_transport_velocity`, `soil_transport_decay_depth` |
### Hydrology
| Component | Method | Key Params |
|-----------|--------|------------|
| `KinwaveOverlandFlowModel` | Kinematic wave + Manning | `precip_rate`, `roughness` |
| `OverlandFlow` | de Almeida et al. (2012) | `h_init`, `mannings_n` |
| `SoilMoisture` | Laio et al. (2001) bucket | `soil_porosity`, `soil_field_capacity` |
| `GroundwaterDupuitPercolator` | Dupuit approx. | `hydraulic_conductivity`, `porosity` |
### Tectonics & Lithology
| Component | Description | Key Params |
|-----------|-------------|------------|
| `NormalFault` | Vertical displacement on fault | `faulted_surface`, `fault_throw_rate_through_time` |
| `Flexure` | Lithospheric flexure (Airy/elastic) | `eet`, `youngs` |
| `Lithology` | Track rock layers through erosion | `layer_ids`, `layer_thicknesses` |
---
## I/O Formats
| Format | Read | Write | Function |
|--------|------|-------|----------|
| ESRI ASCII (.asc) | Yes | Yes | `landlab.io.esri_ascii.load()` / `dump()` |
| NetCDF (.nc) | Yes | Yes | `to_netcdf()` / `from_netcdf()` |
| Shapefile (.shp) | Yes | No | `read_shapefile()` |
| VTK Legacy (.vtk) | No | Yes | `write_legacy_vtk()` |
| OBJ (.obj) | No | Yes | `write_obj()` |
| Native Landlab | Yes | Yes | `save()` / `load()` |
---
## Critical Domain Knowledge
1. **Landlab is unit-agnostic** — it does NOT enforce units internally.
The user MUST ensure all inputs share a consistent unit system.
If elevation is in meters, spacing must be meters, K must be in m^(1-2m)/yr,
diffusivity in m^2/yr, and dt in years. Mixing m/s with m/yr is the #1
source of silent failures. → dt_001, dt_005
2. **K_sp units depend on m_sp and n_sp exponents** — for the stream power
law E = K A^m S^n, when m=0.5 and n=1.0, K has units m^(1-2m)/time =
m^0/yr = 1/yr. Changing exponents changes K units. Copying K values from
papers with different exponents produces wrong erosion rates. → dt_003
3. **Boundary conditions must be set before component instantiation** —
FlowAccumulator needs at least one open boundary node for drainage.
If all boundaries are closed, flow has nowhere to go and drainage_area
will be zero everywhere. → dt_011
4. **Depression routing is not automatic** — D8 flow routing stops at pits.
Without `DepressionFinderAndRouter` or `PriorityFloodFlowRouter`, interior
pits accumulate flow and block downstream erosion. → dt_012
5. **CFL stability for explicit diffusion** — LinearDiffuser uses an explicit
scheme with internal sub-stepping (alpha=0.15 × dx²/D). If the user wraps
it in their own sub-stepping, they may defeat the internal CFL guard. → dt_013
6. **Grid spacing affects slope calculation** — Slope = Δz/Δx. If xy_spacing
is in degrees instead of meters, slopes will be orders of magnitude wrong,
producing unrealistic erosion. → dt_004
7. **FlowAccumulator must run before erosion components** — StreamPowerEroder,
ErosionDeposition, and Space all require `drainage_area`, `flow__receiver_node`,
and `flow__upstream_node_order` to be populated. These are outputs of
FlowAccumulator. Running erosion first yields zero drainage area. → dt_014
---
## Validation: SPACE Binary — Atchafalaya Qs-Q + Concavity (2026-04-29)
### Configuration — Test 1 (Qs-Q binary)
- Grid: 3×51 quasi-1D channel, dx=500 m, slope=5×10⁻⁴, soil_depth=2 m
- Component: SpaceLargeScaleEroder (K_sed=2.5e-5, m_sp=0.5, n_sp=1.0, v_s=5 m/yr, H*=1 m)
- Method: warmup 800 steps × 1 yr; then 7 runoff-rate probes (single SPACE step, no uplift)
- Fit: log(Qs) vs log(Q) — sediment mass flux vs water flux
### Configuration — Test 2 (Steady-state concavity)
- Grid: 50×50 RasterModelGrid, dx=200 m, open south boundary
- Duration: 4000 × 500 yr = 2 Myr; uplift U=1e-3 m/yr
### Results
| Test | Metric | Value | Threshold | Status |
|------|--------|-------|-----------|--------|
| T1: Qs-Q binary | b_sim | 0.897 | [0.35, 1.20] | PASS |
| T1: Qs-Q binary | r (log-log) | 0.978 | ≥ 0.90 | PASS |
| T2: Concavity | θ | 0.464 | [0.40, 0.55] | PASS |
| Context: data quality | obs r | 0.809 | ≥ 0.65 | PASS |
### Key Findings
1. b_sim=0.897 reflects transport-limited SPACE behavior (86.5% sediment-dominated, long travel distance) — physically correct for these parameters
2. Detachment-limited regime (b→m=0.5) requires bare bedrock (H→0) or very high v_s; transport-limited (b→1.0) matches Atchafalaya obs b=1.077
3. Fitting Qs vs Q (not SSC vs Q) is essential — SSC = Qs/Q ∝ Q^(m-1) = Q^(-0.5) always gives negative exponent regardless of model correctness
4. Steady-state θ=0.464 confirms SPACE correctly implements stream-power scaling (Whipple & Tucker 1999 theoretical θ=0.5)
### Output
- Figure: `outputs/landlab_atchafalaya_ssc_q/validation_figure.png`
- Metrics: `outputs/landlab_atchafalaya_ssc_q/metrics.json`
- Run: `python tools/dissect_atchafalaya_ssc_q_surrogate.py`
---
## Validation: Loess Plateau Real-DEM Slope-Area (2026-04-29)
### Configuration
- DEM: SRTM 30m, tile N36E109 (~36.64°N 109.33°E, Shaanxi, China)
- Clip: 500×500 px (15 km × 15 km), dx=30 m, z=[953, 1369] m
- Tool: `convert_dem_to_grid.py` → `FlowAccumulator(D8 + DepressionFinderAndRouter)`
- Slope method: 2D spatial gradient |∇z| (binned; D8 receiver slope is noisy on SRTM)
- Channel threshold: A ≥ 0.1 km²; 25 log-spaced area bins
### Results vs Published Loess Plateau Concavity
| Metric | Simulated | Reference | Status |
|--------|-----------|-----------|--------|
| Concavity θ | 0.1631 | 0.10–0.35 (Loess Plateau gullies) | PASS |
| R² (binned) | 0.8659 | — | PASS (≥ 0.30) |
| Channel nodes | 12,315 | — | PASS (≥ 20) |
### Key Findings
1. Binned slope-area R²=0.866 confirms Landlab correctly extracts channel scaling from real terrain
2. θ=0.163 is consistent with actively eroding Loess Plateau gullies (lower than global mountain-river average θ≈0.45)
3. D8 receiver slope (`topographic__steepest_slope`) gives θ≈0.13 and R²≈0.08 on SRTM — use spatial gradient instead (dt_018)
4. Single-outlet boundary (`closed_all_but_outlet`) depresses θ toward 0 — use `open_all` for DEM clips (dt_019)
5. SRTM integer elevations create discrete slope bands; log-spaced binning recovers R²=0.87 from a raw scatter of R²=0.07
### Triplets exercised
- dt_018: D8 slope artifact on filled DEMs → use spatial gradient
- dt_019: single-outlet boundary depresses θ → use open_all
---
## Validation: Loess Plateau Sediment Yield + Particulate-P Export (2026-04-29)
### Configuration
- DEM: SRTM 30m, tile N36E109, clip 500×500 px (15 km × 15 km), dx=30 m, z=[953, 1369] m
- Model: Detachment-limited stream power law E = K_sp × A^0.5 × S (no SPACE; see dt_022)
- Boundary: `closed_all_but_outlet` (lowest boundary node = outlet, z=959 m, node 189499)
- Flow routing: `FlowAccumulator(D8 + DepressionFinderAndRouter)`, runoff_rate=3.17e-9 m/s (100 mm/yr)
- K_sp calibration: K_sp = SY_target / (mean(A^0.5 × S) × ρ_bulk × 1e6) → 1.64e-4 m^0/yr
- Bulk density: 1400 kg/m³; Soil P: 800 mg/kg; P enrichment ratio: 2.0
- P export: TP = SY × 10 × P_soil_ppm × ER × 1e-6
### Results vs Published Yellow River Tributary Yields
| Metric | Simulated | Reference | Status |
|--------|-----------|-----------|--------|
| Sediment yield | 8390.6 t/km²/yr | 2,000–10,000 t/km²/yr (Liu 1985; Wang 2011) | PASS |
| Particulate-P export | 134.25 kg/ha/yr | 0.5–200 kg/ha/yr (threshold) | PASS |
### Key Findings
1. SPACE is unsuitable for annual SY from real DEMs — transport-limited regime gives ~2.7% export efficiency; use DL stream power instead (dt_022)
2. K_sp calibrated analytically so mean gross erosion = target SY; no iterative tuning needed
3. Spatial gradient |∇z| (np.gradient on 2D elevation array) gives physically meaningful slopes for SY; D8 receiver slope is too noisy at cell scale (dt_018)
4. `closed_all_but_outlet` is mandatory for watershed-scale SY (single catchment definition); `open_all` creates many sub-basins and underestimates outlet flux
5. P export (TP=134.25 kg/ha/yr) is gross potential; actual at watershed outlet is 0.3–0.7× lower due to within-basin P retention
### Triplets exercised
- dt_022: SPACE near-zero SY on real DEMs → use DL stream power for annual SY
### Output
- Figure: `outputs/landlab_loess_sediment/validation_figure.png`
- Metrics: `outputs/landlab_loess_sediment/metrics.json`
- Run: `python tools/dissect_loess_plateau_sediment_yield.py`
---
## Validation: Whipple & Tucker (1999) Steady-State Test (2026-03-25)
### Configuration
- Grid: 50×50 `RasterModelGrid`, dx=100 m
- Components: FlowAccumulator(D8) + StreamPowerEroder(K=1e-5, m=0.5, n=1.0) + LinearDiffuser(D=0.01 m²/yr)
- Uplift: 1e-3 m/yr uniform
- Duration: 2 Myr, dt=500 yr (4000 steps)
- Boundary: South edge open, other three closed
### Results vs Analytical Solution
| Metric | Simulated | Expected | Status |
|--------|-----------|----------|--------|
| Slope-area r² | 0.9978 | ~1.0 | PASS |
| Concavity (θ) | 0.488 | 0.500 | PASS (2.4% error) |
| Relief (m) | 810.2 | ~800 | PASS |
| Wall time | 2.2 s | — | — |
### Key Findings
1. Slope-area correlation r=0.9978 confirms steady-state channel profiles
2. Concavity index θ=0.488 vs theoretical 0.500 (2.4% error, within discretization)
3. Relief stabilized at 810.2 m after 2 Myr of evolution
4. All three components (FlowAccumulator, StreamPowerEroder, LinearDiffuser) validated
5. Wall time 2.2s for 4000 steps on 50×50 grid demonstrates computational efficiency
---
## Tools Reference
| Tool | Stage | Path | Lines | Purpose |
|------|-------|------|-------|---------|
| `convert_dem_to_grid.py` | s2 | `tools/convert_dem_to_grid.py` | ~250 | Load DEM → RasterModelGrid with fields |
| `convert_soil_params.py` | s3 | `tools/convert_soil_params.py` | ~220 | HWSD soil → grid fields (depth, K, porosity) |
| `run_landlab.py` | s5 | `tools/run_landlab.py` | ~280 | Execute Landlab simulation from YAML config |
| `parse_landlab_output.py` | s6 | `tools/parse_landlab_output.py` | ~250 | Extract grid fields → CSV/NetCDF + metrics |
| `dissect_atchafalaya_ssc_q_surrogate.py` | validation | `tools/dissect_atchafalaya_ssc_q_surrogate.py` | ~280 | SSC-Q surrogate validation vs USGS-07381600 (n=357, r=0.8092) |
| `dissect_loess_plateau_slope_area.py` | validation | `tools/dissect_loess_plateau_slope_area.py` | ~430 | Real-DEM slope-area: SRTM N36E109, θ=0.163 R²=0.866 PASS |
| `dissect_loess_plateau_sediment_yield.py` | validation | `tools/dissect_loess_plateau_sediment_yield.py` | ~280 | DL stream power SY + particulate-P: SY=8390.6 t/km²/yr PASS, TP=134.25 kg/ha/yr PASS |
---
## Calibration Parameters (Priority Order)
| Parameter | Component | Range | Controls | Sensitivity |
|-----------|-----------|-------|----------|-------------|
| K_sp | StreamPowerEroder | 1e-7–1e-3 | Channel erosion rate | Very High |
| linear_diffusivity | LinearDiffuser | 0.001–1.0 m²/yr | Hillslope curvature | High |
| m_sp | StreamPowerEroder | 0.3–0.7 | Area-discharge scaling | High |
| n_sp | StreamPowerEroder | 0.7–2.0 | Slope sensitivity | High |
| uplift_rate | (user loop) | 1e-5–1e-2 m/yr | Relief, channel gradient | Very High |
| v_s | Space/ErosionDeposition | 0.01–10 m/s | Transport vs detachment | High |
| K_sed / K_br | Space | 1e-7–1e-3 | Alluvium vs bedrock erosion | High |
| H_star | Space | 0.1–2.0 m | Alluvial cover effect | Medium |
| soil_production_maximum_rate | ExponentialWeatherer | 1e-5–1e-3 m/yr | Soil thickness | Medium |
| soil_production_decay_depth | ExponentialWeatherer | 0.2–2.0 m | Weathering decay | Medium |
---
## Data Requirements
| Data | Source | Format | Notes |
|------|--------|--------|-------|
| DEM | SRTM/ASTER/LiDAR | GeoTIFF or ESRI ASCII | Must be projected (meters) |
| Soil depth | HWSD/SoilGrids | Raster/CSV | Convert cm → m |
| Rainfall | CMFD/MSWX/ERA5 | NetCDF | Convert mm/hr → m/s for `water__unit_flux_in` |
| Uplift rate | Literature/GPS | Scalar or field | Must match dt units |
| Erodibility (K) | Literature/calibration | Scalar or field | Units depend on m, n |
| Diffusivity (D) | Literature/calibration | Scalar | m²/yr typical |
---
## Quick Start
```bash
# Install
pip install landlab
# Run input converter
python tools/convert_dem_to_grid.py --dem input.asc --output grid.nc --spacing 100
# Run soil parameter converter
python tools/convert_soil_params.py --hwsd soil.csv --grid grid.nc --output params.json
# Run simulation
python tools/run_landlab.py --config config.yaml --output results/
# Parse output
python tools/parse_landlab_output.py --input results/ --output results/summary.csv --metrics slope_area
```
---
## Diagnostic Triplets Summary
| ID | Severity | Domain | Short Description |
|----|----------|--------|-------------------|
| dt_001 | silent | unit_conversion | Rainfall in mm/hr instead of m/s |
| dt_002 | silent | unit_conversion | Diffusivity time-unit mismatch |
| dt_003 | silent | unit_conversion | K_sp units wrong for given m,n |
| dt_004 | silent | unit_conversion | Grid spacing in degrees not meters |
| dt_005 | silent | unit_conversion | Timestep unit mismatch with parameters |
| dt_006 | silent | unit_conversion | Soil depth in cm instead of m |
| dt_007 | silent | unit_conversion | Elevation in feet instead of meters |
| dt_008 | silent | unit_conversion | Rainfall mm vs m for soil moisture |
| dt_009 | silent | parameter_format | Manning n vs Chezy C confusion |
| dt_010 | degraded | input_format | DEM nodata not handled |
| dt_011 | fatal | boundary_condition | All boundaries closed |
| dt_012 | degraded | missing_component | No depression routing |
| dt_013 | degraded | numerical_stability | CFL violation in diffusion |
| dt_014 | fatal | component_order | Erosion before flow routing |
| dt_015 | silent | field_location | Field at wrong grid element |
| dt_016 | silent | ssc_q_rating_curve | Wrong runoff_rate breaks SPACE SSC-Q correlation |
| dt_017 | silent | ssc_q_rating_curve | m_sp/n_sp wrong → concavity outside [0.40, 0.55] |
| dt_018 | silent | slope_area_analysis | D8 receiver slope polluted by depression-filling → use spatial gradient |
| dt_019 | silent | slope_area_analysis | Single-outlet boundary depresses θ toward 0 → use open_all for DEM clips |
| dt_020 | silent | ssc_q_rating_curve | Fitting SSC vs Q gives negative exponent — always fit Qs vs Q |
| dt_021 | silent | ssc_q_rating_curve | SPACE b outside [0.35,1.20] — check DL vs TL regime (H, H*, v_s) |
| dt_022 | silent | model_scope_mismatch | SPACE near-zero SY on real DEMs — use DL stream power for annual SY |
Reference: `diagnostics/triplets.yaml`
---
## File Structure
```
ki/
├── SKILL.md # This file
├── tools/
│ ├── convert_dem_to_grid.py # DEM → Landlab grid
│ ├── convert_soil_params.py # Soil data → grid fields
│ ├── run_landlab.py # Execution wrapper
│ ├── parse_landlab_output.py # Output → CSV + metrics
│ ├── dissect_atchafalaya_ssc_q_surrogate.py # SPACE Qs-Q + concavity validation
│ ├── dissect_loess_plateau_slope_area.py # Real-DEM slope-area validation
│ └── dissect_loess_plateau_sediment_yield.py # DL stream power SY + P export validation
├── docs/
│ ├── s1_grid_setup.md # Grid creation skill
│ ├── s2_input_preparation.md # DEM/forcing loading
│ ├── s3_soil_parameters.md # Soil/weathering params
│ ├── s4_component_assembly.md # Coupling components
│ └── s5_execution_output.md # Running and analyzing
└── diagnostics/
└── triplets.yaml # 22 symptom→diagnosis→remedy
```
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!