How twx-phenoweight, twx-risk-severity-library and the weather pipelines map into ag_risk to score each dimension · with two worked examples · 2026-07-16
The engine is fixed; a solution is just a config. Every crop-risk product — Kenya maize today,
Vietnam coffee tomorrow — is the same engine wired to three swappable things: data
sources (pipelines & models that emit a signal on the grid), a WHEN profile
(twx-phenoweight), and HOW-BAD curves (twx-risk-severity-library).
Change those three in config and you get a new crop, region, or peril — with no engine change.
1. The pieces — one engine, three swappable inputs
Per dimension, the model does the same three moves:
get the signal → phenoweight it (WHEN) → score its severity (HOW BAD), then combine the
dimensions. The three inputs are modular; the engine and the combine never change.
One fixed engine; three swappable inputs wired in config. Add a dimension = add a source + a WHEN curve + a severity curve — all config.
Two more inputs the engine needs — per crop, not per dimension (the scaffolding). The three
inputs above are swapped per dimension; every run also needs two per-crop inputs that
are easy to overlook:
Crop calendar (planting date / DOY — e.g. GGCMI) — anchors WHEN in time. The
phenoweight is a relative timing curve; the calendar gives it a zero-point, so "flowering
sensitivity" lands on the right actual dates. Without it, WHEN has nothing to hang on.
Crop mask (where the crop grows — e.g. MapSPAM / WorldCover) — defines WHERE:
which pixels count as this crop. It sets the denominator for every score and is why an
over-inclusive mask distorts the numbers.
So the honest picture is three swappable inputs (data · WHEN · HOW-BAD) + two per-crop scaffolding
inputs (calendar = when the crop grows, mask = where).
2. Worked flow — Kenya maize (today, weather-only)
The shipped setup. Four dimensions, all from the ERA5 (realised) / SEAS5 (forecast) weather
pipeline, scored with a maize phenoweight profile and literature severity curves.
This driver set is a first pass — and what drives a dimension can change. A dimension is a
stable name (drought · heat · cold · water); what drives it — the signal(s), the data
source / feed, and the severity curve — is config, not fixed. Over time these change with
no engine change and no change to the output format: e.g. heat's driver can move from
EDD → Bayer's Tmax>35°C-day count, a second driver can be added to a
dimension, a feed can be swapped, or a curve re-anchored under calibration. The signals shown above
(water deficit + soil moisture for drought, EDD + hot nights for heat, …) are a first,
literature-anchored pass and are expected to evolve — only the shown dimensions / values
move, the emitted schema stays fixed.
3. Worked flow — Vietnam coffee (same engine, new config, mixed sources)
Now a different crop with heterogeneous data sources. The engine, phenoweight and severity
library are unchanged — only the config differs: a coffee phenoweight profile (different
timing), coffee severity curves, and signals from weather + an observed satellite feed + two
models. Each still lands on the same read contract, gets phenoweighted, and is scored by the
library.
Scope note. v1 in Model Foundry is weather-only. The flood / disease / satellite
examples below are the extensibility pattern, not the production model — they live on a
separate GEE testbed branch for testing and expansion, not in the shipped weather model.
weather
observed EO
model output
Four heterogeneous signals — weather, an observed satellite feed, and two models — flow through the same coffee phenoweight × severity library → combine → coffee scores. The engine didn't change.
Dimension
Signal (source)
Phenoweight (coffee)
Severity method
Verdict
Drought
rainfall/ET (ERA5/SEAS5) + soil moisture (SMAP)
flowering / cherry-fill
curve
Directional
Heat
temperature — ERA5 / SEAS5
flowering
curve
Directional
Disease
incidence — disease-detection model
warm-humid window
curve (incidence→loss, generated upstream)
Directional
Flood
inundation fraction + persistence (time underwater) — flood model (Sentinel-1)
SMAP (observed soil moisture) joins the drought signal alongside the modelled water balance — a new observed feed on the same read contract.
The disease-detection model is a data source: its incidence field is turned into an incidence→loss curveupstream (the feature-importance / calibration step) and stored in the severity library; ag_risk reads that curve like any other and phenoweights it to the warm-humid window. (The model never emits severity directly — it feeds curve generation.)
The flood model emits an inundation fraction and a persistence (how long the water stays): the fraction is the exposure (how much of the crop is hit) and persistence scales the conditional damage (how badly the flooded part is hit) — together they give the severity, phenoweighted to the coffee's sensitive stage.
4. Forecast vs realised — one engine, two run modes
The same engine, curves and phenoweight run in two modes — only the feed, the
role and the output store differ:
Realised — --role realised, scored on ERA5 actuals → realised_risk_*.zarr, appended along valid_time (what actually happened, one value per elapsed month).
Forecast — --role forecast, scored on the SEAS5 forward feed → forecast_risk_*.zarr, appended as a new init_time vintage over horizon (the forward outlook, every vintage kept).
Two consequences of "same engine, different feed":
Only forecastable dimensions run in forecast mode. The weather dimensions work on both feeds; observed-EO dimensions (fire, flood, vegetation) are realised-only and gate out of a forecast run automatically — no config change.
Both append the same-schema stores each run, exactly the way the weather pipeline saves ERA5 (realised) and SEAS5 (forecast) — history grows, nothing is overwritten. (Format & lifecycle: the downstream-integration doc.)
5. The point — modularity
Between Kenya maize and Vietnam coffee, the engine, twx-phenoweight and
twx-risk-severity-library never changed. Only the config did — which sources
feed each dimension, which WHEN profile, which severity curves. Adding disease and flood to coffee was
not an engine change: it was a new signal source + a coffee phenoweight window + a severity
entry, all declared in config. That is the framework — a fixed scoring structure that any crop,
region, peril, or data source plugs into.
Same three moves, every time: get the signal (from any pipeline or model) → phenoweight it
(WHEN, from twx-phenoweight) → score its severity (HOW BAD, from
twx-risk-severity-library) → weight-free combine. Swap the inputs, keep the engine.
Companion to the twx-risk-severity-library proposal. Kenya maize is the shipped v1;
Vietnam coffee is illustrative of the extension path (mixed weather / EO / model sources).