Configuration

Overview

The food-opt model is configuration-driven: all scenario parameters, crop selections, constraints, and solver options are defined in YAML configuration files under config/. This allows exploring different scenarios without modifying code.

The default configuration is config/default.yaml, structured into thematic sections.

Custom configuration files

Instead of modifying the default configuration file, it is recommended to explore individual scenarios by creating named configuration files, overriding specific parts of the default configuration. Such a named configuration file must contain at the minimum a name. An example could be something like the following:

# config/my_scenario.yaml
name: "my_scenario"           # Scenario name → results/my_scenario/
planning_horizon: 2040        # Override the default 2030 horizon
land:
  regional_limit: 0.6         # Tighten land availability
  slack_marginal_cost: 1e10   # Optional: raise slack penalty during validation
emissions:
  ghg_price: 250              # Raise the carbon price above the default

Any keys omitted in your custom file fall back to the defaults shown in the sections below, so you can keep overrides concise.

Results are saved under results/{name}/, allowing multiple scenarios coming from different configuration files to coexist.

To build and solve the model based on the above example configuration, you would run the following:

tools/smk -j4 --configfile config/my_scenario.yaml

Scenario Presets

The workflow supports scenario presets defined in config/scenarios.yaml that apply configuration overrides via a {scenario} wildcard. This allows exploring variations (e.g., with/without health constraints or GHG pricing) within a single configuration without duplicating config files.

Each scenario preset in scenarios.yaml contains a set of configuration overrides that are applied recursively on top of the base configuration. For example:

# config/scenarios.yaml
default:
  health:
    enabled: false
  emissions:
    ghg_pricing_enabled: false

HG:
  health:
    enabled: true
  emissions:
    ghg_pricing_enabled: true

The scenario name becomes part of all output paths:

  • Built models: results/{name}/build/model_scen-{scenario}.nc

  • Solved models: results/{name}/solved/model_scen-{scenario}.nc

  • Plots: results/{name}/plots/scen-{scenario}/

To build a specific scenario:

tools/smk -j4 --configfile config/my_scenario.yaml -- results/my_scenario/build/model_scen-HG.nc

This feature enables systematic sensitivity analysis and comparison across policy scenarios using a single configuration file.

Validation Options

validation:
  use_actual_yields: false
  use_actual_production: false
  enforce_gdd_baseline: false # Set food consumption equal to current day values
  land_slack: false # Enable land slack generators (allows exceeding regional land limits at cost)
  slack_marginal_cost: 50. # bn USD per Mt/Mha for validation slack (food groups, feed, land)
  production_year: 2018 # To match with GDD baseline year
  production_stability:
    enabled: false
    crops:
      enabled: true
      max_relative_deviation: 0.2  # ±20%
    animals:
      enabled: true
      max_relative_deviation: 0.2

# --- section: food_group_incentives ---
food_group_incentives:
  enabled: false  # When true, food-group incentives are applied to the objective
  sources: []

# --- section: optimal_taxes ---
optimal_taxes:
  enabled: false  # When true, enables the optimal taxes/subsidies workflow

Set validation.enforce_gdd_baseline to true to force the optimizer to match baseline consumption derived from the processed GDD file. When this flag is active, the diet.baseline_age and diet.baseline_reference_year settings determine which cohort/year is enforced. Use validation.food_group_slack_marginal_cost to set the penalty (USD2024 per Mt) for the slack generators that backstop those fixed food-group loads. Keep the value high so slack only activates when recorded production cannot meet the enforced demand targets.

Production Stability Bounds

The validation.production_stability section allows constraining how much crop and animal product production can deviate from current (baseline) levels. This is useful for investigating what positive changes (e.g., improved health outcomes, reduced emissions) can be achieved with limited disruption to existing production patterns.

When enabled, the solver applies per-(product, country) bounds of the form:

\[(1 - \delta) \times \text{baseline} \le \text{production} \le (1 + \delta) \times \text{baseline}\]

where \(\delta\) is the max_relative_deviation parameter (e.g., 0.2 for ±20%).

Configuration options:

  • production_stability.enabled: Master switch for the feature (default: false)

  • production_stability.crops.enabled: Apply bounds to crop production

  • production_stability.crops.max_relative_deviation: Maximum relative deviation for crops (0-1)

  • production_stability.animals.enabled: Apply bounds to animal product production

  • production_stability.animals.max_relative_deviation: Maximum relative deviation for animal products (0-1)

Behavior notes:

  • Products with zero baseline production are constrained to zero (no new products introduced)

  • Products missing baseline data are skipped with a warning

  • Multi-cropping is automatically disabled when production stability is enabled

Configuration sections

Scenario Metadata

scenario_defs: "config/scenarios.yaml"
planning_horizon: 2030
currency_base_year: 2024  # Base year for inflation-adjusted USD values
  • planning_horizon: Target year for optimization (default: 2030). Currently determined only which (projected) population levels to use.

  • currency_base_year: Base year for inflation-adjusted USD values (default: 2024). All cost data is automatically converted to real USD in this base year using CPI adjustments. See Crop Production (Production Costs section) for details on cost modeling.

Download Options

downloads:
  show_progress: true

Crop Selection

crops:
# Core cereals
- wheat
- dryland-rice
- wetland-rice
- maize
- barley
- oat
- rye
- sorghum
- buckwheat
- foxtail-millet
- pearl-millet
# Legumes/pulses
- soybean
- dry-pea
- chickpea
- cowpea
- gram
- phaseolus-bean
- pigeonpea
# Roots and tubers
- white-potato
- sweet-potato
- cassava
- yam
# Vegetables
- tomato
- carrot
- onion
- cabbage
# Fruits
- banana
- citrus
- coconut
# Oil crops
- sunflower
- rapeseed
- groundnut
- sesame
- oil-palm
- olive
# Sugar crops
- sugarcane
- sugarbeet
# Fodder / biomass (also listed in non_food_crops below)
- alfalfa
- silage-maize
- biomass-sorghum
# Note: mango and taro excluded - missing RES02 (growing season) data for GFDL-ESM4

# --- section: non_food_crops ---
# Crops not intended for human food production (fodder, biomass).
# These are excluded from foods.csv validation but still need yield/land data.
non_food_crops:
- alfalfa
- silage-maize
- biomass-sorghum

See Crop Production for full list. Add/remove crops to explore specialized vs. diversified production systems.

Multiple Cropping

multiple_cropping:
  double_rice:
    crops:
    - wetland-rice
    - wetland-rice
    water_supplies:
    - r
    - i
  rice_wheat:
    crops:
    - wetland-rice
    - wheat
    water_supplies:
    - r
    - i
  maize_soybean:
    crops:
    - maize
    - soybean
    water_supplies:
    - r
    - i

Define sequential cropping systems as ordered lists of crops. Entries may repeat a crop (double rice) or mix cereals and legumes (rice→wheat, maize→soybean) and list multiple water_supplies (r for rainfed, i for irrigated) to build both variants. The build_multi_cropping rule checks growing-season compatibility, aggregates eligible area/yields, and sums irrigated water demand; build_model turns each combination into a multi-output land link. Leave the section empty to disable the feature. Multiple cropping zones that imply relay cropping (GAEZ classes “limited double” or “double rice … limited triple”) are still accepted here but are interpreted as sequential crop chains; relay-specific dynamics are not yet modelled.

Country Coverage

countries:
# - ABW  # No level-1 GADM data
- AFG
- AGO
# - AIA  # No regions (microstate)
# - ALA  # No population
- ALB
# - AND  # excluded: microstate
- ARE
- ARG
- ARM
- ASM
# - ATA  # No level-1 GADM data
# - ATF  # No population
- ATG
- AUS
- AUT
- AZE
- BDI
- BEL
- BEN
# - BES  # excluded: small overseas territory
- BFA
- BGD
- BGR
# - BHR  # excluded: desert city-state
- BHS
- BIH
# - BLM  # No regions (microstate)
- BLR
- BLZ
# - BMU  # No regions (microstate)
- BOL
- BRA
- BRB
- BRN
- BTN
# - BVT  # No level-1 GADM data
- BWA
- CAF
- CAN
# - CCK  # No level-1 GADM data
- CHE
- CHL
- CHN
- CIV
- CMR
- COD
- COG
# - COK  # excluded: small island territory
- COL
- COM
- CPV
- CRI
- CUB
# - CUW  # No level-1 GADM data
# - CXR  # No level-1 GADM data
# - CYM  # excluded: small overseas territory
- CYP
- CZE
- DEU
- DJI
# - DMA  # excluded: small island state
- DNK
- DOM
- DZA
- ECU
- EGY
- ERI
# - ESH  # excluded: sparse desert territory
- ESP
- EST
- ETH
- FIN
- FJI
# - FLK  # No level-1 GADM data
- FRA
# - FRO  # excluded: small island territory
# - FSM  # excluded: small island state
- GAB
- GBR
- GEO
# - GGY  # Too small
- GHA
# - GIB  # No level-1 GADM data
- GIN
# - GLP  # excluded: overseas department
- GMB
- GNB
- GNQ
- GRC
- GRD
# - GRL  # excluded: ice-dominated
- GTM
- GUF
# - GUM  # excluded: small island territory
- GUY
# - HKG  # No level-1 GADM data
# - HMD  # No level-1 GADM data
- HND
- HRV
- HTI
- HUN
- IDN
# - IMN  # excluded: small island territory
- IND
# - IOT  # No level-1 GADM data
- IRL
- IRN
- IRQ
- ISL
- ISR
- ITA
- JAM
# - JEY  # No regions (microstate)
- JOR
- JPN
- KAZ
- KEN
- KGZ
- KHM
# - KIR  # No level-1 GADM data
# - KNA  # excluded: small island state
- KOR
# - KWT  # excluded: desert city-state
- LAO
- LBN
- LBR
- LBY
# - LCA  # excluded: small island state
# - LIE  # excluded: microstate
- LKA
- LSO
- LTU
- LUX
- LVA
# - MAC  # No level-1 GADM data
# - MAF  # No level-1 GADM data
- MAR
# - MCO  # No level-1 GADM data
- MDA
- MDG
# - MDV  # No level-1 GADM data
- MEX
# - MHL  # No regions (microstate)
- MKD
- MLI
- MLT
- MMR
- MNE
- MNG
# - MNP  # excluded: small island territory
- MOZ
- MRT
# - MSR  # excluded: small island territory
# - MTQ  # excluded: overseas department
- MUS
- MWI
- MYS
# - MYT  # excluded: overseas department
- NAM
# - NCL  # excluded: overseas territory
- NER
# - NFK  # No level-1 GADM data
- NGA
- NIC
# - NIU  # No level-1 GADM data
- NLD
- NOR
- NPL
# - NRU  # No regions (microstate)
- NZL
- OMN
- PAK
- PAN
# - PCN  # No level-1 GADM data
- PER
- PHL
# - PLW  # excluded: small island state
- PNG
- POL
- PRI
# - PRK  # excluded: no health data available for North Korea
- PRT
- PRY
- PSE
# - PYF  # excluded: overseas territory
# - QAT  # excluded: desert city-state
# - REU  # excluded: overseas department
- ROU
- RUS
- RWA
- SAU
- SDN
- SEN
# - SGP  # excluded: desert city-state (urban)
# - SGS  # No level-1 GADM data
# - SHN  # excluded: small island territory
# - SJM  # No population
- SLB
- SLE
- SLV
# - SMR  # No regions (microstate)
- SOM
# - SPM  # excluded: small island territory
- SRB
- SSD
- STP
- SUR
- SVK
- SVN
- SWE
- SWZ
# - SXM  # No level-1 GADM data
# - SYC  # excluded: small island state
- SYR
# - TCA  # excluded: small island territory
- TCD
- TGO
- THA
- TJK
# - TKL  # No regions (microstate)
- TKM
- TLS
# - TON  # excluded: small island state
- TTO
- TUN
- TUR
# - TUV  # No regions (microstate)
- TWN
- TZA
- UGA
- UKR
# - UMI  # No population
- URY
- USA
- UZB
# - VAT  # No level-1 GADM data
# - VCT  # excluded: small island state
- VEN
# - VGB  # excluded: small island territory
# - VIR  # excluded: small island territory
- VNM
- VUT
# - WLF  # excluded: overseas territory
# - WSM  # excluded: small island state
- YEM
- ZAF
- ZMB
- ZWE

Include countries/territories to model; exclude to reduce problem size. Microstate and countries missing essential data are commented out.

Spatial Aggregation

Controls regional resolution and land classification.

aggregation:
  regions:
    target_count: 400
    allow_cross_border: false
    method: "kmeans"
  simplify_tolerance_km: 5
  simplify_min_area_km: 25
  resource_class_quantiles: [0.25, 0.5, 0.75]
  # Crop land-use limitation source used when aggregating yields by region/resource class.
  # - "suitability": limit area using GAEZ suitability rasters per water supply (irrigated/rainfed)
  # - "irrigated": limit area using the irrigated cropland share (for irrigated) and its complement (for rainfed)
  land_limit_dataset: "irrigated"
Trade-offs:
  • More regions → higher spatial resolution, longer solve time

  • Fewer resource classes → faster solving, less yield heterogeneity

Land, Water, Fertilizer, and Residues

Limits on land, fertilizer availability, and residue management.

land:
  regional_limit: 0.7 # fraction of each region's potential cropland that is made available.

Water Supply

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: sustainable
  # Reference year for Huang irrigation data (only used when supply_scenario is "current_use")
  huang_reference_year: 2010
  • water.supply_scenario selects the water availability dataset: sustainable (Water Footprint Network blue water availability) or current_use (Huang et al. irrigation withdrawals). Use current_use for validation or benchmarking against present-day withdrawals.

  • water.huang_reference_year selects the year (1971-2010) used for the Huang monthly withdrawals when supply_scenario is current_use.

fertilizer:
  limit: 200_000_000  # t-N (200 Mt-N total limit in synthetic fertilizer application)
  marginal_cost_usd_per_tonne: 500 # USD per t-N of synthetic fertilizer
  # High-input agriculture N application rates (percentile of global FUBC data)
  n_percentile: 80  # Use 80th percentile for high-input systems (range: 0-100)
  # Manure nitrogen management
  manure_n_to_fertilizer: 0.75 # Fraction of N excreted in confined quarters available as fertilizer (accounting for losses during storage/handling)
residues:
  max_feed_fraction: 0.30 # Maximum fraction of residues that can be removed for animal feed (remainder must be incorporated into soil)
  max_feed_fraction_by_region: {} # Overrides by ISO3 country code or M49 region/sub-region name (country overrides sub-region overrides region)
  • residues.max_feed_fraction_by_region overrides the global fraction for ISO3 countries or UN M49 regions/sub-regions.

  • Precedence is: country overrides sub-region overrides region.

GAEZ Data Parameters

Configures which GAEZ v5 climate scenario and input level to use.

data:
  gaez:
    # GAEZ v5 parameters
    # Note: RES05 (yields/suitability) has ENSEMBLE, but RES02 (growing season) only has individual GCMs
    climate_model: "GFDL-ESM4" # Specific GCMs: "GFDL-ESM4", "IPSL-CM6A-LR", "MPI-ESM1-2-HR", "MRI-ESM2-0", "UKESM1-0-LL"
    climate_model_ensemble: "ENSEMBLE" # Multi-model mean (only available for RES05, not RES02)
    period: "FP2140" # Future: "FP2140" (2021-2040), "FP4160" (2041-2060), "FP6180" (2061-2080), "FP8100" (2081-2100); Historical: "HP0120" (2001-2020), "HP8100" (1981-2000)
    climate_scenario: "SSP126" # "SSP126" (low emissions), "SSP370" (medium, ~RCP4.5), "SSP585" (high), "HIST" (historical)
    input_level: "H" # "H" (High), "L" (Low)
    water_supply: "R" # "I" (irrigated), "R" (rainfed)
    # Variable codes for GAEZ v5
    yield_var: "RES05-YCX" # Average attainable yield, current cropland
    water_requirement_var: "RES05-WDC" # Water deficit/net irrigation requirement during crop cycle, current cropland
    suitability_var: "RES05-SX1" # Share of grid cell assessed as VS or S (very suitable or suitable)
  usda:
    # API credentials: configure in config/secrets.yaml or via USDA_API_KEY environment variable
    # See config/secrets.yaml.example for setup instructions
    retrieve_nutrition: true  # Set to true to fetch nutrition data from USDA instead of using the provided data
    # Nutrient mapping: internal name -> USDA FoodData Central name
    # USDA names must match nutrient names in FoodData Central exactly
    nutrients:
      protein: "Protein"
      carb: "Carbohydrate, by difference"
      fat: "Total lipid (fat)"
      cal: "Energy"
  land_cover:
    # ECMWF credentials: configure in config/secrets.yaml or via environment variables
    # See config/secrets.yaml.example for setup instructions
    year: "2022"
    version: "v2_1_1"
  soilgrids:
    target_resolution_m: 10000  # Target resolution in meters (10000m = 10km)
Scenarios:
  • SSP126: Strong mitigation (1.5-2°C warming)

  • SSP370: Moderate emissions (~3°C)

  • SSP585: High emissions (~4-5°C)

Input Levels:
  • H: Modern agriculture (fertilizer, irrigation, pest control)

  • L: Subsistence farming (minimal external inputs)

Irrigation

irrigation:
  # Which model crops are allowed to have irrigated production.
  # In GAEZ v5, all crops have both irrigated (HILM/LILM) and rainfed (HRLM/LRLM) data available.
  # List specific crops here if you want to restrict irrigation, or use "all" for all crops.
  irrigated_crops: "all"

# --- section: costs ---
animal_costs:
  averaging_period:
    start_year: 2015
    end_year: 2024

  fadn:
    high_cost_threshold_usd_per_mt: 50000
    livestock_specific_costs:
      SE330: "Other livestock specific costs"
    shared_farm_costs:
      SE340: "Machinery & building current costs"
      SE345: "Energy"
      SE350: "Contract work"
      SE356: "Other direct inputs"
      SE360: "Depreciation"
      SE370: "Wages paid"
      SE380: "Interest paid"
      SE390: "Taxes"
    grazing_cost_items:
      SE310: "Feed for grazing livestock"
      SE315: "Feed for grazing livestock home-grown"
    exclude_costs:
      SE320: "Feed for pigs & poultry"
      SE325: "Feed for pigs & poultry home-grown"
      SE375: "Rent paid"

  usda:
    request_timeout_seconds: 120
    # Conversion factors: kg per head dressed weight
    dressed_weight_kg_per_head:
      meat-cattle: 350.0
      meat-pig: 90.0
    include_items:
    - "Hired labor"
    - "Opportunity cost of unpaid labor"
    - "Bedding and litter"
    - "Custom services"
    - "Fuel, lube, and electricity"
    - "Repairs"
    - "Interest on operating capital"
    - "Marketing"
    - "Veterinary and medicine"
    - "Capital recovery of machinery and equipment"
    - "General farm overhead"
    - "Taxes and insurance"
    grazing_cost_items:
    - "Grazed feed"
    exclude_items:
    - "Homegrown harvested feed"
    - "Purchased feed"
    - "Total, feed costs"
    - "Opportunity cost of land"
    - "Total, operating costs"
    - "Costs listed"

  faostat:
    aggregate_area_code_limit: 5000
    element_codes:
      production: ["2510", "5510"]
      stocks: ["2111", "5111"]
      producing_animals: ["2313", "5318", "5313"]

crop_costs:
  averaging_period:
    start_year: 2015
    end_year: 2024

  fadn:
    per_year_costs:
      SE340: "Machinery & building current costs"
      SE345: "Energy"
      SE350: "Contract work"
      SE360: "Depreciation"
      SE370: "Wages paid"
      SE380: "Interest paid"
    per_planting_costs:
      SE285: "Seeds and plants"
      SE300: "Crop protection"
      SE305: "Other crop specific costs"
    exclude_costs:
      SE295: "Fertilisers"
      SE375: "Rent paid"
    crop_groups:
      Cereals:
        outputs: ["SE140"]
        area: "SE035"
        crops: ["SE140"]
      Vegetables:
        outputs: ["SE170"]
        area: "SE046"
        crops: ["SE170"]
      Wine:
        outputs: ["SE185"]
        area: "SE050"
        crops: ["SE185"]
      Olives:
        outputs: ["SE190"]
        area: "SE060"
        crops: ["SE190"]
      Fruit & Citrus:
        outputs: ["SE175", "SE180"]
        area: "SE055"
        crops: ["SE175", "SE180"]
      Other Field Crops:
        outputs: ["SE145", "SE150", "SE155", "SE160", "SE165", "SE146", "SE200"]
        area: "SE041"
        crops: ["SE145", "SE150", "SE155", "SE160", "SE165"]

  usda:
    request_timeout_seconds: 120
    per_year_costs:
    - "Capital recovery of machinery and equipment"
    - "General farm overhead"
    - "Taxes and insurance"
    per_planting_costs:
    - "Chemicals"
    - "Custom services"
    - "Fuel, lube, and electricity"
    - "Interest on operating capital"
    - "Repairs"
    - "Seed"
    - "Hired labor"
    - "Opportunity cost of unpaid labor"
    exclude_items:
    - "Fertilizer"
    - "Opportunity cost of land"
    - "Purchased irrigation water"

Restrict irrigation to water-scarce scenarios or explore rainfed-only production.

Macronutrients

macronutrients: {}
  # For each of "carb", "protein", "fat" and "kcal" we support "min",
  # "max" and "equal" keywords, which are given in g/person/day; see
  # example below.
  # carb:
  #   min: 250     # g/person/day
  # protein:
  #   min: 50      # g/person/day
  # fat:
  #   min: 50      # g/person/day
  # cal:
  #   min: 2000    # kcal/person/day

# --- section: byproducts ---
# Foods that are not for direct human consumption (excluded from food group tracking)
byproducts:
- wheat-bran
- wheat-germ
- rice-bran
- barley-bran
- oat-bran
- buckwheat-hulls
- sunflower-meal
- rapeseed-meal

Use min, max, or equal constraints.

Food Groups

food_groups:
  included:
  - whole_grains
  - grain
  - fruits
  - vegetables
  - legumes
  - nuts_seeds
  - starchy_vegetable
  - oil
  - red_meat
  - poultry
  - dairy
  - eggs
  - sugar
  # Optional per-group constraints with "min", "max" or "equal" in g/person/day
  constraints: {}
  equal_by_country_source: null

included lists the food groups tracked by the model. constraints is an optional mapping where any included group may define min, max, or equal targets in g/person/day. Leaving constraints empty disables all food group limits; add entries only for the groups you want to control.

Diet Controls

diet:
  baseline_age: "All ages"
  baseline_reference_year: 2018 # Keeping this the same as the health reference year makes sense

Customize baseline_age or baseline_reference_year if you pre-process alternative cohorts or years for the baseline diet. These values are used whenever validation.enforce_gdd_baseline is set to true.

Biomass

biomass:
  enabled: true
  crops:
  - maize
  - oil-palm
  - sugarcane
  - biomass-sorghum
  marginal_cost: 50  # USD_2024 per tonne dry matter exported to the energy sector

Set enabled: true to create a per-country biomass bus that tracks dry-matter exports to the energy sector. All foods listed under byproducts gain optional links to this bus, and any crops listed in biomass.crops can be diverted directly as feedstocks. The marginal_cost parameter (USD2024 per tonne dry matter) sets the price received when biomass leaves the food system.

Animal Products

animal_products:
  include:
  - meat-cattle
  - meat-pig
  - meat-chicken
  - dairy
  - eggs
  - dairy-buffalo
  - meat-sheep
  # Feed conversion efficiency mode (how much feed is required per unit product)
  # Source: Wirsenius (2000) regional feed energy requirements
  # Options:
  #   - List of regions: average efficiencies across those regions (all countries use same values)
  #   - null: use country-specific regional efficiencies based on geographic mapping
  # Available regions: East Asia, East Europe, Latin America & Caribbean,
  #   North Africa & West Asia, North America & Oceania, South & Central Asia,
  #   Sub-Saharan Africa, West Europe
  feed_efficiency_regions:
  - North America & Oceania
  - West Europe
  # Ruminant net-to-metabolizable energy conversion efficiency factors
  # Used to convert net energy (NE) requirements to metabolizable energy (ME) requirements
  # Based on NRC (2000) typical values for mixed diets
  # ME_required = NE_m/k_m + NE_g/k_g (+ NE_l/k_l for dairy)
  # TODO: Should check the reference for this.
  net_to_metabolizable_energy_conversion:
    k_m: 0.60  # Maintenance efficiency
    k_g: 0.40  # Growth efficiency
    k_l: 0.60  # Lactation efficiency (dairy)
  # Carcass-to-retail meat conversion factors
  carcass_to_retail_meat:
    meat-cattle: 0.67  # kg boneless retail beef per kg carcass (OECD-FAO 2023)
    meat-pig: 0.73     # kg boneless retail pork per kg carcass (OECD-FAO 2023)
    meat-chicken: 0.60 # kg boneless retail chicken per kg carcass (OECD-FAO 2023)
    eggs: 1.00         # No conversion needed (whole egg = retail product)
    dairy: 1.00        # No conversion needed (whole milk = retail product)
    meat-sheep: 0.63   # kg boneless retail lamb per kg carcass (slightly lower than beef)
    dairy-buffalo: 1.00 # No conversion needed (whole milk = retail product)
  feed_proxy_map:
    dairy-buffalo: dairy
    meat-sheep: meat-cattle
  residue_crops:
  - banana
  - barley
  - chickpea
  - cowpea
  - dry-pea
  - dryland-rice
  - foxtail-millet
  - gram
  - maize
  - oat
  - pearl-millet
  - phaseolus-bean
  - pigeonpea
  - rye
  - sorghum
  - sugarcane
  - wetland-rice
  - wheat

grazing:
  enabled: true
  pasture_utilization_rate: 0.50 # Fraction of grassland yield available for grazing

Disable grazing to force intensive feed-based systems.

Trade Configuration

trade:
  crop_hubs: 20
  crop_default_trade_cost_per_km: 0.01  # USD_2024 per tonne per km (1e-2)
  crop_trade_cost_categories:
    bulk_dry_goods:
      cost_per_km: 0.006  # USD_2024 per tonne per km (6e-3)
      crops:
      - wheat
      - dryland-rice
      - wetland-rice
      - maize
      - soybean
      - barley
      - oat
      - rye
      - dry-pea
      - chickpea
    bulky_fresh:
      cost_per_km: 0.014  # USD_2024 per tonne per km (1.4e-2)
      crops:
      - white-potato
      - sweet-potato
      - yam
      - cassava
      - sugarbeet
      - biomass-sorghum
    perishable_high_value:
      cost_per_km: 0.022  # USD_2024 per tonne per km (2.2e-2)
      crops:
      - tomato
      - carrot
      - onion
      - cabbage
      - banana
      - sugarcane
      - sunflower
      - rapeseed
      - groundnut
  non_tradable_crops:
    - alfalfa
    - biomass-sorghum
    - silage-maize
  food_hubs: 20
  food_default_trade_cost_per_km: 0.021  # USD_2024 per tonne per km (2.1e-2)
  food_trade_cost_categories:
    chilled_meat:
      cost_per_km: 0.028  # USD_2024 per tonne per km (2.8e-2)
      foods:
      - meat-cattle
      - meat-pig
      - meat-chicken
    dairy_and_eggs:
      cost_per_km: 0.024  # USD_2024 per tonne per km (2.4e-2)
      foods:
      - dairy
      - eggs
  non_tradable_foods: []
  feed_hubs: 15
  feed_default_trade_cost_per_km: 0.012  # USD_2024 per tonne per km (1.2e-2)
  feed_trade_cost_categories:
    grain_protein:
      cost_per_km: 0.006  # USD_2024 per tonne per km (6e-3) - matches crop bulk_dry_goods
      feeds:
      - ruminant_grain
      - ruminant_protein
      - monogastric_grain
      - monogastric_energy
      - monogastric_protein
    forage:
      cost_per_km: 0.012  # USD_2024 per tonne per km (1.2e-2) - 2x grain cost
      feeds:
      - ruminant_forage
    bulky_low_quality:
      cost_per_km: 0.016  # USD_2024 per tonne per km (1.6e-2) - 2.67x grain cost
      feeds:
      - ruminant_roughage
      - monogastric_low_quality
  non_tradable_feeds:
  - ruminant_grassland

Increase trade costs to explore localized food systems; decrease for globalized trade.

All trade costs are expressed in USD_2024 per tonne per kilometer.

Emissions Pricing

emissions:
  ghg_pricing_enabled: true # Whether to include GHG pricing in the objective function
  ghg_price: 200 # USD_2024/tCO2-eq (emissions stored in MtCO2-eq internally)
  ch4_to_co2_factor: 27.0 # IPCC AR6 GWP100 (WG1, Chapter 7, Table 7.15; https://www.ipcc.ch/report/ar6/wg1/chapter/chapter-7/)
  n2o_to_co2_factor: 273.0 # IPCC AR6 GWP100 (WG1, Chapter 7, Table 7.15; https://www.ipcc.ch/report/ar6/wg1/chapter/chapter-7/)
  rice:
    methane_emission_factor_kg_per_ha: 134.47 # kg CH4 per ha per crop (IPCC 2019 Refinement, Vol 4, Chapter 5, Tables 5.11 and 5.11A. Default for continuously flooded fields.)
    rainfed_wetland_rice_ch4_scaling_factor: 0.54 # IPCC 2019 Refinement, Vol 4, Chapter 5, Table 5.12. Scaling factor for "Regular rainfed" water regime.
  fertilizer:
    synthetic_n2o_factor: 0.010 # kg N2O-N per kg N input (IPCC 2019 Refinement, Table 11.1 aggregated default)
    # Indirect N2O emission parameters (IPCC 2019 Refinement, Chapter 11.2.2, Table 11.3)
    indirect_ef4: 0.010 # kg N2O-N per kg (NH3-N + NOx-N) volatilized and deposited (EF4)
    indirect_ef5: 0.011 # kg N2O-N per kg N leached/runoff (EF5)
    frac_gasf: 0.11 # Fraction of synthetic fertilizer N volatilized as NH3 and NOx (FracGASF)
    frac_gasm: 0.21 # Fraction of organic N and grazing N volatilized as NH3 and NOx (FracGASM)
    frac_leach: 0.24 # Fraction of applied/deposited N lost through leaching and runoff in wet climates (FracLEACH-(H))
  residues:
    incorporation_n2o_factor: 0.010 # kg N2O-N per kg residue N incorporated into soil (IPCC 2019 Refinement, Table 11.1 aggregated default)

Land Use Change

luc:
  horizon_years: 25
  managed_flux_mode: "zero"
  forest_fraction_threshold: 0.2  # Minimum forest fraction (0-1) to apply regrowth sequestration
  spared_land_agb_threshold_tc_per_ha: 20.0  # Max AGB (tC/ha) for spared land eligibility

Controls how land use change emissions and carbon sequestration are modeled over the planning horizon.

Parameters:
  • horizon_years: Time horizon (years) for amortizing land use change emissions

  • managed_flux_mode: How to treat emissions from existing managed land ("zero" assumes no net flux from current agricultural land)

  • forest_fraction_threshold: Minimum forest cover fraction (0-1) required for a grid cell to be eligible for regrowth sequestration when land is spared

  • spared_land_agb_threshold_tc_per_ha: Maximum above-ground biomass (tonnes C per hectare) for spared land to be eligible for regrowth sequestration

Health Configuration

health:
  enabled: true  # Whether to include health costs in the objective function
  region_clusters: 30
  reference_year: 2018
  intake_grid_points: 100  # Number of grid knots over empirical RR range
  log_rr_points: 100
  omega3_per_100g_fish: 1.5
  ssb_sugar_g_per_100g: 5.7  # ≈50 kcal per 226.8 g sugar-sweetened beverage (SSB) implies ~5.7 g sugar per 100 g
  value_per_yll: 50000  # USD_2024 per year of life lost
  intake_cap_g_per_day: 1000  # Uniform generous cap on intake grids and clipping
  intake_age_min: 11  # GDD adult band starts at 11; set to 11 to retain adult intake data. Note however that GDB chronic disease risk factors are for adults of >=25 years.
  # Dietary risk factors to consider (must match GDD data items)
  risk_factors:
  - fruits
  - vegetables
  - nuts_seeds
  - legumes
  - fish
  - red_meat
  - prc_meat
  - whole_grains
  # - sugar # Has a funky relative risk curve
  # Health outcomes/causes to consider (must be present in IHME GBD data and relative risks)
  causes:
  - CHD              # Coronary/Ischemic Heart Disease
  - Stroke           # Stroke (all types)
  - T2DM             # Type 2 Diabetes Mellitus
  - CRC              # Colorectal Cancer
  # Mapping of risk factors to the causes they affect
  risk_cause_map:
    fruits: [CHD, Stroke, T2DM]
    vegetables: [CHD, Stroke]
    nuts_seeds: [CHD, CRC, T2DM]
    legumes: [CHD]
    fish: [CHD]
    red_meat: [CHD, Stroke, T2DM, CRC]
    prc_meat: [CHD, T2DM, CRC]
    whole_grains: [CHD, Stroke, T2DM, CRC]
    # sugar: [CHD, Stroke, T2DM, CRC]
  # Theoretical minimum risk exposure levels (TMREL) from GBD Study 2021
  # Source: Brauer et al. (2024), Global Burden of Disease Study 2021
  # Values represent optimal intake levels where health risk is minimized
  # Reference: https://doi.org/10.1016/S0140-6736(24)00933-4
  tmrel_g_per_day:
    fruits: 345         # TMREL: 340-350 g/day (midpoint)
    vegetables: 339     # TMREL: 306-372 g/day (midpoint)
    whole_grains: 185   # TMREL: 160-210 g/day (midpoint)
    nuts_seeds: 21.5    # TMREL: 19-24 g/day (midpoint)
    legumes: 105        # TMREL: 100-110 g/day (midpoint)
    fish: 37.7          # TMREL: 470-660 mg/day omega-3 (midpoint 565 mg, converted using omega3_per_100g_fish)
    red_meat: 0         # TMREL: 0-200 g/day (using conservative lower bound)
    prc_meat: 0         # TMREL: 0 g/day (any intake increases risk)
    sugar: 0            # TMREL: 0 g/day (any refined sugar intake increases risk)
  # Multi-objective clustering settings for grouping countries into health clusters
  clustering:
    gdp_reference_year: 2025  # Reference year for GDP per capita data
    weights:
      geography: 1.0    # Weight for geographic proximity
      gdp: 0.5          # Weight for GDP per capita similarity
      population: 0.3   # Weight for population balance across clusters

Reduce region_clusters or log_rr_points to speed up solving.

The value_per_yll parameter monetizes health impacts in USD_2024 per year of life lost (YLL).

Solver Configuration

solving:
  solver: highs
  # solver: gurobi
  # io_api controls how the model is communicated to the solver:
  # - 'lp' or 'mps': Write problem to file (LP/MPS format) which solver reads
  # - 'direct': Use solver's Python API directly (e.g., gurobipy) for faster performance
  # - null: Use linopy's default (typically 'lp')
  io_api: null
  threads: 1  # Number of threads to use for solving
  # The calculate_fixed_duals option induces linopy to solve the MILP,
  # then fix all integer variables to their optimal values, then solve
  # the resulting LP in order to get dual variables for model
  # constraints.
  calculate_fixed_duals: true
  options_gurobi:
    LogToConsole: 0
    OutputFlag: 1
    Method: 2
    MIPGap: 0.001  # target 0.1% relative optimality gap
  options_highs:
    solver: "choose"
    mip_rel_gap: 0.001  # align relative gap with gurobi setting
  # NetCDF export settings for solved network
  # Passed to xarray.Dataset.to_netcdf via PyPSA; None disables compression.
  netcdf_compression:
    zlib: true
    complevel: 4
Solver choice:
  • HiGHS: Open-source, fast, good for most problems

  • Gurobi: Commercial, often faster for very large problems, requires license (free for academic users)

Plotting Configuration

plotting:
  comparison_scenarios:
  - "scen-default"
  colors:
    crops:
      wheat: "#C58E2D"
      'dryland-rice': "#E0B341"
      'wetland-rice': "#F7E29E"
      maize: "#F1C232"
      barley: "#B68D23"
      oat: "#D4B483"
      rye: "#A67C52"
      sorghum: "#A0522D"
      buckwheat: "#8B5A2B"
      'foxtail-millet': "#E3C878"
      'pearl-millet': "#D9A441"
      soybean: "#7B4F2A"
      'dry-pea': "#B9925B"
      chickpea: "#D7B377"
      cowpea: "#8C5C38"
      gram: "#A47038"
      'phaseolus-bean': "#6E3B1E"
      pigeonpea: "#9C6B3E"
      'white-potato': "#8FB98B"
      'sweet-potato': "#CE7B3A"
      cassava: "#6E8B3D"
      yam: "#4F6F2C"
      tomato: "#C0392B"
      carrot: "#E67E22"
      onion: "#D35400"
      cabbage: "#27AE60"
      banana: "#F7DC6F"
      citrus: "#F39C12"
      coconut: "#8E735B"
      sunflower: "#F1C40F"
      rapeseed: "#F5B041"
      groundnut: "#A8683C"
      sesame: "#C97A2B"
      'oil-palm': "#A04000"
      olive: "#6E7D57"
      sugarcane: "#9B59B6"
      sugarbeet: "#AF7AC5"
      alfalfa: "#1ABC9C"
      'biomass-sorghum': "#16A085"
      grassland: "#7FB77E"
    food_groups:
      whole_grains: "#8C564B"
      grain: "#C49C94"
      fruits: "#E15759"
      vegetables: "#59A14F"
      legumes: "#B07AA1"
      nuts_seeds: "#AA7C51"
      starchy_vegetable: "#F28E2C"
      oil: "#FFBE7D"
      red_meat: "#D62728"
      poultry: "#FF9896"
      dairy: "#9EDAE5"
      eggs: "#FFE377"

  fallback_cmaps:
    crops: "Set3"

Customize visualization colors for publication-quality plots. The colors.food_groups palette is applied consistently across all food-group charts and maps; extend it if you add new groups to data/food_groups.csv.