Costs Output

Contents

Overview | System-Level Cost Files | Breakdown Cost Files | Cost Categories | Configuration | Assumptions | Examples | See Also

Overview

Files: costs.csv, undiscounted_costs.csv, costs_by_type.csv, costs_by_zone.csv, undiscounted_costs_by_type.csv, undiscounted_costs_by_zone.csv

Macro writes six cost output files after each solve. Together they provide a complete picture of the system's total expenditure, broken down by discounting status, cost category, asset type, and zone.

  • costs.csv and undiscounted_costs.csv — system-wide totals (three rows: fixed, variable, total)
  • costs_by_type.csv and undiscounted_costs_by_type.csv — breakdown by asset type and cost category
  • costs_by_zone.csv and undiscounted_costs_by_zone.csv — breakdown by zone and cost category

All values represent costs for the entire modeled period, not annualized values.

System-Level Cost Files

costs.csv (discounted) and undiscounted_costs.csv

These files contain exactly three rows — one for fixed costs, one for variable costs, and one for the total. All metadata columns are set to "all" or "missing" because the values are system-wide aggregates with no per-asset or per-zone breakdown.

ColumnTypeDescription
typeStringAlways "Cost"
variableStringCost type: see table below
valueFloat64Cost value in the model's monetary units

variable values in costs.csv (discounted)

variableDescription
DiscountedFixedCostPresent value of all fixed costs (investment annuities + fixed O&M)
DiscountedVariableCostPresent value of all variable costs (variable O&M, fuel, startup, NSD penalties)
DiscountedTotalCostSum of DiscountedFixedCost + DiscountedVariableCost (the objective value)

variable values in undiscounted_costs.csv

variableDescription
FixedCostUndiscounted total fixed costs
VariableCostUndiscounted total variable costs
TotalCostSum of FixedCost + VariableCost

Breakdown Cost Files

costs_by_type.csv and undiscounted_costs_by_type.csv

These files break costs down by asset type (e.g., ThermalPower{NaturalGas}, Battery, VRE) and cost category (e.g., Investment, Fuel). A final row with type = "Total" gives the system-wide sum for each category.

ColumnTypeDescription
typeStringAsset type string, or "Total" for the system-wide sum
categoryStringCost category (see Cost Categories)
valueFloat64Cost value for this asset type and category

costs_by_zone.csv and undiscounted_costs_by_zone.csv

These files break costs down by zone (location) and cost category. A final row with zone = "Total" gives the system-wide sum.

ColumnTypeDescription
zoneStringZone (location) name, or "Total" for the system-wide sum
categoryStringCost category (see Cost Categories)
valueFloat64Cost value for this zone and category
Discounted vs. undiscounted breakdown files

costs_by_type.csv and costs_by_zone.csv report discounted costs. Their undiscounted counterparts (undiscounted_costs_by_type.csv, undiscounted_costs_by_zone.csv) have the same structure but values are not discounted to present value.

Cost Categories

The category column in the breakdown files takes one of the following values. The same category names appear in both the discounted (costs_by_type.csv, costs_by_zone.csv) and undiscounted (undiscounted_costs_by_type.csv, undiscounted_costs_by_zone.csv) files — the distinction is in how the values are computed, not in the category names themselves.

Internally, Macro splits categories into two groups that use different discounting multipliers.

Fixed cost categories:

CategoryDiscountedUndiscounted
InvestmentAnnualized capital expenditure (CAPEX), amortized over the capital recovery period via CRF and then present-valued over the years the asset operates within the modeling horizonLump-sum CAPEX multiplied by the capital recovery factor (CRF) to annualize it, then multiplied by the number of years the asset operates within the modeling horizon (up to the CRP)
FixedOMAnnual fixed operations and maintenance cost, present-valued over the period lengthAnnual fixed operations and maintenance cost multiplied by the period length

Variable operating cost categories

All follow the same discounting structure. For each category, $C$ is the category-specific annualized cost computed from the representative time steps: $C = \sum_t w_t \times \text{unit cost} \times \text{flow}_t$, where $w_t$ is the subperiod weight and the unit cost varies by category (see description table below). The discounted value is $C$ multiplied by the present-value annuity factor (PVAF) for the period length and then multiplied by the period discount factor (DF). The undiscounted value is simply $C$ multiplied by the period length.

CategoryDescription
VariableOMCost proportional to energy output, using the variable O&M rate per unit of flow
FuelCost of fuel consumed, using the fuel price per unit of fuel flow
StartupCost of starting thermal generators, using the startup cost per start event
NonServedDemandPenalty for unmet demand, using the non-served demand penalty parameter (not actual expenditure — an optimization penalty to incentivize meeting demand)
SupplyCost of purchasing commodity supply from nodes with an associated supply price
UnmetPolicyPenaltyPenalty for violating policy constraints such as renewable portfolio standards or emission caps with slack variables (not actual expenditure — an optimization penalty)

Notation and Formulas

The following notation is used throughout:

SymbolMeaning
$DR$Discount rate (DiscountRate in case_settings.json)
$\lvert I \rvert$Total number of planning periods
$L_i$Period length in years for period $i$ (PeriodLengths[i] in case_settings.json)
$N_i$Years from the case start year to the start of period $i$: $N_i = \sum_{s=1}^{i-1} L_s$
$M_i$Total years from the start of period $i$ to the end of the last period — i.e., $M_i = \sum_{s=i}^{\lvert I \rvert} L_s$
$\text{CRP}$Capital Recovery Period — the number of years over which the capital investment is amortized into annual payments, set per component via capital_recovery_period in the input data (may differ from the physical asset lifetime)
$\text{CRF}$Capital Recovery Factor — converts a lump-sum CAPEX into an equivalent annual payment: $\text{CRF}(DR, N) = \dfrac{DR}{1-(1+DR)^{-N}}$

The present-value annuity factor (PVAF) converts a stream of $N$ annual payments into a present value at year 0:

\[\text{PVAF}(DR, N) = \sum_{t=1}^{N} \frac{1}{(1+DR)^t} = \frac{1-(1+DR)^{-N}}{DR} = \frac{1}{\text{CRF}(DR, N)}\]

The period discount factor brings a value at the start of period $p$ back to the case base year:

\[\text{DF}(DR, N_i) = \frac{1}{(1+DR)^{N_i}}\]

Note: when $DR = 0$, $\text{PVAF}(0, N) = N$, $\text{CRF}(0, N) = 1/N$, and $\text{DF}(0, N_i) = 1$.

Given these definitions, the cost categories are computed as follows:

Fixed cost categories:

CategoryDiscounted valueUndiscounted value
Investment$\text{CAPEX} \times \text{CRF}(DR, \text{CRP}) \times \text{PVAF}(DR,\, \min(\text{CRP},\, M_i)) \times \text{DF}(DR, N_i)$$\text{CAPEX} \times \text{CRF}(DR, \text{CRP}) \times \min(\text{CRP},\, M_i)$
FixedOMannual fixed O&M $\times\, \text{PVAF}(DR, L_i) \times \text{DF}(DR, N_i)$annual fixed O&M $\times\, L_i$
Myopic investment cost reporting

In a myopic run, the optimizer only considers investment costs over the current period length $L_i$ (i.e., it uses $\min(\text{CRP}, L_i)$ rather than $\min(\text{CRP}, M_i)$). However, before writing outputs, Macro adds back the annuities that the myopic optimizer did not account for, so that the reported investment cost uses $\min(\text{CRP}, M_i)$ in both myopic and perfect-foresight runs. This adjustment enables direct cost comparisons across solution algorithms. See Multi-Period Accounting for details.

Variable cost categories:

CategoryDiscounted valueUndiscounted value
VariableOM$C \times \text{PVAF}(DR, L_i) \times \text{DF}(DR, N_i)$$C \times L_i$
Fuel$C \times \text{PVAF}(DR, L_i) \times \text{DF}(DR, N_i)$$C \times L_i$
Startup$C \times \text{PVAF}(DR, L_i) \times \text{DF}(DR, N_i)$$C \times L_i$
NonServedDemand$C \times \text{PVAF}(DR, L_i) \times \text{DF}(DR, N_i)$$C \times L_i$
Supply$C \times \text{PVAF}(DR, L_i) \times \text{DF}(DR, N_i)$$C \times L_i$
UnmetPolicyPenalty$C \times \text{PVAF}(DR, L_i) \times \text{DF}(DR, N_i)$$C \times L_i$
Zero-value categories

Categories with zero total cost are still included in the output. Not all categories will be non-zero for every system — for example, a system with no unit-commitment assets will have Startup = 0 throughout.

Configuration

SettingFileDefaultEffect
OutputLayout (or OutputLayout.Costs)macro_settings.json"long"Set to "wide" to pivot all cost files. For costs.csv/undiscounted_costs.csv, the three rows become a single row with variable names as columns. For the breakdown files, cost categories are pivoted to columns and a Total column is added.

Assumptions

  • Period-wide totals, not annualized. All cost values represent total costs over the entire modeled period, not an annual rate. In a single-period model this is typically one year. In a multi-period model, costs for period N represent the costs incurred over the length of that period (see PeriodLengths in case_settings.json).
  • Discounting. Discounted costs in costs.csv are present values referenced to the base year of the model. The discount rate is the DiscountRate field in case_settings.json. See Multi-Period Accounting for the full discounting formula.
  • Objective value. DiscountedTotalCost in costs.csv equals the value of the optimization objective function (modulo any numerical scaling applied internally). Minimizing this quantity is what the optimizer does.
  • Units. Costs are in the monetary units used in your input data (typically $). Macro does not enforce a specific currency; it is the user's responsibility to ensure consistent units across all input cost parameters.
  • Non-served demand costs. These are counted as variable costs in the system-level files and appear as the NonServedDemand category in the breakdown files. They are a penalty, not actual expenditure — they incentivize the optimizer to meet demand.
  • Supply costs. Costs associated with commodity supply (e.g., natural gas supply nodes with a price) appear under the Supply category.

Examples

costs.csv (wide format, typical single-period model)

With OutputLayout.Costs = "wide", the three rows are pivoted into a single row with one column per cost variable:

DiscountedFixedCostDiscountedVariableCostDiscountedTotalCost
2.929e111.719e114.649e11

The metadata columns (case_name, commodity, zone, resource_id, component_id, type, year) are dropped automatically in wide format.

undiscounted_costs.csv (wide format)

FixedCostVariableCostTotalCost
2.929e111.719e114.649e11

costs_by_type.csv (default long format)

typecategoryvalue
ThermalPower{NaturalGas}Investment1.20e10
ThermalPower{NaturalGas}FixedOM8.50e9
ThermalPower{NaturalGas}Fuel3.10e10
BatteryInvestment5.40e10
BatteryFixedOM2.10e9
VREInvestment7.80e10
VREFixedOM1.30e9
TotalInvestment2.54e11
TotalFuel3.10e10

costs_by_type.csv (wide format)

With OutputLayout.Costs = "wide", cost categories are pivoted to columns and a Total column is added:

typeInvestmentFixedOMVariableOMFuelStartupNonServedDemandSupplyUnmetPolicyPenaltyTotal
ThermalPower{NaturalGas}1.20e108.50e90.03.10e100.00.00.00.05.15e10
Battery5.40e102.10e90.00.00.00.00.00.05.61e10
VRE7.80e101.30e90.00.00.00.00.00.07.93e10
Total2.54e111.19e100.03.10e100.00.00.00.02.97e11

costs_by_zone.csv (default long format)

zonecategoryvalue
SEInvestment9.80e10
SEFixedOM3.20e9
MIDATInvestment8.10e10
NEInvestment7.60e10
TotalInvestment2.54e11

costs_by_zone.csv (wide format)

zoneInvestmentFixedOMVariableOMFuelStartupNonServedDemandSupplyUnmetPolicyPenaltyTotal
SE9.80e103.20e90.01.20e100.00.00.00.01.13e11
MIDAT8.10e104.50e90.01.10e100.00.00.00.09.65e10
NE7.60e104.20e90.08.00e90.00.00.00.08.82e10
Total2.54e111.19e100.03.10e100.00.00.00.02.97e11

Reading Costs in Julia

using CSV, DataFrames

costs = CSV.read("results/costs.csv", DataFrame)
costs_by_type = CSV.read("results/costs_by_type.csv", DataFrame)

# Get total investment cost
total_investment = costs_by_type[costs_by_type.type .== "Total" .&& costs_by_type.category .== "Investment", :value][1]

See Also