PlantAgentsModel Orchestration¶
Overview¶
PlantAgentsModel is the main orchestrator that coordinates all PAM decision-making within a simulation year. It processes plants and plant groups in sequence, issuing commands for technology switches, renovations, closures, and capacity expansions.
The model executes the following workflow each year:
Calculate market conditions (current and future prices of steel and iron)
Evaluate each plant’s furnace groups for potential actions
Evaluate plant groups for plant expansion opportunities
Track and enforce capacity limits (for steel and iron buildout separately)
Role in Simulation¶
PAM is called once per simulation year from simulation.py:
Simulation(bus=bus, economic_model=PlantAgentsModel()).run_simulation()
Before PAM runs:
Trade Module allocates production:
Sets
furnace_group.allocated_volumesSets
furnace_group.utilization_rate
TM-PAM Connector updates costs:
Sets
furnace_group.bill_of_materialsSets
furnace_group.emissions
After PAM runs:
Handlers execute commands:
Update furnace group attributes
Create new furnace groups
Record domain events
Checkpoint saves state:
Serializes all plants, furnace groups, plant groups
Records issued commands for replay
Orchestration Workflow¶
PAM executes the following steps each simulation year:
Step 1: Calculate Market Conditions¶
Establishes the economic environment for all decisions:
Carbon costs: Updates carbon costs for all furnace groups based on current carbon price
Current prices: Extracts market prices from cost curves (frozen for consistent evaluation)
Future price series: Builds time series of expected prices for NPV and COSA calculations (covering construction time + plant lifetime)
Step 2: Evaluate Furnace Group Strategies¶
For each plant (in random order):
Update balances: Aggregates furnace group balances to plant level and resets furnace group balances to zero
Evaluate each furnace group (in random order) using
plant.evaluate_furnace_group_strategy():Renovate: Current technology remains optimal at end-of-life
Switch: Different technology offers superior NPV (accounting for COSA)
Close: Historic losses exceed acceptable threshold
Continue: No action needed
Issue commands: Approved decisions are sent via message bus
Step 3: Evaluate Plant Group Expansions¶
For each plant group (in random order):
Collect total balance: Sums all plant balances to calculate available capital for investment
Evaluate expansion using
plant_group.evaluate_expansion():Assesses all plants and available technologies
Selects the option with highest NPV (using future price forecasts based on demand)
Verifies affordability against accumulated balance
Checks compliance with capacity limits (separate for steel and iron)
Issue commands: Approved expansions are sent via message bus
Step 4: Track and Enforce Capacity Limits¶
Throughout the evaluation process:
Monitors cumulative capacity additions by product type (steel/iron)
Rejects expansion and switch commands that would exceed annual limits
Capacity limits exclude new plants (handled separately by GeospatialModel)
Data Flow and Key Parameters¶
Inputs¶
From Unit of Work (bus.uow)¶
bus.uow.plants.list()→ List of allPlantobjectsEach plant contains
furnace_groupslist
bus.uow.plant_groups.list()→ List of allPlantGroupobjectsAggregated by ownership (parent company)
From Environment (bus.env)¶
Demand Signals:
current_demand→ Current year steel/iron demand (dict by product)iron_demand→ Iron-specific demandfuture_demand(year)→ Demand forecast for future years
Market Data:
extract_price_from_costcurve()→ Derives market prices from cost curvecarbon_costs_for_emissions→ Carbon price time series
Cost Data:
name_to_capex→ CAPEX by technology and regioncapex_renovation_share→ Brownfield cost multipliersindustrial_cost_of_debt→ Interest rates by countryindustrial_cost_of_equity→ Required returns by country
Technology Configuration:
allowed_furnace_transitions→ Valid technology switchesdynamic_feedstocks→ Feedstock options by technologyallowed_techs→ Technologies allowed by year
Constraints:
capacity_limit_steel→ Max steel capacity additions per yearcapacity_limit_iron→ Max iron capacity additions per yearnew_capacity_share_from_new_plants→ Reserved capacity share for new plants
Subsidies (optional):
tech_capex_subsidies→ Capital subsidies by country/technologytech_opex_subsidies→ Operating subsidiestech_debt_subsidies→ Interest rate subsidies
Intermediate Values¶
Price Snapshots:
freeze_market_price→ Market price at start of PAM run (dict by product)Used for balance sheet updates of furnace groups
current_price→ Refreshed price for each evaluationfuture_price→ Future prices based on future demand and current cost curves for all furnace evaluations
Tracking Variables:
counter→ Number of commands issued this yearadded_capacity→ Total capacity added by PAM this year (tracked inbus.env)
Regional Mapping:
region_capex→ CAPEX data indexed by plant’s region, changes as new capacity is added following the learning curve logicrenovation_share→ Renovation cost ratios by technology
Outputs¶
Commands Dispatched¶
All commands sent via bus.handle(cmd):
ChangeFurnaceGroupStatusToSwitchingTechnology→ Scheduling technology switchAllows to produce with the old technology, unitl new technology is installed
ChangeFurnaceGroupTechnology→ Technology switchRenovateFurnaceGroup→ Renovate at end-of-lifeCloseFurnaceGroup→ Shut down unprofitable furnaceAddFurnaceGroup→ Expand capacity at existing plant
Logged Metrics¶
counter→ Total commands issuedbus.env.added_capacity→ Total capacity added
Key Design Patterns¶
1. Snapshot Market Prices¶
Freeze prices at start of PAM run and use frozen prices for all furnace evaluations to prevent mid-run price changes from affecting decisions and ensure all furnace groups evaluate under consistent market conditions.
2. Command-Based Updates¶
All changes issued as commands via message bus to:
Decouple decision-making from state mutation
Enable event sourcing and undo/replay
Handler can enforce validation and logging
Debugging Tips¶
Tracing Decisions¶
Enable logging for decision-making in logging_config.py
ENABLE_FURNACE_GROUP_DEBUG = True # Set to False to disable furnace group debug output
Key Metrics to Monitor¶
Command counts: How many switches, renovations, closures, expansions?
Price stability: Are prices fluctuating year-to-year?
Balance sheet health: What % of plant groups have positive balance?
Capacity utilization: Are new plants being utilized?
Subsidy uptake: How many commands benefited from subsidies?
Common Issues¶
Issue: No technology switches happening
Check: allowed transitions Excel tab allows switches between expected technologies
Check: NPV calculations include correct CAPEX and OPEX
Check: COSA isn’t too high (check remaining debt)
Issue: Expansions not happening
Check: Plant group balances (need positive balance)
Check: New capacity limits reached (increase limits, decrease expansion capacity, or decrease the share of new capacity reserved for new plants)
Check: Future demand forecasts (need growth)