Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 55 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,58 @@ introduce breaking changes to configuration and outputs.
resource-class cell coverage once per configuration and reuse it across
crops, substantially reducing build time and peak memory without changing
outputs.
- **The water system has been rebuilt on a consumption basis.** Irrigation
previously drew from a single per-region growing-season store sized from
Huang et al. withdrawals. It now draws from a regional pool anchored on
WaterGAP 2.2e irrigation consumption, through a per-region delivery link
whose efficiency `eta_c` is calibrated at build time against observed
consumption, with availability and scarcity characterised by AWARE 2.0. The
three water quantities the literature conflates (crop net requirement,
consumption, withdrawal) are now distinct and separately reported. New
automatic downloads: AWARE 2.0 and WaterGAP 2.2e (ISIMIP3a). The Water
Footprint Network "sustainable" supply scenario and the
`water.supply_scenario` key are removed, along with the WFN availability
download/processing pipeline and its documentation figure; the source is now
`water.data.availability` (`aware` or `current_use`), defaulting to `aware`.
**This is a results-affecting default change** — the AWARE pool is a looser
constraint than the previous binding present-day withdrawal cap.
- Water supply and demand can be resolved at **intra-year periods**
(`water.temporal_resolution`, a divisor of 12), so a season whose surface
cannot meet its demand draws groundwater endogenously instead of being
rescued by annual averaging. Crop water demand is placed into periods by the
observed MIRCA-OS irrigated crop calendar, retimed to WaterGAP's monthly
requirement. **The default is 1 (annual), which is cheap but has a
consequence worth stating plainly: at annual resolution the groundwater bands
are nearly inert and reported depletion falls to near zero — an artefact of
the resolution, not a finding.** Studies about water should raise it.
- Surface water and renewable groundwater are characterised as **one AWARE
renewable resource**: each basin's CF curve spans the joint envelope
(WaterGAP surface delivery plus renewable groundwater) and is split at the
basin's surface fraction — the lower slice is period-bound surface, the
upper slice becomes annual per-region renewable-groundwater CF bands. This
replaces the earlier draft's flat renewable-groundwater band at the region's
scarcest surface CF, which saturated at the AWARE cutoff (CF 100) almost
everywhere and drifted with the temporal resolution. Groundwater is always
part of the aware supply (the `water.supply.groundwater` switch is removed;
cap mining at solve time via `groundwater_depletion.cap_mm3: 0` for a
mining-free system); the `current_use` source emits no groundwater bands,
since its observed-withdrawal pool already contains groundwater. Irrigation's
share of the groundwater-storage depletion trend is attributed by its share
of all-sector potential groundwater consumption (new WaterGAP `ptotusegw`
download), so basins mined by municipal or industrial pumping no longer
zero irrigation's renewable band. Water supply fidelity remains a single
switch, `water.supply.scarcity_tiers` (convex AWARE scarcity curves, default
off — each pool is one flat availability cap). Scarcity pricing or capping
requires it and raises otherwise, since with collapsed curves there is no
scarcity signal to price.
- New optional solve-time levers, both off by default: `water_scarcity`
(pricing and/or capping accumulated AWARE scarcity) and
`groundwater_depletion` (pricing and/or capping accumulated mining). With
`water_scarcity.nonrenewable_cf` set, mined groundwater is charged at that
CF under scarcity pricing *and* counts CF-fold against a scarcity cap (a
joint constraint), so neither lever can be satisfied by free substitution
into fossil groundwater. Analysis gains a `water_metrics` output with
per-region withdrawal, scarcity, renewable groundwater and depletion.
- Model regions are now built **basin-aware**: GADM provinces are first split
along AWARE hydrological basin boundaries, and each country is partitioned
into regions balancing geography against basin scarcity
Expand Down Expand Up @@ -170,6 +222,9 @@ introduce breaking changes to configuration and outputs.

### Fixed

- Fixed a GAEZ data artefact where a handful of cells carry a negative net
irrigation requirement, which flipped those crop links into spurious water
*producers*. Negative requirements are now clipped to zero.
- Baseline biofuel/industrial and biogas demand is enforced again. Since
2026-05-20 the crops-with-supply safety check in `add_biofuel_links` ran
before any crop production links existed, so every build silently dropped
Expand Down
3 changes: 2 additions & 1 deletion config/calibration/cost.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,8 @@ validation:
slack_marginal_cost: 7.5

water:
supply_scenario: "current_use"
data:
availability: "current_use"

# NB: diet.anchor_groups_to_gbd is pinned to the base config's resolved value
# by tools/calibrate (via a config overlay), so disabling health here for
Expand Down
114 changes: 106 additions & 8 deletions config/default.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ numerics:
# Coefficient clipping: zero negligible coefficients on retained components.
min_link_area_mha: 0.000001 # ~1 ha; smaller land areas -> 0
min_water_requirement_m3_per_ha: 0.1 # <0.1 mm/ha irrigation -> no water draw
min_water_capacity_mm3: 0.000001 # <1 m3 annual/period capacity -> no supply link
min_co2_coefficient_tco2_per_ha: 0.001 # near-zero land carbon flux -> 0
min_cost_correction_bnusd: 0.000001 # ~0.001 USD per unit flow -> 0

Expand Down Expand Up @@ -250,14 +251,111 @@ land:

# --- section: water ---
water:
# Water supply scenario determines which dataset is used for regional water limits:
# - "sustainable": Water Footprint Network blue water availability by basin (Hoekstra & Mekonnen 2011)
# Represents sustainable water extraction limits.
# - "current_use": Huang et al. (2018) gridded irrigation water withdrawals
# Represents actual/current agricultural water use, useful for validation.
supply_scenario: "current_use"
# Reference year for Huang irrigation data (only used when supply_scenario is "current_use")
huang_reference_year: 2010
# Number of intra-year periods the LP resolves for water supply and demand
# (structural; must divide 12). Each period balances its own surface
# availability against the crop water demand whose growing season falls in it,
# so a season whose surface cannot meet its demand draws groundwater
# endogenously. 1 = annual, 12 = monthly, 4 = quarterly.
#
# The default is 1: cheap, and adequate wherever water is not the object of
# study. Note what it costs. At 1 a region's whole annual surface pool is
# available to every season, so wet-season surplus subsidises the dry season
# and the groundwater bands go nearly inert -- a near-zero reported depletion
# at T=1 is an artefact of the resolution, not a finding. Studies about water
# should raise this (4 is a reasonable seasonality/solve-time compromise).
temporal_resolution: 1
# Structural (build-time) water-supply configuration. Groundwater is always
# part of the aware supply: additive renewable- and non-renewable-groundwater
# bands (annual, per region) on top of the period-bound surface supply, so
# mining emerges endogenously wherever surface plus renewable groundwater
# fall short. Studies wanting a groundwater-free system can constrain it at
# solve time (groundwater_depletion.cap_mm3: 0 caps mining; scarcity pricing
# penalises the renewable draw).
supply:
# Convex AWARE scarcity tiers per region-period (and renewable-groundwater
# bands), so drawing more water costs more where water is scarcer. False
# collapses each pool to one flat cap -- a plain availability constraint,
# cheaper and sufficient where water is not the object of study. Scarcity
# pricing (see water_scarcity) requires this.
scarcity_tiers: false
# Marginal cost of non-renewable groundwater (USD per m3): a small pumping-
# energy cost (~100 m lift at ~0.1 USD/kWh) that also orders groundwater last
# in the merit order.
pumping_cost_usd_per_m3: 0.04
# Non-renewable groundwater ceiling as a multiple of a region's annual
# agricultural consumption. Generous and non-binding: mining is drawn only to
# cover the residual deficit (pumping cost keeps it minimal), so this bounds
# mining well above any plausible draw without pinning it.
groundwater_ceiling_factor: 3.0
# Data provenance (build-time). Swapping a source changes numbers, not model
# behaviour; every source yields the same consumption-basis regional pool.
data:
# Regional water availability + scarcity pool: "aware" (AWARE2.0) or
# "current_use" (Huang et al. gridded irrigation withdrawals; validation).
availability: "aware"
# Inclusive reference window for WaterGAP irrigation consumption, surface
# delivery and the eta_c anchor. Matches AWARE2.0's recent scarcity window.
surface_reference_start: 1990
surface_reference_end: 2019
# Inclusive year window for the groundwater-storage depletion trend. This
# stays recent so the depletion band represents current mining.
groundwater_trend_start: 2000
groundwater_trend_end: 2019
# Reference year for Huang irrigation data (only when availability is "current_use").
huang_reference_year: 2010
# Consumption-basis irrigation delivery. The water pool, scarcity and
# groundwater bands sit on consumption C; crops need the GAEZ net requirement
# E. A per-region delivery link draws C = E / eta_c from the pool, with eta_c
# calibrated at build time as E_baseline / C_anchor (clipped to
# [eta_min, eta_max] and floored at E_baseline / pool for baseline
# feasibility). The anchor is WaterGAP irrigation consumption (pirruse) --
# one volume basis for supply envelope, groundwater bands and demand.
irrigation:
# Lower clip on the calibrated consumptive efficiency; ratios below this
# are treated as data-quality issues (logged), not real inefficiency.
eta_min: 0.2
# Upper clip. Values above 1 encode deficit irrigation: observed
# consumption below the GAEZ full requirement (India, Pakistan, Thailand),
# so the baseline draw matches observed consumption instead of the
# requirement. The cap guards regions with unreliably small consumption
# anchors, where an uncapped ratio would make marginal irrigation free.
eta_max: 5.0
# Consumed fraction C/W (consumption over withdrawal). Used to report
# withdrawal in the analysis and to convert the current_use availability
# pool from withdrawal to consumption basis. Global constant for now.
consumed_fraction: 0.58

# --- section: water_scarcity ---
# Solve-time pricing and/or capping of the accumulated AWARE water-scarcity
# impact (Mm3 world-equivalent). Both default off; the availability cap from the
# supply curve is always active. Requires water.supply.scarcity_tiers, since with
# collapsed tiers every surface characterisation factor is zero and there is no
# scarcity signal to price.
water_scarcity:
pricing_enabled: false
price: 0 # Shadow price, USD per m3 world-equivalent
cap_mm3_world_eq: null # Epsilon-constraint cap (Mm3 world-eq); null = no cap
# CF applied to non-renewable groundwater under scarcity pricing or capping:
# each mined m3 is charged nonrenewable_cf * price, and counts
# nonrenewable_cf-fold against cap_mm3_world_eq (a joint constraint). AWARE
# covers renewable water only and excludes fossil stocks, so this is a
# bridging weight that prices a mined m3 at the scarcity of the exhausted
# renewable water it displaces, closing the otherwise free substitution into
# fossil groundwater. 100 is AWARE's demand-exceeds-availability cutoff plus
# a non-renewability premium. Set null to leave mining out of both, e.g.
# when groundwater_depletion prices or caps it separately (combining both
# pricings is an error; a null-cf scarcity cap with mining unconstrained
# logs a porosity warning).
nonrenewable_cf: 100

# --- section: groundwater_depletion ---
# Solve-time pricing and/or capping of accumulated non-renewable groundwater
# depletion (Mm3 mined). Requires water.data.availability: aware; both
# default off.
groundwater_depletion:
pricing_enabled: false
price: 0 # Shadow price, USD per m3 mined
cap_mm3: null # Epsilon-constraint cap (Mm3 mined); null = no cap

# --- section: fertilizer ---
fertilizer:
Expand Down
144 changes: 132 additions & 12 deletions config/schemas/config.schema.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ required:
- optimal_taxes
- land
- water
- water_scarcity
- groundwater_depletion
- fertilizer
- residues
- emissions
Expand Down Expand Up @@ -190,6 +192,10 @@ properties:
type: number
minimum: 0
description: "Land areas (p_nom_max, baseline) below this many Mha are zeroed"
min_water_capacity_mm3:
type: number
minimum: 0
description: "Water supply bands below this capacity (Mm3) are dropped"
min_water_requirement_m3_per_ha:
type: number
minimum: 0
Expand Down Expand Up @@ -407,21 +413,135 @@ properties:

water:
type: object
required: [supply_scenario, huang_reference_year]
required: [temporal_resolution, supply, data, irrigation]
additionalProperties: false
properties:
supply_scenario:
type: string
enum: [sustainable, current_use]
description: |
Water supply scenario determining which dataset is used for regional water limits:
- 'sustainable': Water Footprint Network blue water availability (Hoekstra & Mekonnen 2011)
- 'current_use': Huang et al. (2018) gridded irrigation water withdrawals
huang_reference_year:
temporal_resolution:
type: integer
minimum: 1971
maximum: 2010
description: "Reference year for Huang irrigation data (only used when supply_scenario is 'current_use')"
enum: [1, 2, 3, 4, 6, 12]
description: |
Structural number of intra-year water periods the LP resolves (a
divisor of 12). Each period balances its own surface availability
against the crop water demand whose growing season falls in it, so
seasonal shortfalls draw groundwater endogenously. 1 = annual (no
seasonal binding); 12 = monthly (faithful); intermediate values
group whole months into equal blocks. Cost scales ~linearly in the
value on the water side of the model.
supply:
type: object
required: [scarcity_tiers, pumping_cost_usd_per_m3, groundwater_ceiling_factor]
additionalProperties: false
properties:
scarcity_tiers:
type: boolean
description: |
Keep the convex AWARE scarcity curves (per-region-period surface
tiers and annual renewable-groundwater bands, true), or collapse
each pool to one flat availability cap (false). Required for
water_scarcity pricing or capping, which has no signal to act on
once every CF is zero.
pumping_cost_usd_per_m3:
type: number
minimum: 0
description: "Marginal cost of non-renewable groundwater (USD per m3); merit-order separator"
groundwater_ceiling_factor:
type: number
minimum: 0
description: "Non-renewable groundwater ceiling as a multiple of annual agricultural consumption; generous and non-binding"
data:
type: object
required: [availability, surface_reference_start, surface_reference_end, groundwater_trend_start, groundwater_trend_end, huang_reference_year]
additionalProperties: false
properties:
availability:
type: string
enum: [aware, current_use]
description: |
Regional water availability + scarcity pool source:
- 'aware': AWARE2.0 (Seitfudem et al. 2025, WaterGAP2.2e) convex scarcity curve and native basin geometry; pool volumes, monthly timing and within-region basin allocation set by the WaterGAP surface field
- 'current_use': Huang et al. (2018) gridded irrigation water withdrawals
surface_reference_start:
type: integer
minimum: 1901
maximum: 2019
description: "First year (inclusive) of the WaterGAP irrigation-consumption and surface-delivery reference window"
surface_reference_end:
type: integer
minimum: 1901
maximum: 2019
description: "Last year (inclusive) of the WaterGAP irrigation-consumption and surface-delivery reference window"
groundwater_trend_start:
type: integer
minimum: 1901
maximum: 2019
description: "First year (inclusive) of the groundwater-storage depletion trend window"
groundwater_trend_end:
type: integer
minimum: 1901
maximum: 2019
description: "Last year (inclusive) of the groundwater-storage depletion trend window"
huang_reference_year:
type: integer
minimum: 1971
maximum: 2010
description: "Reference year for Huang irrigation data (only used when availability is 'current_use')"
irrigation:
type: object
required: [eta_min, eta_max, consumed_fraction]
additionalProperties: false
properties:
eta_min:
type: number
exclusiveMinimum: 0
maximum: 1
description: "Lower clip on the calibrated consumptive irrigation efficiency eta_c = E_baseline / C"
eta_max:
type: number
minimum: 1
description: "Upper clip on eta_c; values above 1 encode deficit irrigation (observed consumption below the GAEZ requirement)"
consumed_fraction:
type: number
exclusiveMinimum: 0
maximum: 1
description: "Consumed fraction C/W (consumption over withdrawal); reporting and current_use pool conversion"

water_scarcity:
type: object
required: [pricing_enabled, price, cap_mm3_world_eq, nonrenewable_cf]
additionalProperties: false
properties:
pricing_enabled:
type: boolean
description: "Whether to price accumulated water scarcity in the objective"
price:
type: number
minimum: 0
description: "Water-scarcity shadow price, USD per m3 world-equivalent"
cap_mm3_world_eq:
type: [number, "null"]
minimum: 0
description: "Epsilon-constraint cap on total water scarcity (Mm3 world-eq); null disables the cap"
nonrenewable_cf:
type: [number, "null"]
minimum: 0
description: "CF charged per m3 of non-renewable groundwater under scarcity pricing and capping (AWARE cutoff 100); null leaves mining out of both"

groundwater_depletion:
type: object
required: [pricing_enabled, price, cap_mm3]
additionalProperties: false
properties:
pricing_enabled:
type: boolean
description: "Whether to price accumulated non-renewable groundwater depletion in the objective"
price:
type: number
minimum: 0
description: "Groundwater-depletion shadow price, USD per m3 mined"
cap_mm3:
type: [number, "null"]
minimum: 0
description: "Epsilon-constraint cap on total groundwater depletion (Mm3 mined); null disables the cap"

fertilizer:
type: object
Expand Down
Loading
Loading