Skill v1.0.0
currentAutomated scan100/100metadata: version: "1.0.0"
MANDATORY EXECUTION POLICY — READ BEFORE PROCEEDINGYou MUST run the actual model binary or package described in this document.If the model fails to import, compile, or execute, you MUST:1. Checkdiagnostics/triplets.yamlfor a matching error pattern2. Try the fix suggested in the triplet'sremedysection3. If still failing, report the error to the user with full detailsYou 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.yamlmay already cover this error2. Read official docs — The model's own documentation for expected formats/units3. Find working examples — Checkoutputs/or the model's shipped test data4. Fix the tool — With knowledge of what "correct" looks likeDo 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: current tool, stage-doc and triplet counts: the KI map at the top of this file Validation status: production_validated
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)
python -m venv venv && source venv/bin/activatepip install landlab
From source (development)
git clone https://github.com/landlab/landlab.gitcd landlabpip 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
import landlab; print(landlab.__version__)from landlab import RasterModelGridmg = 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:
from landlab import RasterModelGridfrom landlab.components import FlowAccumulator, StreamPowerEroder, LinearDiffuser# 1. Create gridmg = 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 conditionsmg.set_closed_boundaries_at_grid_edges(True, False, True, False)# 3. Instantiate componentsfa = 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 loopdt = 1000.0 # yearsfor t in range(2000):fa.run_one_step()sp.run_one_step(dt)ld.run_one_step(dt)# 5. Outputfrom landlab.io import write_esri_asciiwrite_esri_ascii("output.asc", mg, "topographic__elevation")
BMI Interface
Components can also be driven via the Basic Modeling Interface (BMI):
from landlab.bmi import wrap_as_bmiBmiSPE = 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. Validation Status
Run results (site, period, scores) are not kept here. They live in the run records (test_runs in hydrocraft.db and each run's own result files).
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 | |||||||
|---|---|---|---|---|---|---|---|---|---|---|
sediment_yield | pbias | zero_centered | very good: ` | PBIAS | <= 10 (moriasi2015); good: | PBIAS | <= 15 (moriasi2015); satisfactory: | PBIAS | <= 20` (moriasi2015) | |
topographic__elevation | csi | maximize | very good: no cited threshold; good: no cited threshold; satisfactory: no cited threshold; cites: [] | |||||||
concavity (theta) | pbias | zero_centered | very good: no cited threshold; good: no cited threshold; satisfactory: no cited threshold; cites: [] | |||||||
steepness (ks) | pbias | zero_centered | very good: no cited threshold; good: no cited threshold; satisfactory: no cited threshold; cites: [] |
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 | Available | 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
Synthetic test setups (how-to)
SPACE Qs-Q test (quasi-1D channel)
- 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
SPACE steady-state concavity test
- Grid: 50×50 RasterModelGrid, dx=200 m, open south boundary
- Duration: 4000 × 500 yr = 2 Myr; uplift U=1e-3 m/yr
Whipple & Tucker (1999) steady-state test
- 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
Lessons from past runs (rules)
- Fit Qs vs Q directly, never SSC = Qs/Q vs Q: SSC vs Q always gives a negative exponent,
whatever the model does (dt_020).
- If the SPACE Qs-Q exponent b is outside [0.35, 1.20], check whether SPACE runs in the
detachment-limited or transport-limited regime (H, H*, v_s) (dt_021).
- For slope-area on a real DEM, use the 2D spatial gradient |∇z| with log-spaced area bins,
not the D8 receiver slope (topographic__steepest_slope) (dt_018).
- For slope-area on a DEM clip, use
open_allboundaries; a single-outlet boundary
(closed_all_but_outlet) pushes θ toward 0 (dt_019).
- Do not use SPACE for annual sediment yield on a real DEM; use detachment-limited stream
power, set K_sp analytically from the target SY, and use closed_all_but_outlet for watershed-scale SY (dt_022).
- P export from TP = SY × 10 × P_soil_ppm × ER × 1e-6 is a gross potential; the amount at the
watershed outlet is lower because of P retention inside the basin.
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 | SPACE Qs-Q rating and steady-state concavity check | |
dissect_loess_plateau_slope_area.py | validation | tools/dissect_loess_plateau_slope_area.py | ~430 | Real-DEM slope-area (spatial gradient, log-binned) | |
dissect_loess_plateau_sediment_yield.py | validation | tools/dissect_loess_plateau_sediment_yield.py | ~280 | DL stream power SY + particulate-P export |
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
# Installpip install landlab# Run input converterpython tools/convert_dem_to_grid.py --dem input.asc --output grid.nc --spacing 100# Run soil parameter converterpython tools/convert_soil_params.py --hwsd soil.csv --grid grid.nc --output params.json# Run simulationpython tools/run_landlab.py --config config.yaml --output results/# Parse outputpython 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 # diagnostic triplets (count: KI map)