Output functions
MacroEnergy.add_total_row!MacroEnergy.aggregate_costs_by_typeMacroEnergy.aggregate_costs_by_zoneMacroEnergy.aggregate_operational_costsMacroEnergy.compute_fixed_om_costMacroEnergy.compute_fuel_costMacroEnergy.compute_investment_costMacroEnergy.compute_nsd_costMacroEnergy.compute_slack_costMacroEnergy.compute_startup_costMacroEnergy.compute_supply_costMacroEnergy.compute_variable_om_costMacroEnergy.get_detailed_costsMacroEnergy.get_detailed_costs_bendersMacroEnergy.get_fixed_costs_bendersMacroEnergy.get_optimal_capacityMacroEnergy.get_optimal_curtailmentMacroEnergy.get_optimal_discounted_costsMacroEnergy.get_optimal_flowMacroEnergy.get_optimal_new_capacityMacroEnergy.get_optimal_non_served_demandMacroEnergy.get_optimal_retired_capacityMacroEnergy.get_optimal_storage_levelMacroEnergy.get_optimal_undiscounted_costsMacroEnergy.mkpath_for_periodMacroEnergy.reshape_costs_wideMacroEnergy.write_balance_dualsMacroEnergy.write_capacityMacroEnergy.write_co2_cap_dualsMacroEnergy.write_cost_breakdown_files!MacroEnergy.write_costsMacroEnergy.write_curtailmentMacroEnergy.write_dataframeMacroEnergy.write_detailed_costsMacroEnergy.write_detailed_costs_bendersMacroEnergy.write_dualsMacroEnergy.write_flowMacroEnergy.write_non_served_demandMacroEnergy.write_outputsMacroEnergy.write_outputs_myopicMacroEnergy.write_period_outputsMacroEnergy.write_settingsMacroEnergy.write_storage_levelMacroEnergy.write_time_weightsMacroEnergy.write_undiscounted_costs
get_optimal_capacity
MacroEnergy.get_optimal_capacity — Function
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 analyzescaling::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.6545get_detailed_costs
MacroEnergy.get_detailed_costs — Function
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.
get_detailed_costs_benders
MacroEnergy.get_detailed_costs_benders — Function
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.
get_optimal_curtailment
MacroEnergy.get_optimal_curtailment — Function
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 analyzescaling::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)get_optimal_discounted_costs
MacroEnergy.get_optimal_discounted_costs — Function
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.
get_optimal_flow
MacroEnergy.get_optimal_flow — Function
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:
Parameter-free matching:
"ThermalPower"matches anyThermalPower{...}type (i.e. no need to specify parameters inside{})
Wildcards using "*":
"ThermalPower*"matchesThermalPower{Fuel},ThermalPowerCCS{Fuel}, etc."CO2*"matchesCO2,CO2Captured, etc.
Arguments
system::System: The system containing the all edges to outputscaling::Float64: The scaling factor for the results.commodity::Union{AbstractString,Vector{<:AbstractString},Nothing}: The commodity to filter byasset_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)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 analyzescaling::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)get_optimal_new_capacity
MacroEnergy.get_optimal_new_capacity — Function
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 analyzescaling::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.0get_optimal_non_served_demand
MacroEnergy.get_optimal_non_served_demand — Function
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 analyzescaling::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)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 fromscaling::Float64: The scaling factor for the results
Returns
DataFrame: A dataframe containing the optimal non-served demand values
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 fromscaling::Float64: The scaling factor for the results
Returns
DataFrame: A dataframe containing the optimal non-served demand values for the node
get_optimal_retired_capacity
MacroEnergy.get_optimal_retired_capacity — Function
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 analyzescaling::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.0get_optimal_storage_level
MacroEnergy.get_optimal_storage_level — Function
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 analyzescaling::Float64: The scaling factor for the results (default: 1.0)commodity::Union{AbstractString,Vector{<:AbstractString},Nothing}: The commodity to filter byasset_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")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 analyzescaling::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)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 fromscaling::Float64: The scaling factor for the resultsstorage_asset_map::Dict{Symbol,Base.RefValue{<:AbstractAsset}}: Mapping of storage IDs to assets
Returns
DataFrame: A dataframe containing the optimal storage level values
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 fromscaling::Float64: The scaling factor for the resultsstorage_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
get_optimal_undiscounted_costs
MacroEnergy.get_optimal_undiscounted_costs — Function
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.
write_balance_duals
MacroEnergy.write_balance_duals — Function
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
:demandbalance equation
Arguments
results_dir::AbstractString: Directory where CSV file will be writtensystem::System: The system containing solved balance constraintsscaling::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.csvwrite_capacity
MacroEnergy.write_capacity — Function
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:
Parameter-free matching:
"ThermalPower"matches anyThermalPower{...}type (i.e. no need to specify parameters inside{})
Wildcards using "*":
"ThermalPower*"matchesThermalPower{Fuel},ThermalPowerCCS{Fuel}, etc."CO2*"matchesCO2,CO2Captured, etc.
Arguments
file_path::AbstractString: The path to the file where the results will be writtensystem::System: The system containing the assets/edges to analyze as well as the settings for the outputscaling::Float64: The scaling factor for the resultsdrop_cols::Vector{AbstractString}: Columns to drop from the DataFramecommodity::Union{AbstractString,Vector{AbstractString},Nothing}: The commodity to filter byasset_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"])write_co2_cap_duals
MacroEnergy.write_co2_cap_duals — Function
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 IDCO2_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 writtensystem::System: The system containing solved CO2 cap constraintsscaling::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.csvwrite_costs
MacroEnergy.write_costs — Function
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 writtensystem::System: The system containing the assets/edges to analyze as well as the settings for the outputmodel::Union{Model,NamedTuple}: The optimal model after the optimizationscaling::Float64: The scaling factor for the resultsdrop_cols::Vector{AbstractString}: Columns to drop from the DataFrame
Returns
nothing: The function returns nothing, but writes the results to the file
write_curtailment
MacroEnergy.write_curtailment — Function
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 filesystem::System: The system containing VRE assets to analyzescaling::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)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 filesystem::System: The system (used for output layout settings)curtailment_dfs::Vector{DataFrame}: Vector of curtailment DataFrames to concatenate and write
Returns
nothing
write_detailed_costs
MacroEnergy.write_detailed_costs — Function
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 filessystem::System: The system containing assetsmodel::Model: The optimized model (for objective value validation). It should contain four fields: :eDiscountedFixedCost, :eDiscountedVariableCost, :eFixedCost, :eVariableCost.settings::NamedTuple: Case settings containing DiscountRate and PeriodLengthsscaling::Float64=1.0: Scaling factor
write_detailed_costs_benders
MacroEnergy.write_detailed_costs_benders — Function
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 filessystem::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 periodsettings::NamedTuple: Case settingsscaling::Float64=1.0: Scaling factor
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 filesdetailed_costs::DataFrame: DataFrame with columns: zone, type, category, valuelayout::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)
write_duals
MacroEnergy.write_duals — Function
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 writtensystem::System: The system containing solved constraints with dual valuesscaling::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
write_balance_duals: Export balance constraint dualswrite_co2_cap_duals: Export CO2 cap constraint duals
write_flow
MacroEnergy.write_flow — Function
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:
Parameter-free matching:
"ThermalPower"matches anyThermalPower{...}type (i.e. no need to specify parameters inside{})
Wildcards using "*":
"ThermalPower*"matchesThermalPower{Fuel},ThermalPowerCCS{Fuel}, etc."CO2*"matchesCO2,CO2Captured, etc.
Arguments
file_path::AbstractString: The path to the file where the results will be writtensystem::System: The system containing the edges to analyze as well as the settings for the outputscaling::Float64: The scaling factor for the resultsdrop_cols::Vector{<:AbstractString}: Columns to drop from the DataFramecommodity::Union{AbstractString,Vector{<:AbstractString},Nothing}: The commodity to filter byasset_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*")write_non_served_demand
MacroEnergy.write_non_served_demand — Function
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_idandsegmentas 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 writtensystem::System: The system containing the nodes to analyzescaling::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)write_settings
MacroEnergy.write_settings — Function
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 writefilepath::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 settingssystem_settings: An array of settings for each system in the case
write_storage_level
MacroEnergy.write_storage_level — Function
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:
Parameter-free matching:
"Battery"matches anyBatterytype
Wildcards using "*":
"*Storage"matches types ending with Storage
Arguments
file_path::AbstractString: The path to the file where the results will be writtensystem::System: The system containing the storage units to analyzescaling::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 byasset_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")write_time_weights
MacroEnergy.write_time_weights — Function
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) = TotalHoursModeledwhere 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 thetimecolumn in other outputs)subperiod_index— index of the representative sub-period that the timestep belongs toweight— 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)write_undiscounted_costs
MacroEnergy.write_undiscounted_costs — Function
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 filesystem::System: The system (for output layout settings)model::Union{Model,NamedTuple}: The optimized modelscaling::Float64: Scaling factor for the resultsdrop_cols::Vector{AbstractString}: Columns to drop from the DataFrame
write_dataframe
MacroEnergy.write_dataframe — Function
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 filedf::AbstractDataFrame: DataFrame to writedrop_cols::Vector{<:AbstractString}: Columns to drop from the DataFrame
MacroEnergy.write_outputs
MacroEnergy.write_outputs — Function
Write results when using Monolithic as solution algorithm.
Write results when using Benders as solution algorithm.
MacroEnergy.write_outputs_myopic
MacroEnergy.write_outputs_myopic — Function
Write results when using Myopic as solution algorithm.
MacroEnergy.write_period_outputs
MacroEnergy.write_period_outputs — Function
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.
Output utility functions
These helpers support cost aggregation, reshaping, and Benders-specific cost extraction.
aggregate_costs_by_type
MacroEnergy.aggregate_costs_by_type — Function
aggregate_costs_by_type(costs_df::DataFrame)Aggregate detailed costs by asset type. Returns a DataFrame with one row per (type, category) combination.
aggregate_costs_by_zone
MacroEnergy.aggregate_costs_by_zone — Function
aggregate_costs_by_zone(costs_df::DataFrame)Aggregate detailed costs by zone. Returns a DataFrame with one row per (zone, category) combination.
aggregate_operational_costs
MacroEnergy.aggregate_operational_costs — Function
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.
add_total_row!
MacroEnergy.add_total_row! — Function
add_total_row!(df::DataFrame, group_col::Symbol)Add Total row and Total column to the DataFrame.
reshape_costs_wide
MacroEnergy.reshape_costs_wide — Function
reshape_costs_wide(df::DataFrame, group_col::Symbol)Reshape cost DataFrame from long to wide format.
get_fixed_costs_benders
MacroEnergy.get_fixed_costs_benders — Function
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.
mkpath_for_period
MacroEnergy.mkpath_for_period — Function
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).
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_cost — Function
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).
compute_fixed_om_cost
MacroEnergy.compute_fixed_om_cost — Function
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).
compute_variable_om_cost
MacroEnergy.compute_variable_om_cost — Function
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.
compute_fuel_cost
MacroEnergy.compute_fuel_cost — Function
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.
compute_startup_cost
MacroEnergy.compute_startup_cost — Function
compute_startup_cost(e::EdgeWithUC)Compute startup cost for an edge. Only applicable to edges with unit commitment constraints (EdgeWithUC). Returns a Float64 value.
compute_nsd_cost
MacroEnergy.compute_nsd_cost — Function
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.
compute_supply_cost
MacroEnergy.compute_supply_cost — Function
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.
compute_slack_cost
MacroEnergy.compute_slack_cost — Function
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.