Cost Calculation Module¶
Overview¶
The Cost Calculation Module (calculate_costs.py) is the financial analysis engine of the Plant Agent Model (PAM). It bridges detailed cost data (materials, energy, labor) with high-level economic assessments (NPV, LCOH, COSA) to enable informed decision-making:
Operational decisions: What to produce and at what cost, by computing unit production costs combining OPEX, carbon costs, and debt repayment for each furnace group.
Investment decisions: Whether to build new capacity and where, by evaluating business opportunities for capacity expansion through comprehensive NPV analysis.
Technology decisions: When to switch to cleaner or cheaper technologies, by calculating NPV and stranded asset costs (COSA) for all technology options.
Strategic decisions: How subsidies and carbon costs affect competitiveness, by modeling time-varying CAPEX, OPEX, and debt subsidies alongside carbon pricing impacts.
All calculations are normalized to per-unit-of-production basis, support time-varying subsidies, and account for debt financing with straight-line amortization, making this module central to realistic steel industry economic modeling.
Functional Modules¶
Subsidy Management¶
Handles time-varying subsidies for CAPEX, OPEX, and cost of debt. Subsidies are automatically filtered by year and applied to reduce costs. Bounds checking prevents costs from going negative or below floors (CAPEX/OPEX ≥ 0, Cost_of_Debt ≥ Risk_Free_Rate).
Three types of subsidies are supported:
CAPEX subsidies: Reduce upfront investment costs
Absolute: Fixed dollar amount per unit (e.g., $50/t)
Relative: Percentage reduction (e.g., 20% off)
OPEX subsidies: Reduce operating costs
Absolute: Fixed dollar amount per unit (e.g., $10/t)
Relative: Percentage reduction (e.g., 15% off)
Cost of Debt subsidies: Reduce interest rates
Absolute only: Percentage point reduction (e.g., -2% points)
Relative subsidies are ignored for debt
All subsidies are time-bound with start_year and end_year, automatically filtered each simulation year.
Functions:
filter_active_subsidies()- Filters subsidies to only those active in the current yearcalculate_capex_with_subsidies()- Applies absolute and relative subsidies to CAPEXcalculate_opex_with_subsidies()- Applies absolute and relative subsidies to OPEX (floor at 0)calculate_opex_list_with_subsidies()- Generates time-varying OPEX with year-specific subsidiescalculate_debt_with_subsidies()- Cost-of-debt subsidies; absolute point reductions only; floored at risk-free rate
Cost Breakdown Analysis¶
Extracts and processes bills of materials (BOM) to accurately assess the material and energy costs associated with production. Returns nested dictionaries with cost breakdowns by output product or feedstock.
Functions:
calculate_cost_breakdown()- Calculates normalized cost breakdown per unit of productioncalculate_cost_breakdown_by_feedstock()- Detailed cost breakdown for each feedstock option
Operating Expenditure Calculation¶
Computes both variable and fixed operating expenditures based on input cost data and capital investment ratios. Returns 0 for unit costs when utilization is zero (no production means no unit cost).
Functions:
calculate_variable_opex()- Weighted average of material and energy costs. Accepts flexible input formats: unit_cost as float or{"Value": float, "Unit": str}, and usesdemandordemand_share_pctfor weighting.calculate_unit_total_opex()- Sums variable and fixed OPEX per unit (returns 0 if utilization is 0)calculate_unit_production_cost()- Total unit cost including OPEX, carbon costs, debt repayment, and secondary output adjustmentscalculate_cost_adjustments_from_secondary_outputs()- Computes average per-unit cost adjustment from secondary outputs (by-products)
Debt Repayment¶
Generates debt repayment schedules using straight-line amortization (constant principal, declining interest) and calculates debt payment breakdowns.
The module uses straight-line amortization with constant principal and declining interest:
Principal is repaid equally each year:
Principal = Total Debt / LifetimeInterest is calculated on average debt balance:
Interest = ((Debt_Start + Debt_End) / 2) × Cost_of_DebtTotal repayment = Principal + Interest (declines over time as debt decreases)
This method differs from annuity loans where payments are constant. Here, payments start higher and decrease over time.
Functions:
calculate_debt_repayment()- Generates full yearly debt repayment schedulecalculate_current_debt_repayment()- Calculates single year’s debt paymentcalculate_debt_report()- Breaks down debt into principal and interest components for reporting
Note: years_elapsed is 1-indexed (first operational year = 1). If lifetime_expired is True or debt == 0, returns 0.
Cash Flow Analysis¶
Calculates cash flows over time for profitability analysis and stranded asset cost calculations. Validates array lengths before operations and raises ValueError for mismatches (fail-fast for data consistency).
Functions:
calculate_gross_cash_flow()- Cash flow as (Revenue - OPEX) per periodcalculate_net_cash_flow()- Subtracts debt from gross cash flow (for NPV)calculate_lost_cash_flow()- Adds debt to gross cash flow (for COSA)
Note: If unit OPEX == 0 for a period, the model assumes no production and sets cash flow to 0 (revenue is not realized).
Investment Evaluation - NPV¶
Provides tools to calculate net present value (NPV) for technology investments, supporting both individual and batch calculations for business opportunities. Returns -1e9 for invalid inputs (NaN values in cash flows or cost_of_equity ≤ -1.0) to signal non-viability.
Functions:
calculate_npv_costs()- Basic NPV from equity investor perspectivecalculate_npv_full()- Comprehensive NPV with construction lag, carbon costs, and infrastructurecalculate_business_opportunity_npvs()- Batch NPV calculation for multiple sites/technologies
Note: construction_time years are prepended to OPEX and debt schedules. price_series must include the same lead periods so its length matches the lagged OPEX/debt.
Stranded Asset Analysis¶
Calculates the Cost of Stranded Asset (COSA) when switching technologies before end-of-life, accounting for remaining debt obligations and foregone operating profits.
When switching technologies before end-of-life, COSA represents the NPV of:
Remaining debt obligations - Must still be repaid even if asset is abandoned
Foregone operating profits - Lost future revenue from current technology
Formula: COSA = NPV(Gross_Cash_Flow + Remaining_Debt)
COSA is subtracted from the NPV of the new technology to determine if a switch is economically viable.
Functions:
calculate_cost_of_stranded_asset()- NPV of losses from stranding an assetstranding_asset_cost()- Full COSA calculation combining debt obligations and foregone profits
CAPEX Calculations¶
Handles capital expenditure calculations including subsidies and learning-by-doing cost reductions. Returns 1.0 (no reduction) when capacity_zero is 0 to avoid division by zero.
Functions:
calculate_capex_with_subsidies()- Applies absolute and relative subsidies to CAPEXcalculate_capex_reduction_rate()- Learning-by-doing cost reductions based on the learning curve logic (the more mature a technology, the cheaper its unit cost). The learning coefficient for CAPEX reduction is set to ca. -0.04, which corresponds to approximately 7% cost reduction per doubling of capacity.
Hydrogen Cost Calculations¶
Calculates levelized cost of hydrogen (LCOH) with regional ceilings and intraregional trade options.
For hydrogen-based technologies, LCOH represents the full cost of hydrogen production:
LCOH ($/kg) = Energy_Consumption (kWh/kg) × Electricity_Price ($/kWh) + CAPEX_OPEX ($/kg)
The module supports:
Regional hydrogen price ceilings (percentile-based)
Intraregional hydrogen trade with transport costs
Country-level LCOH calculations with electrolyzer efficiency curves
Note: This LCOH calculation is analogous to the one used in the geospatial model, but based on grid power prices instead of custom renewable energy costs. Functions are duplicated in geospatial_calculations.py and changes must be manually synchronized between both locations. See Priority Location Selection for more details on ceilings and trade.
Functions:
calculate_lcoh_from_electricity_country_level()- LCOH per country from electricity pricescalculate_regional_hydrogen_ceiling_country_level()- Regional hydrogen price ceilings (percentile-based)apply_hydrogen_price_cap_country_level()- Applies price caps with intraregional trade options
Note: Functions are duplicated in geospatial_calculations.py and changes must be manually synchronized between both locations.
Energy and Reductant Selection¶
Identifies the most cost-effective reductant across different production paths based on energy costs.
Functions:
calculate_energy_costs_and_most_common_reductant()- Identifies most cost-effective reductant across all metallic inputs
Cross-Cutting Solutions¶
Cost Normalization¶
All costs normalized to per-unit-of-production basis for comparability
Handles both absolute ($/t) and relative (%) subsidies
Ensures minimum values: CAPEX/OPEX ≥ 0, Cost_of_Debt ≥ Risk_Free_Rate
Time Series Treatment¶
Construction time lags: Prepends zeros to schedules for projects under construction
Year-specific subsidies: Automatically filters subsidies active in each year
Variable time horizons: Supports both full lifetime and remaining lifetime calculations
Edge Case Handling¶
Zero utilization → Returns 0 for unit costs (no production means no unit cost)
Invalid NPV inputs → Returns -1e9 (large negative value signals non-viability)
Missing data → Returns 0 or empty collections (graceful degradation)
Array length mismatches → Raises ValueError (fail-fast for data consistency)
Integration Points¶
This module is called by the following PAM components:
Caller |
Function Used |
Purpose |
|---|---|---|
|
|
Technology switching NPV analysis |
|
|
Current operating cost |
|
|
Debt schedule for COSA calculations |
|
|
New plant investment analysis |
|
|
Hydrogen cost modeling |