Skip to content
Back to skills

Docs

ASecurity

> **Stage ID**: s2_soil_params > **Pipeline order**: 2 of 8 > **Depends on**: none

  • 231 stars
  • 0 votes
  • 0 copies
  • 3 views
  • Added September 11, 2026
testingpythongobashspringgitdatabase

Security analysis

A100/100

Pro scans all 14 files and shows the line behind each finding

Scanned November 9, 2026

npx -y skills add lzwei196/KISS---Knowledge-Infrastructure-for-Scientific-Simulation --skill docs --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Docs?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Docs
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/lzwei196-docs-b1a04323/badge)](https://www.skillsdirectory.com/skills/lzwei196-docs-b1a04323)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
# Soil Parameter Setup — Skill Document

> **Stage ID**: s2_soil_params
> **Pipeline order**: 2 of 8
> **Depends on**: none

## Purpose

Configure soil hydraulic and physical parameters for the WOFOST classic water balance. Unlike DSSAT's multi-layer soil profile, WOFOST uses a single-layer "bucket" model characterized by three moisture limits (SMW, SMFCF, SM0) and hydraulic conductivity (K0). The soil parameters determine water stress patterns, which directly control yield in water-limited (WLP) simulations. Incorrect soil parameters produce plausible-looking but wrong yields with no error message.

## Prerequisites

- [ ] Target location coordinates known (lat, lon)
- [ ] HWSD global raster accessible at `data/soil/HWSD_RASTER/hwsd.bil` (or equivalent soil database)
- [ ] HWSD MDB database accessible at `data/forcing/huaihe_raw/soil/HWSD.mdb`
- [ ] Understanding of which simulation mode will be used (soil params not needed for Wofost72_PP)

## Inputs

| Input | Type | Source | Description |
|-------|------|--------|-------------|
| lat | number | basin delineation | Latitude of grid cell center |
| lon | number | basin delineation | Longitude of grid cell center |
| hwsd_raster | file | data/soil/HWSD_RASTER/hwsd.bil | HWSD global soil raster |
| hwsd_mdb | file | data/forcing/huaihe_raw/soil/HWSD.mdb | HWSD properties database |
| sand_pct | number | HWSD or lab data | Sand fraction (%) |
| clay_pct | number | HWSD or lab data | Clay fraction (%) |
| organic_carbon | number | HWSD or lab data | Organic carbon (%) |
| bulk_density | number | HWSD or lab data | Bulk density (g/cm3) |

## Procedure

### Step 1: Extract soil properties from HWSD

```python
# Use the shared HWSD soil adapter (same source as VIC and DSSAT)
# This extracts sand, silt, clay, OC, bulk density for the location
from hwsd_soil_adapter import get_soil_properties
props = get_soil_properties(lat=52.0, lon=5.5,
    hwsd_raster='data/soil/HWSD_RASTER/hwsd.bil',
    hwsd_mdb='data/forcing/huaihe_raw/soil/HWSD.mdb')
```

**Expected result**: Dictionary with T_SAND, T_SILT, T_CLAY, T_OC, T_REF_BULK_DENSITY.

**If this fails**: Check HWSD file paths; verify lat/lon is on land (ocean cells have no soil data).

### Step 2: Convert to WOFOST soil parameters — use the SHARED hydraulics, never a private formula

> **dt_v007 (fixed 2026-09-12).** This step used to teach three linear formulas labelled
> "Wösten et al. (1999)". They were NOT Wösten's method, and against an independent Saxton &
> Rawls (2006) reference they gave soil with a median **0.58×** the real plant-available water.
> Re-running the C01 maize case with correct soil moved rainfed yield **+7.3% / +35.0% / 0%**
> over three seasons. Do not re-derive these numbers from a formula in a doc. Use the tool, which
> uses the shared library:

```bash
# stdout = the soil JSON (logs go to stderr). SOIL_PARAMS_JSON also makes the tool write
# soil_params.json.provenance.json (texture used, PTF source, units) next to it.
SOIL_PARAMS_JSON=soil_params.json \
  python tools/s2_soil_params/convert_hwsd_to_pcse_soil.py <lat> <lon> > soil_params.json
```

Then load the result. Every later step uses these two names (`soil_params`, `texture`):

```python
import json
with open('soil_params.json') as f:
    soil_params = json.load(f)     # 8 soil-hydraulic keys: SMW SMFCF SM0 CRAIRC K0 SOPE KSUB RDMSOL
with open('soil_params.json.provenance.json') as f:
    texture = json.load(f)['texture_input']   # sand/clay/silt/oc (%) and bd the tool used
```

If you must build the soil by hand (no tool run), make the SAME 8-key dict from the shared
library. This is what the tool does:

```python
from ki_tools_common.soil_utils import lookup_hwsd
props = lookup_hwsd(lat, lon)          # dominant HWSD component WITH usable texture (fixed 2026-09-11)
hyd   = props['hydraulics']            # shared Rawls-1982 class table, the same values every crop KI uses
texture = {'sand': props['sand'], 'clay': props['clay'], 'silt': props['silt'], 'oc': props['oc']}
soil_params = {
    'SMW':   round(float(hyd['wilting_point']), 4),
    'SMFCF': round(float(hyd['field_capacity']), 4),
    'SM0':   round(float(hyd['saturation']), 4),
    'CRAIRC': 0.06,                    # critical soil air content (standard WOFOST value), NOT SM0-SMFCF
    'K0':    round(float(hyd['ksat_cm_hr']) * 24.0, 2),   # UNITS: shared table is cm/hr, WOFOST K0 is cm/DAY
    'SOPE':  1.0,
    'KSUB':  1.0,
    'RDMSOL': 120.0,                   # set by texture in Step 4
}
```

Do NOT put IFUNRN, NOTINF, SSI, SSMAX, WAV or SMLIM in `soil_params`: in PCSE 6.0 they come from
`WOFOST72SiteDataProvider`, and having them in both places raises "Duplicate parameter found"
(dt_v003).

The shared table is itself a coarse 12-class lookup at ~0.86× the continuous reference — good
enough, and honest, but not perfect. Upgrading it is a `ki_tools_common` job, not a WOFOST one.

**Never** write `SMW = 0.001 + 0.26*clay ...` or any other private pedotransfer here. If you find
one in a WOFOST tool, it is the bug this note exists to prevent.

### Step 3: Validate water limits

```bash
python tools/s2_soil_params/validate_soil_params.py soil_params.json   # exit 0 = pass, 2 = a check failed
```

or, in the same Python session:

```python
assert soil_params['SMW'] < soil_params['SMFCF'], \
    f"SMW ({soil_params['SMW']}) must be < SMFCF ({soil_params['SMFCF']})"
assert soil_params['SMFCF'] < soil_params['SM0'], \
    f"SMFCF ({soil_params['SMFCF']}) must be < SM0 ({soil_params['SM0']})"
assert soil_params['SM0'] <= 0.65, \
    f"SM0 ({soil_params['SM0']}) unreasonably high (max porosity ~0.65)"
assert soil_params['CRAIRC'] < soil_params['SM0'] - soil_params['SMFCF'], \
    "CRAIRC must be < SM0 - SMFCF (available air at field capacity)"
```

**Expected result**: All assertions pass.

**If this fails**: See diagnostic triplet dt_009 (soil FC < WP).

### Step 4: Set rootable depth

RDMSOL should reflect actual soil depth. The tool already applies the texture rule below; repeat
it only for a hand-built dict, or lower it further when you know the soil is shallow (rocky, hardpan).

```python
# Adjust RDMSOL based on soil type (same rule as convert_hwsd_to_pcse_soil.py)
if texture['clay'] > 60:
    soil_params['RDMSOL'] = 80.0   # heavy clay, shallow roots
elif texture['sand'] > 80:
    soil_params['RDMSOL'] = 150.0  # sandy, deep roots possible
else:
    soil_params['RDMSOL'] = 120.0  # medium textures
```

### Step 5: Save soil parameters

```python
import json
output_path = f'outputs/{run_name}/wofost/soil_params.json'
with open(output_path, 'w') as f:
    json.dump(soil_params, f, indent=2)
```

## Expected Outputs

| Output | Path | Verification |
|--------|------|--------------|
| soil_params dict | `outputs/{run}/wofost/soil_params.json` | JSON with the 8 soil-hydraulic keys; SMW < SMFCF < SM0 |

## Validation Checks

1. **Water limits ordered**: SMW < SMFCF < SM0 — violation means pedotransfer failed
   - Command: `assert soil['SMW'] < soil['SMFCF'] < soil['SM0']`
   - If unexpected: See diagnostic triplet dt_009

2. **SM0 physically possible**: SM0 <= 0.65 (porosity cannot exceed ~65%)
   - If SM0 > 0.65: bulk density is too low or miscalculated

3. **K0 positive**: K0 > 0 cm/day — zero K0 means no water movement
   - Typical range: 1-50 cm/day

4. **RDMSOL reasonable**: 10-300 cm — shallower than 10 cm is extreme; deeper than 300 cm is unrealistic

## Common Pitfalls

> **PITFALL**: Using DSSAT soil parameters directly
> DSSAT uses multi-layer SLLL/SDUL/SSAT per layer. WOFOST uses single-layer SMW/SMFCF/SM0 for the entire profile. You cannot simply copy DSSAT layer 1 values — you need a profile-average or pedotransfer from raw texture.
> **Do this instead**: Always use pedotransfer from HWSD sand/clay/OC to WOFOST parameters.
> See diagnostic triplet dt_009.

> **PITFALL**: Forgetting WAV (initial available water)
> WAV defaults to 0 if not set. Starting a simulation with completely dry soil can cause immediate crop death, especially for spring-sown crops.
> **Do this instead**: Set WAV to a reasonable value (10-50 cm) representing antecedent moisture. For VIC-coupled runs, use VIC soil moisture output to initialize WAV.

> **PITFALL**: Not needed for PP mode
> If running Wofost72_PP (potential production), soil parameters are ignored. Don't waste time optimizing soil params for PP runs.

---

*This skill document is part of the wofost-pcse-knowledge infrastructure.*
*Stage 2 of 8 | Tools: convert_hwsd_to_pcse_soil, validate_soil_params | Related triplets: dt_009*

Files in this skill

  • REFERENCES.md1.5 KB
  • format_spec.yaml41.2 KB
  • model_couplings.yaml14.4 KB
  • papers.json6.5 KB
  • s1_crop_params_skill.md6.9 KB
  • s2_soil_params_skill.md6.8 KB
  • s3_weather_prep_skill.md8.4 KB
  • s4_agromanagement_skill.md7.1 KB
  • s5_engine_config_skill.md5.8 KB
  • s6_execution_skill.md5.9 KB
  • s7_output_parsing_skill.md5 KB
  • s8_yield_analysis_skill.md6.5 KB
  • validation_convention.yaml22.3 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

View all in testing →

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.