Food Processing & Trade

Food Processing

Overview

The food processing module converts raw agricultural products (crops and animal products) into final food products consumed by the population. This captures:

  • Multi-output processing: Single crops can produce multiple co-products (e.g., wheat → white flour + bran + germ)

  • Alternative pathways: Different processing options for the same crop (e.g., white flour vs. wholemeal flour from wheat)

  • Mass balance: Processing losses and byproducts are explicitly tracked

  • Unit conversion: Conversion from dry matter (DM) to fresh weight as consumed

Processing is represented in the model as PyPSA multi-output links with crop buses as inputs and multiple food buses as outputs. Each pathway creates one link per country, with efficiencies adjusted for food loss and waste factors.

Data Files

The two files below, created and distributed for internal food-opt use, define possible food processing pathways and food groups.

data/foods.csv

Defines crop-to-food processing pathways using a pathway-based format that supports multi-output processing. Each pathway can convert one crop into one or more food products, with conversion factors maintaining mass balance.

Columns:

  • pathway: Unique identifier for the processing pathway (e.g., white_flour, milled_rice)

  • crop: Input crop name (must match config crops list)

  • food: Output food product name

  • factor: Conversion factor (mass of food output per unit mass of crop input)

  • description: Explanation of the conversion and source reference

Multi-output pathways: Multiple rows with the same pathway name represent co-products from a single processing operation. For example, the white_flour pathway produces white flour (0.75), wheat bran (0.20), and wheat germ (0.03) from wheat, with factors summing to ≤ 1.0 to respect mass balance.

Alternative pathways: Different pathways for the same crop represent processing alternatives that the model can choose between based on demand and costs. For example, wheat can be processed via white_flour or wholemeal_flour pathways.

data/food_groups.csv

Maps foods to food groups for dietary constraint aggregation and health impact assessment. Each food must be assigned to exactly one food group.

Columns:

  • food: Food product name (must match foods produced in data/foods.csv)

  • group: Food group identifier (e.g., grain, whole_grains, legumes, oil, byproduct)

Coverage: This file must include all foods that can be produced according to data/foods.csv pathways, including byproducts (bran, meal, hulls, etc.). Foods without group assignments will generate warnings and will not contribute to food group constraints or health impact calculations.

Food groups: Standard groups include grains, whole_grains, legumes, nuts_seeds, oil, starchy_vegetable, fruits, vegetables, sugar, byproduct, red_meat, poultry, dairy, and eggs. Additional groups can be defined in the config file under food_groups.

Byproduct handling: Foods assigned to the byproduct group (such as wheat bran, rice bran, oat bran, wheat germ, sunflower meal, rapeseed meal, and buckwheat hulls) are excluded from direct human consumption. Instead, these byproducts can be utilized as animal feed (see byproduct-feed-conversion), making them available for livestock production systems.

Food Loss & Waste Adjustments

The workflow incorporates food loss (pre-retail) and food waste (retail & household) adjustments when converting crops to foods. Food loss and waste are measured and tracked by the UN under the Sustainable Development Goal 12.3. The FAO is responsible for preparing data on food loss, whereas the UNEP is responsible for preparing data on food waste. Both are available through a UN Statistics Division API.

  • workflow/scripts/prepare_food_loss_waste.py retrieves * SDG indicator 12.3.1 data (series AG_FLS_PCT and AG_FOOD_WST_PC) from the UN Statistics Division API, using ISO-3 area codes. * Food Balance Sheets data (FBS domain) from FAOSTAT to obtain country-level per-capita food supply.

  • UNSD reports food loss as a percentage. Regional totals (ALP product code) are available for M49 regions, while product-level breakdowns (CRL_PUL, FRT_VGT, RT_TBR, ANMPROD) exist only for the global series. The script therefore: 1. Pulls the latest world loss percentages by product type. 2. Converts them into correction factors by dividing each product share by the global ALP total (e.g. fruits & vegetables ≈ 25 % / 13 % ≈ 1.9). 3. Applies these factors to each country’s regional ALP percentage, yielding group-specific loss fractions for the model food groups.

  • Food waste is reported as kilograms per capita per year. To convert this to a fraction of available food supply, the script retrieves the FAOSTAT FBS Grand Total item (kg/capita/year), converts both to grams/day, and computes waste_fraction = waste_g_day / supply_g_day.

  • The resulting dataset processing/{name}/food_loss_waste.csv lists, for every country and model food group, the derived loss_fraction and waste_fraction.

During build_model the crop→food conversion links multiply the baseline processing efficiency by (1 - loss_fraction) * (1 - waste_fraction) for the relevant country-food group pair. Because all factors are multiplicative (dry matter → fresh mass → edible portion → usable food), their ordering does not affect the final efficiency.

Trade

Overview

The trade module enables inter-regional flows of crops and food products, subject to transport costs.

To avoid creating a complete graph of region-to-region links (entailing \(O(n^2)\) links for \(n\) regions), the model uses a hub-based topology:

  1. Country buses: Each country has local crop/food buses

  2. Hub buses: A small number of hub nodes (configured count)

  3. Hub connections: Regions connect to nearest hubs; hubs connect to each other

This reduces links from \(O(n^2)\) to \(O(n \times h + h^2)\), where \(n\) = regions and \(h\) = hubs.

Configuration

trade:
  crop_hubs: 20
  crop_default_trade_cost_per_km: 1e-2
  crop_trade_cost_categories:
    bulk_dry_goods:
      cost_per_km: 6e-3
      crops:
      - wheat
      - dryland-rice
      - wetland-rice
      - maize
      - soybean
      - barley
      - oat
      - rye
      - dry-pea
      - chickpea
    bulky_fresh:
      cost_per_km: 1.4e-2
      crops:
      - white-potato
      - sweet-potato
      - yam
      - cassava
      - sugarbeet
      - biomass-sorghum
    perishable_high_value:
      cost_per_km: 2.2e-2
      crops:
      - tomato
      - carrot
      - onion
      - cabbage
      - banana
      - sugarcane
      - sunflower
      - rapeseed
      - groundnut
  animal_product_hubs: 20
  animal_product_default_trade_cost_per_km: 2.1e-2
  animal_product_trade_cost_categories:
    chilled_meat:
      cost_per_km: 2.8e-2
      products:
      - cattle meat
      - pig meat
  non_tradable_crops:
    - alfalfa
    - biomass-sorghum
    - silage-maize
  non_tradable_animal_products: []

Trade Cost Categories

Transport costs differentiate by commodity handling requirements:

  • Bulk dry goods: Cereals, legumes in containers/bulk carriers

  • Bulky fresh: Potatoes, cassava, sugar beets

  • Perishable high-value: Fruits, vegetables, sugarcane requiring refrigeration

  • Chilled meat: Temperature-controlled meat transport

Hub Location

Hub positions are determined by k-means clustering on region centroids:

  1. Compute population-weighted centroid for each region

  2. Run k-means with k = configured hub count

  3. Assign each region to nearest hub

  4. Create hub-hub distance matrix for hub-to-hub transport

This ensures hubs are spatially distributed to minimize total transport distance.

Trade network topology

Hub-based trade network showing trade hubs (green circles) and trade links: country-to-hub links (thin) and hub-to-hub links (thick).

Non-Tradable Commodities

Certain products are designated non-tradable:

  • Fodder crops (alfalfa, biomass sorghum): Too bulky/low-value to transport

  • Perishables (optional): Can restrict local consumption of fragile goods

Non-tradable crops must be consumed (as food or feed) within their production region.

Model Implementation

Trade links are created in workflow/scripts/build_model.py:

# Pseudocode
for crop in tradable_crops:
    for region in regions:
        hub = nearest_hub(region)
        n.add("Link",
              f"trade_{crop}_{region}_to_{hub}",
              bus0=f"crop_{crop}_{region}",
              bus1=f"crop_{crop}_hub{hub}",
              p_nom=inf,  # No capacity limit
              marginal_cost=distance * cost_per_km)

    for hub_i, hub_j in hub_pairs:
        n.add("Link",
              f"trade_{crop}_hub{hub_i}_to_hub{hub_j}",
              bus0=f"crop_{crop}_hub{hub_i}",
              bus1=f"crop_{crop}_hub{hub_j}",
              p_nom=inf,
              marginal_cost=hub_distance * cost_per_km)

Similar structure for animal products.