Output functions

get_optimal_capacity

MacroEnergy.get_optimal_capacityFunction
get_optimal_capacity(system::System; scaling::Float64=1.0)

Get the optimal capacity values for all assets/edges in a system.

Arguments

  • system::System: The system containing the assets/edges to analyze
  • scaling::Float64: The scaling factor for the results.

Returns

  • DataFrame: A dataframe containing the optimal capacity values for all assets/edges, with missing columns removed

Example

get_optimal_capacity(system)
153×8 DataFrame
 Row │ commodity    zone     resource_id        component_id            resource_type  component_type      variable   value    
     │ Symbol       Symbol   Symbol             Symbol                  String         String              Symbol     Float64 
─────┼───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
   1 │ Electricity  elec_SE  existing_solar_SE  existing_solar_SE_edge  VRE            Edge{Electricity}   capacity   8.5022
   2 │ Electricity  elec_NE  existing_solar_NE  existing_solar_NE_edge  VRE            Edge{Electricity}   capacity   0.0   
   3 │ Electricity  elec_NE  existing_wind_NE   existing_wind_NE_edge   VRE            Edge{Electricity}   capacity   3.6545
source

get_detailed_costs

MacroEnergy.get_detailed_costsFunction
get_detailed_costs(system::System, settings::NamedTuple; scaling::Float64=1.0)

Collect all detailed costs from the system, returning both discounted and undiscounted DataFrames. Uses period cost attributes (for edges and storages) and economics.jl for discount factors.

Returns a NamedTuple (discounted=df_discounted, undiscounted=df_undiscounted) with the two DataFrames having columns: zone, type, category, value.

source

get_detailed_costs_benders

MacroEnergy.get_detailed_costs_bendersFunction
get_detailed_costs_benders(
    system::System,
    operational_costs::DataFrame,
    settings::NamedTuple;
    scaling::Float64=1.0
)

Combine fixed costs from the planning problem with operational costs from subproblems. Returns a NamedTuple (discounted=df_discounted, undiscounted=df_undiscounted) with the two DataFrames having columns: zone, type, category, value.

source

get_optimal_curtailment

MacroEnergy.get_optimal_curtailmentFunction
get_optimal_curtailment(
    system::System;
    scaling::Float64=1.0
)

Get the optimal curtailment values for all VRE assets in a system.

Curtailment at each timestep t is computed as: capacity(e) × availability(e, t) - flow(e, t)

where capacity is the final installed capacity and flow is the actual generation.

Arguments

  • system::System: The system containing VRE assets to analyze
  • scaling::Float64: Scaling factor for the results (default: 1.0)

Returns

  • DataFrame: Temporal curtailment with columns for commodity, zone, resource_id, resource_type, component_id, component_type, variable, time, value

Example

curtailment_df = get_optimal_curtailment(system)
source

get_optimal_discounted_costs

MacroEnergy.get_optimal_discounted_costsFunction
get_optimal_discounted_costs(model::Union{Model,NamedTuple}; scaling::Float64=1.0)

Extract total discounted costs (fixed, variable, total) from the optimization results and return them as a DataFrame.

source

get_optimal_flow

MacroEnergy.get_optimal_flowFunction
get_optimal_flow(
    system::System; 
    scaling::Float64=1.0, 
    commodity::Union{AbstractString,Vector{<:AbstractString},Nothing}=nothing, 
    asset_type::Union{AbstractString,Vector{<:AbstractString},Nothing}=nothing
)

Get the optimal flow values for all edges in a system.

Filtering

Results can be filtered by:

  • commodity: Specific commodity type(s)
  • asset_type: Specific asset type(s)

Pattern Matching

Two types of pattern matching are supported:

  1. Parameter-free matching:

    • "ThermalPower" matches any ThermalPower{...} type (i.e. no need to specify parameters inside {})
  2. Wildcards using "*":

    • "ThermalPower*" matches ThermalPower{Fuel}, ThermalPowerCCS{Fuel}, etc.
    • "CO2*" matches CO2, CO2Captured, etc.

Arguments

  • system::System: The system containing the all edges to output
  • scaling::Float64: The scaling factor for the results.
  • commodity::Union{AbstractString,Vector{<:AbstractString},Nothing}: The commodity to filter by
  • asset_type::Union{AbstractString,Vector{<:AbstractString},Nothing}: The asset type to filter by

Returns

  • DataFrame: A dataframe containing the optimal flow values for all edges, with missing columns removed

Example

get_optimal_flow(system)
186984×10 DataFrame
    Row │ commodity  zone        resource_id                component_id                       resource_type     component_type     variable  segment  time   value     
        │ Symbol     Symbol      Symbol                     Symbol                             String            String             Symbol    Int64    Int64  Float64
────────┼────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
      1 │ Biomass    bioherb_SE  SE_BECCS_Electricity_Herb  SE_BECCS_Electricity_Herb_biomas…  BECCSElectricity  Edge{Electricity}  flow      1        1      0.0    
      2 │ Biomass    bioherb_SE  SE_BECCS_Electricity_Herb  SE_BECCS_Electricity_Herb_biomas…  BECCSElectricity  Edge{Electricity}  flow      1        2      0.0    
      3 │ Biomass    bioherb_SE  SE_BECCS_Electricity_Herb  SE_BECCS_Electricity_Herb_biomas…  BECCSElectricity  Edge{Electricity}  flow      1        3      0.0    
      ...
# Filter by commodity
get_optimal_flow(system, commodity="Electricity")
# Filter by commodity and asset type using parameter-free matching
get_optimal_flow(system, commodity="Electricity", asset_type="ThermalPower") # only ThermalPower{Fuel} will be returned
# Filter by commodity and asset type using wildcard matching
get_optimal_flow(system, commodity="Electricity", asset_type="ThermalPower*") # all types starting with ThermalPower (e.g., ThermalPower{Fuel}, ThermalPowerCCS{Fuel}) will be returned)
source
get_optimal_flow(asset::AbstractAsset, scaling::Float64=1.0)

Get the optimal flow values for all edges in an asset.

Arguments

  • asset::AbstractAsset: The asset containing the edges to analyze
  • scaling::Float64: The scaling factor for the results.

Returns

  • DataFrame: A dataframe containing the optimal flow values for all edges, with missing columns removed

Example

asset = get_asset_by_id(system, :elec_SE)
get_optimal_flow(asset)
source

get_optimal_new_capacity

MacroEnergy.get_optimal_new_capacityFunction
get_optimal_new_capacity(system::System; scaling::Float64=1.0)

Get the optimal new capacity values for all assets/edges in a system.

Arguments

  • system::System: The system containing the assets/edges to analyze
  • scaling::Float64: The scaling factor for the results.

Returns

  • DataFrame: A dataframe containing the optimal new capacity values for all assets/edges, with missing columns removed

Example

get_optimal_new_capacity(system)
153×7 DataFrame
 Row │ commodity  zone           resource_id                   component_id                       resource_type     component_type     variable       value  
     │ Symbol     Symbol         Symbol                        Symbol                             String            String             Symbol         Float64
─────┼───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
   1 │ Biomass    bioherb_SE     SE_BECCS_Electricity_Herb     SE_BECCS_Electricity_Herb_biomas…  BECCSElectricity  Edge{Electricity}  new_capacity   0.0
   2 │ Biomass    bioherb_MIDAT  MIDAT_BECCS_Electricity_Herb  MIDAT_BECCS_Electricity_Herb_bio…  BECCSElectricity  Edge{Electricity}  new_capacity   0.0
   3 │ Biomass    bioherb_NE     NE_BECCS_Electricity_Herb     NE_BECCS_Electricity_Herb_biomas…  BECCSElectricity  Edge{Electricity}  new_capacity   0.0
source

get_optimal_non_served_demand

MacroEnergy.get_optimal_non_served_demandFunction
get_optimal_non_served_demand(system::System; scaling::Float64=1.0)

Get the optimal non-served demand values for all nodes in a system.

Only nodes that have non-served demand variables are included in the output.

Arguments

  • system::System: The system containing the nodes to analyze
  • scaling::Float64: The scaling factor for the results (default: 1.0)

Returns

  • DataFrame: A dataframe containing the optimal non-served demand values, with columns for commodity, zone, component_id, variable, segment, time, and value. Returns an empty DataFrame if no nodes have NSD variables.

Example

get_optimal_non_served_demand(system)
source
get_optimal_non_served_demand(nodes::Vector{<:Node}, scaling::Float64=1.0)

Get the optimal non-served demand values for a list of nodes.

Arguments

  • nodes::Vector{<:Node}: The nodes to extract NSD values from
  • scaling::Float64: The scaling factor for the results

Returns

  • DataFrame: A dataframe containing the optimal non-served demand values
source
get_optimal_non_served_demand(node::Node, scaling::Float64=1.0)

Get the optimal non-served demand values for a single node.

Arguments

  • node::Node: The node to extract NSD values from
  • scaling::Float64: The scaling factor for the results

Returns

  • DataFrame: A dataframe containing the optimal non-served demand values for the node
source

get_optimal_retired_capacity

MacroEnergy.get_optimal_retired_capacityFunction
get_optimal_retired_capacity(system::System; scaling::Float64=1.0)

Get the optimal retired capacity values for all assets/edges in a system.

Arguments

  • system::System: The system containing the assets/edges to analyze
  • scaling::Float64: The scaling factor for the results.

Returns

  • DataFrame: A dataframe containing the optimal retired capacity values for all assets/edges, with missing columns removed

Example

get_optimal_retired_capacity(system)
153×7 DataFrame
 Row │ commodity  zone           resource_id                   component_id                       resource_type     component_type     variable          value    
     │ Symbol     Symbol         Symbol                        Symbol                             String            String             Symbol            Float64  
─────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
   1 │ Biomass    bioherb_SE     SE_BECCS_Electricity_Herb     SE_BECCS_Electricity_Herb_biomas…  BECCSElectricity  Edge{Electricity}  retired_capacity  0.0
   2 │ Biomass    bioherb_MIDAT  MIDAT_BECCS_Electricity_Herb  MIDAT_BECCS_Electricity_Herb_bio…  BECCSElectricity  Edge{Electricity}  retired_capacity  0.0
   3 │ Biomass    bioherb_NE     NE_BECCS_Electricity_Herb     NE_BECCS_Electricity_Herb_biomas…  BECCSElectricity  Edge{Electricity}  retired_capacity  0.0
source

get_optimal_storage_level

MacroEnergy.get_optimal_storage_levelFunction
get_optimal_storage_level(
    system::System; 
    scaling::Float64=1.0,
    commodity::Union{AbstractString,Vector{<:AbstractString},Nothing}=nothing,
    asset_type::Union{AbstractString,Vector{<:AbstractString},Nothing}=nothing
)

Get the optimal storage level values for all storage units in a system.

Filtering

Results can be filtered by:

  • commodity: Specific commodity type(s)
  • asset_type: Specific asset type(s)

Arguments

  • system::System: The system containing the storage units to analyze
  • scaling::Float64: The scaling factor for the results (default: 1.0)
  • commodity::Union{AbstractString,Vector{<:AbstractString},Nothing}: The commodity to filter by
  • asset_type::Union{AbstractString,Vector{<:AbstractString},Nothing}: The asset type to filter by

Returns

  • DataFrame: A dataframe containing the optimal storage level values, with columns for commodity, zone, resourceid, componentid, resourcetype, componenttype, variable, time, and value.

Example

get_optimal_storage_level(system)
get_optimal_storage_level(system, commodity="Electricity")
get_optimal_storage_level(system, asset_type="Battery")
source
get_optimal_storage_level(asset::AbstractAsset; scaling::Float64=1.0)

Get the optimal storage level values for all storage units in an asset.

Arguments

  • asset::AbstractAsset: The asset containing the storage units to analyze
  • scaling::Float64: The scaling factor for the results

Returns

  • DataFrame: A dataframe containing the optimal storage level values

Example

asset = get_asset_by_id(system, :battery_1)
get_optimal_storage_level(asset)
source
get_optimal_storage_level(
    storages::Vector{<:AbstractStorage}, 
    scaling::Float64=1.0,
    storage_asset_map::Dict{Symbol,Base.RefValue{<:AbstractAsset}}=Dict{Symbol,Base.RefValue{<:AbstractAsset}}()
)

Get the optimal storage level values for a list of storage units.

Arguments

  • storages::Vector{<:AbstractStorage}: The storage units to extract values from
  • scaling::Float64: The scaling factor for the results
  • storage_asset_map::Dict{Symbol,Base.RefValue{<:AbstractAsset}}: Mapping of storage IDs to assets

Returns

  • DataFrame: A dataframe containing the optimal storage level values
source
get_optimal_storage_level(
    storage::AbstractStorage, 
    scaling::Float64=1.0,
    storage_asset_map::Dict{Symbol,Base.RefValue{<:AbstractAsset}}=Dict{Symbol,Base.RefValue{<:AbstractAsset}}()
)

Get the optimal storage level values for a single storage unit.

Arguments

  • storage::AbstractStorage: The storage unit to extract values from
  • scaling::Float64: The scaling factor for the results
  • storage_asset_map::Dict{Symbol,Base.RefValue{<:AbstractAsset}}: Mapping of storage IDs to assets

Returns

  • DataFrame: A dataframe containing the optimal storage level values for the storage unit
source

get_optimal_undiscounted_costs

MacroEnergy.get_optimal_undiscounted_costsFunction
get_optimal_undiscounted_costs(model::Union{Model,NamedTuple}; scaling::Float64=1.0)

Extract total undiscounted costs (fixed, variable, total) from the optimization results and return them as a DataFrame.

source

write_balance_duals

MacroEnergy.write_balance_dualsFunction
write_balance_duals(results_dir::AbstractString, system::System, scaling::Float64=1.0)

Write balance constraint dual values (marginal prices) to CSV file.

Extracts dual values from the :demand balance equation on all nodes and exports them to balance_duals.csv. The dual values are automatically rescaled by subperiod weights to provide proper marginal values per unit of energy/commodity.

Note: Only nodes with a :demand balance equation will be included in the output. Nodes with other balance equations (e.g., :emissions, :co2_storage) are skipped.

Output Format

Wide-format CSV with:

  • Rows: Time steps
  • Columns: Node IDs
  • Values: Rescaled dual values (shadow prices) for the :demand balance equation

Arguments

  • results_dir::AbstractString: Directory where CSV file will be written
  • system::System: The system containing solved balance constraints
  • scaling::Float64: Scaling factor for the dual values (e.g. to account for discounting in multi-period models)

Examples

write_balance_duals("results/", system)
# Creates: results/balance_duals.csv
source

write_capacity

MacroEnergy.write_capacityFunction
write_capacity(
    file_path::AbstractString, 
    system::System; 
    scaling::Float64=1.0, 
    drop_cols::Vector{AbstractString}=String[], 
    commodity::Union{AbstractString,Vector{AbstractString},Nothing}=nothing, 
    asset_type::Union{AbstractString,Vector{AbstractString},Nothing}=nothing
)

Write the optimal capacity results for all assets/edges in a system to a file. The extension of the file determines the format of the file. Capacity, NewCapacity, and RetiredCapacity are first concatenated and then written to the file.

Filtering

Results can be filtered by:

  • commodity: Specific commodity type(s)
  • asset_type: Specific asset type(s)

Pattern Matching

Two types of pattern matching are supported:

  1. Parameter-free matching:

    • "ThermalPower" matches any ThermalPower{...} type (i.e. no need to specify parameters inside {})
  2. Wildcards using "*":

    • "ThermalPower*" matches ThermalPower{Fuel}, ThermalPowerCCS{Fuel}, etc.
    • "CO2*" matches CO2, CO2Captured, etc.

Arguments

  • file_path::AbstractString: The path to the file where the results will be written
  • system::System: The system containing the assets/edges to analyze as well as the settings for the output
  • scaling::Float64: The scaling factor for the results
  • drop_cols::Vector{AbstractString}: Columns to drop from the DataFrame
  • commodity::Union{AbstractString,Vector{AbstractString},Nothing}: The commodity to filter by
  • asset_type::Union{AbstractString,Vector{AbstractString},Nothing}: The asset type to filter by

Returns

  • nothing: The function returns nothing, but writes the results to the file

Example

write_capacity("capacity.csv", system)
# Filter by commodity
write_capacity("capacity.csv", system, commodity="Electricity")
# Filter by commodity and asset type using parameter-free matching
write_capacity("capacity.csv", system, asset_type="ThermalPower")
# Filter by asset type using wildcard matching
write_capacity("capacity.csv", system, asset_type="ThermalPower*")
# Filter by commodity and asset type
write_capacity("capacity.csv", system, commodity="Electricity", asset_type=["ThermalPower", "Battery"])
source

write_co2_cap_duals

MacroEnergy.write_co2_cap_dualsFunction
write_co2_cap_duals(results_dir::AbstractString, system::System, scaling::Float64=1.0)

Write CO2 cap constraint dual values (carbon prices) and penalty costs to CSV file.

Extracts dual values from CO2 cap policy budget constraints and exports them to co2_cap_duals.csv. If slack variables exist, also exports penalty costs.

Output Format

Long-format CSV with columns:

  • Node: Node ID
  • CO2_Shadow_Price: Carbon price (shadow price of the CO2 cap constraint)
  • CO2_Slack: Total penalty cost across subperiods (if slack variables exist)

Arguments

  • results_dir::AbstractString: Directory where CSV file will be written
  • system::System: The system containing solved CO2 cap constraints
  • scaling::Float64: Scaling factor for the dual values (e.g. to account for discounting in multi-period models)

Examples

write_co2_cap_duals("results/", system)
# Creates: results/co2_cap_duals.csv
source

write_costs

MacroEnergy.write_costsFunction
write_costs(
    file_path::AbstractString, 
    system::System, 
    model::Union{Model,NamedTuple}; 
    scaling::Float64=1.0, 
    drop_cols::Vector{AbstractString}=String[]
)

Write the optimal cost results for all assets/edges in a system to a file. The extension of the file determines the format of the file.

Arguments

  • file_path::AbstractString: The path to the file where the results will be written
  • system::System: The system containing the assets/edges to analyze as well as the settings for the output
  • model::Union{Model,NamedTuple}: The optimal model after the optimization
  • scaling::Float64: The scaling factor for the results
  • drop_cols::Vector{AbstractString}: Columns to drop from the DataFrame

Returns

  • nothing: The function returns nothing, but writes the results to the file
source

write_curtailment

MacroEnergy.write_curtailmentFunction
write_curtailment(
    file_path::AbstractString,
    system::System;
    scaling::Float64=1.0,
    drop_cols::Vector{<:AbstractString}=String[]
)

Write the optimal curtailment results for VRE assets to a file.

Output format

Long format with columns for commodity, zone, resource_id, component_id, resource_type, component_type, variable, time, and value (curtailment per timestep). In wide format, values are pivoted by time and resource_id.

Arguments

  • file_path::AbstractString: Path for the curtailment file
  • system::System: The system containing VRE assets to analyze
  • scaling::Float64: Scaling factor for the results (default: 1.0)
  • drop_cols::Vector{<:AbstractString}: Columns to drop from the DataFrame

Returns

  • nothing

Example

write_curtailment(joinpath(results_dir, "curtailment.csv"), system)
source
write_curtailment(
    file_path::AbstractString,
    system::System,
    curtailment_dfs::Vector{DataFrame}
)

Write curtailment results from pre-computed DataFrames to a file.

Used internally by the Benders decomposition workflow when curtailment data is collected from multiple subproblem periods and concatenated before writing.

Arguments

  • file_path::AbstractString: Path for the curtailment file
  • system::System: The system (used for output layout settings)
  • curtailment_dfs::Vector{DataFrame}: Vector of curtailment DataFrames to concatenate and write

Returns

  • nothing
source

write_detailed_costs

MacroEnergy.write_detailed_costsFunction
write_detailed_costs(
    results_dir::AbstractString,
    system::System,
    model::Model,
    settings::NamedTuple;
    scaling::Float64=1.0
)

Write detailed cost breakdown files (both discounted and undiscounted):

  • costs_by_type.csv / undiscounted_costs_by_type.csv
  • costs_by_zone.csv / undiscounted_costs_by_zone.csv

Costs are computed once per discounting mode, then aggregated and written.

Arguments

  • results_dir::AbstractString: Directory to write output files
  • system::System: The system containing assets
  • model::Model: The optimized model (for objective value validation). It should contain four fields: :eDiscountedFixedCost, :eDiscountedVariableCost, :eFixedCost, :eVariableCost.
  • settings::NamedTuple: Case settings containing DiscountRate and PeriodLengths
  • scaling::Float64=1.0: Scaling factor
source

write_detailed_costs_benders

MacroEnergy.write_detailed_costs_bendersFunction
write_detailed_costs_benders(
    results_dir::AbstractString,
    system::System,
    planning_problem_costs::NamedTuple,
    operational_costs_df::Vector{DataFrame},
    settings::NamedTuple;
    scaling::Float64=1.0
)

Write detailed cost breakdown files for Benders decomposition for a single period. Combines fixed costs from the planning problem with operational costs from subproblems.

Arguments

  • results_dir::AbstractString: Directory to write output files
  • system::System: The system (with capacity values from planning solution)
  • benders_costs::NamedTuple: The benders costs (for objective value validation). It should contain four fields: :eDiscountedFixedCost, :eDiscountedVariableCost, :eFixedCost, :eVariableCost.
  • operational_costs_df::Vector{DataFrame}: Operational costs from subproblems for the current period
  • settings::NamedTuple: Case settings
  • scaling::Float64=1.0: Scaling factor
source

write_cost_breakdown_files!

MacroEnergy.write_cost_breakdown_files!Function
write_cost_breakdown_files!(
    results_dir::AbstractString,
    detailed_costs::DataFrame,
    layout::String,
    suffix::String;
    validate_model::Union{Model,NamedTuple,Nothing}=nothing,
    discounted::Bool=false,
    scaling::Float64=1.0
)

Helper function to write cost breakdown files (by type and by zone). Used by both write_detailed_costs and write_detailed_costs_benders.

Arguments

  • results_dir::AbstractString: Directory to write output files
  • detailed_costs::DataFrame: DataFrame with columns: zone, type, category, value
  • layout::String: Output layout ("wide" or "long")
  • prefix::String: Prefix for file names (e.g., "costs" or "undiscounted_costs")
  • validate_model: Optional model for objective value validation. It should contain four fields: :eDiscountedFixedCost, :eDiscountedVariableCost, :eFixedCost, :eVariableCost.
  • discounted::Bool: Whether costs are discounted (for validation)
  • scaling::Float64: Scaling factor (for validation)
source

write_duals

MacroEnergy.write_dualsFunction
write_duals(results_dir::AbstractString, system::System, scaling::Float64=1.0)

Write dual values for all supported constraint types to separate CSV files.

Currently, this function exports dual values for:

  • Balance constraints → balance_duals.csv
  • CO2 cap constraints → co2_cap_duals.csv

Arguments

  • results_dir::AbstractString: Directory where CSV files will be written
  • system::System: The system containing solved constraints with dual values
  • scaling::Float64: Scaling factor for the dual values (e.g. to account for discounting in multi-period models)

Examples

# After solving the model
(case, model) = solve_case(case, optimizer);
system = case.systems[1]; # single period case

# Export all dual values
write_duals("results/", system)

See Also

source

write_flow

MacroEnergy.write_flowFunction
write_flow(
    file_path::AbstractString, 
    system::System; 
    scaling::Float64=1.0, 
    drop_cols::Vector{<:AbstractString}=String[],
    commodity::Union{AbstractString,Vector{<:AbstractString},Nothing}=nothing,
    asset_type::Union{AbstractString,Vector{<:AbstractString},Nothing}=nothing
)

Write the optimal flow results for the system to a file. The extension of the file determines the format of the file.

Filtering

Results can be filtered by:

  • commodity: Specific commodity type(s)
  • asset_type: Specific asset type(s)

Pattern Matching

Two types of pattern matching are supported:

  1. Parameter-free matching:

    • "ThermalPower" matches any ThermalPower{...} type (i.e. no need to specify parameters inside {})
  2. Wildcards using "*":

    • "ThermalPower*" matches ThermalPower{Fuel}, ThermalPowerCCS{Fuel}, etc.
    • "CO2*" matches CO2, CO2Captured, etc.

Arguments

  • file_path::AbstractString: The path to the file where the results will be written
  • system::System: The system containing the edges to analyze as well as the settings for the output
  • scaling::Float64: The scaling factor for the results
  • drop_cols::Vector{<:AbstractString}: Columns to drop from the DataFrame
  • commodity::Union{AbstractString,Vector{<:AbstractString},Nothing}: The commodity to filter by
  • asset_type::Union{AbstractString,Vector{<:AbstractString},Nothing}: The asset type to filter by

Returns

  • nothing: The function returns nothing, but writes the results to the file

Example

write_flow("flow.csv", system)
# Filter by commodity
write_flow("flow.csv", system, commodity="Electricity")
# Filter by commodity and asset type using parameter-free matching
write_flow("flow.csv", system, commodity="Electricity", asset_type="ThermalPower")
# Filter by commodity and asset type using wildcard matching
write_flow("flow.csv", system, commodity="Electricity", asset_type="ThermalPower*")
source

write_non_served_demand

MacroEnergy.write_non_served_demandFunction
write_non_served_demand(
    file_path::AbstractString, 
    system::System; 
    scaling::Float64=1.0, 
    drop_cols::Vector{<:AbstractString}=String[]
)

Write the optimal non-served demand results for all nodes in a system to a file. The extension of the file determines the format of the file (CSV, CSV.GZ, or Parquet).

Only nodes that have non-served demand variables (i.e., nodes with max_nsd != [0.0]) in the input data are included in the output.

Output Format

  • Long format: Includes component_id and segment as separate columns
  • Wide format: Uses compound column names {component_id}_seg{segment} (e.g., elec_node_seg1, elec_node_seg2)

Arguments

  • file_path::AbstractString: The path to the file where the results will be written
  • system::System: The system containing the nodes to analyze
  • scaling::Float64: The scaling factor for the results (default: 1.0)
  • drop_cols::Vector{<:AbstractString}: Columns to drop from the DataFrame (default: empty)

Returns

  • nothing: The function returns nothing, but writes the results to the file

Example

write_non_served_demand("non_served_demand.csv", system)
write_non_served_demand("non_served_demand.csv", system, scaling=1000.0)
source

write_settings

MacroEnergy.write_settingsFunction
write_settings(case::Case, filepath::AbstractString)

Write the case and system settings to a JSON file.

This function extracts the settings from a Case object and writes them to a JSON file. The settings include both case-level settings and system-level settings for all systems in the case.

Arguments

  • case::Case: The case object containing the settings to write
  • filepath::AbstractString: The full path to the output JSON file

Returns

  • nothing: The function returns nothing, but writes the settings to the specified file

Example

# Write settings to a JSON file
write_settings(case, "output/settings.json")

# Write settings to a case results directory
write_settings(case, joinpath(case_path, "settings.json"))

Output Format

The JSON file will contain:

  • case_settings: The case-level settings
  • system_settings: An array of settings for each system in the case
source

write_storage_level

MacroEnergy.write_storage_levelFunction
write_storage_level(
    file_path::AbstractString, 
    system::System; 
    scaling::Float64=1.0, 
    drop_cols::Vector{<:AbstractString}=String[],
    commodity::Union{AbstractString,Vector{<:AbstractString},Nothing}=nothing,
    asset_type::Union{AbstractString,Vector{<:AbstractString},Nothing}=nothing
)

Write the optimal storage level results for all storage units in a system to a file. The extension of the file determines the format of the file (CSV, CSV.GZ, or Parquet).

Filtering

Results can be filtered by:

  • commodity: Specific commodity type(s)
  • asset_type: Specific asset type(s)

Pattern Matching

Two types of pattern matching are supported:

  1. Parameter-free matching:

    • "Battery" matches any Battery type
  2. Wildcards using "*":

    • "*Storage" matches types ending with Storage

Arguments

  • file_path::AbstractString: The path to the file where the results will be written
  • system::System: The system containing the storage units to analyze
  • scaling::Float64: The scaling factor for the results (default: 1.0)
  • drop_cols::Vector{<:AbstractString}: Columns to drop from the DataFrame (default: empty)
  • commodity::Union{AbstractString,Vector{<:AbstractString},Nothing}: The commodity to filter by
  • asset_type::Union{AbstractString,Vector{<:AbstractString},Nothing}: The asset type to filter by

Returns

  • nothing: The function returns nothing, but writes the results to the file

Example

write_storage_level("storage_level.csv", system)
write_storage_level("storage_level.csv", system, commodity="Electricity")
write_storage_level("storage_level.csv", system, asset_type="Battery")
source

write_time_weights

MacroEnergy.write_time_weightsFunction
write_time_weights(file_path, system)

Write a CSV that maps every optimization timestep index to the weight of its representative sub-period.

Weights and TDR

When time-domain reduction (TDR) is used, many full-year sub-periods are clustered into a smaller set of representative periods. The weight of a representative period equals the number of full-year sub-periods it represents, scaled so that the weighted sum of hours equals TotalHoursModeled (typically 8760). All timesteps within the same representative sub-period share the same weight. So, the weight of a timestep is the number of hours that timestep represents when annualizing: multiplying flow(t) × weight(t) and summing over t yields the full-year equivalent.

Weights are normalized so that:

Σ_k  weight(k) × hours_per_subperiod(k)  =  TotalHoursModeled

where hours_per_subperiod(k) is the length of subperiod k in hours.

Without TDR (single representative sub-period), every timestep receives weight 1.0. However, if TotalHoursModeled differs from the sum of timestep durations in the model (e.g., partial-year or leap-year), the same normalization applies: weights scale so the formula above holds, keeping annualization consistent across configurations.

Output columns

  • time — timestep index (1-based integer, matching the time column in other outputs)
  • subperiod_index — index of the representative sub-period that the timestep belongs to
  • weight — weight of the representative sub-period (hours it represents in the full year)

Usage

write_time_weights(joinpath(results_dir, "time_weights.csv"), system)

Weights are used downstream to compute weighted annual sums, e.g. energy revenue:

EnergyRevenue = Σ_t  flow(t) × price(t) × weight(t)
source

write_undiscounted_costs

MacroEnergy.write_undiscounted_costsFunction
write_undiscounted_costs(
    file_path::AbstractString,
    system::System,
    model::Union{Model,NamedTuple};
    scaling::Float64=1.0,
    drop_cols::Vector{AbstractString}=String[]
)

Write the optimal undiscounted cost results (fixed, variable, total) to a file. The extension of the file determines the format (CSV, Parquet, etc.).

Arguments

  • file_path::AbstractString: Path to the output file
  • system::System: The system (for output layout settings)
  • model::Union{Model,NamedTuple}: The optimized model
  • scaling::Float64: Scaling factor for the results
  • drop_cols::Vector{AbstractString}: Columns to drop from the DataFrame
source

write_dataframe

MacroEnergy.write_dataframeFunction
write_dataframe(
    file_path::AbstractString, 
    df::AbstractDataFrame, 
    drop_cols::Vector{<:AbstractString}=String[]
)

Write a DataFrame to a file in the appropriate format based on file extension. Supported formats: .csv, .csv.gz, .parquet

Arguments

  • file_path::AbstractString: Path where to save the file
  • df::AbstractDataFrame: DataFrame to write
  • drop_cols::Vector{<:AbstractString}: Columns to drop from the DataFrame
source

MacroEnergy.write_outputs

MacroEnergy.write_outputs_myopic

MacroEnergy.write_period_outputs

MacroEnergy.write_period_outputsFunction
write_period_outputs(results_dir, period_idx, system, model, settings)

Write all outputs for a single period (one iteration of the Monolithic/Myopic loop). Sets up cost expressions, then writes capacity, costs, flows, NSD, storage, and duals. Used by Monolithic in its loop and by Myopic after setup.

source

Output utility functions

These helpers support cost aggregation, reshaping, and Benders-specific cost extraction.

aggregate_costs_by_type

aggregate_costs_by_zone

aggregate_operational_costs

MacroEnergy.aggregate_operational_costsFunction
aggregate_operational_costs(cost_dfs::Vector{DataFrame})

Aggregate operational costs from multiple subproblems into a single DataFrame. Sums values across subproblems for each (zone, type, category) combination.

Used for Benders decomposition where operational costs is distributed across multiple subproblems.

source

add_total_row!

reshape_costs_wide

get_fixed_costs_benders

MacroEnergy.get_fixed_costs_bendersFunction
get_fixed_costs_benders(system::System, settings::NamedTuple; scaling::Float64=1.0)

Compute fixed costs (Investment, FixedOM) from the planning problem. Returns (discounted=df, undiscounted=df) for Benders decomposition.

source

mkpath_for_period

MacroEnergy.mkpath_for_periodFunction
mkpath_for_period(case_path::AbstractString, num_periods::Int, period_idx::Int)

Return the results directory path for the given period and create it (mkpath). Single-period cases use results, multi-period use results_period_(period_idx).

source

Cost computation helpers

Low-level functions used by get_detailed_costs to compute cost components. Useful for extending or debugging cost logic.

compute_investment_cost

MacroEnergy.compute_investment_costFunction
compute_investment_cost(o::T) where T <: Union{AbstractEdge, AbstractStorage}

Returns (pv, cf)::NTuple{2,Float64}: investment cost discounted to period start (PV) and undiscounted total cash flow (CF) for an edge or storage (computed as cost * new_capacity).

source

compute_fixed_om_cost

MacroEnergy.compute_fixed_om_costFunction
compute_fixed_om_cost(o::T) where T <: Union{AbstractEdge, AbstractStorage}

Returns (pv, cf)::NTuple{2,Float64}: fixed O&M cost discounted to period start (PV) and undiscounted total cash flow (CF) for an edge or storage (computed as cost * capacity).

source

compute_variable_om_cost

MacroEnergy.compute_variable_om_costFunction
compute_variable_om_cost(o::T) where T <: Union{AbstractEdge, AbstractStorage}

Compute variable O&M cost for an edge: sum over time of subperiod_weight * variable_om_cost * flow. Returns a Float64 value.

source

compute_fuel_cost

MacroEnergy.compute_fuel_costFunction
compute_fuel_cost(e::AbstractEdge)

Compute attributed fuel cost for an edge using the postprocessed node price at the start node. Returns a Float64 value.

source

compute_startup_cost

MacroEnergy.compute_startup_costFunction
compute_startup_cost(e::EdgeWithUC)

Compute startup cost for an edge. Only applicable to edges with unit commitment constraints (EdgeWithUC). Returns a Float64 value.

source

compute_nsd_cost

MacroEnergy.compute_nsd_costFunction
compute_nsd_cost(n::Node)

Compute non-served demand (NSD) cost for a node: sum over time of subperiod_weight * price_non_served_demand * non_served_demand. Only applicable to nodes with non-served demand constraints. Returns a Float64 value.

source

compute_supply_cost

MacroEnergy.compute_supply_costFunction
compute_supply_cost(n::Node)

Compute supply cost for a node: sum over time of subperiod_weight * price_supply * supply_flow. Only applicable to nodes with non-zero max_supply. Returns a Float64 value.

source

compute_slack_cost

MacroEnergy.compute_slack_costFunction
compute_slack_cost(n::Node)

Compute unmet policy (slack) cost for a node: sum over time of subperiod_weight * price_unmet_policy * policy_slack_vars. Only applicable to nodes with policy constraints. Returns a Float64 value.

source