Reference
TulipaEnergyModel.EnergyProblem
TulipaEnergyModel.GraphAssetData
TulipaEnergyModel.GraphFlowData
TulipaEnergyModel.Group
TulipaEnergyModel.ModelParameters
TulipaEnergyModel.RepresentativePeriod
TulipaEnergyModel.Timeframe
TulipaEnergyModel.Year
TulipaEnergyModel._check_initial_storage_level!
TulipaEnergyModel._construct_inter_rp_dataframes
TulipaEnergyModel._get_graph_asset_or_flow
TulipaEnergyModel._interpolate_storage_level!
TulipaEnergyModel._parse_rp_partition
TulipaEnergyModel.add_expression_is_charging_terms_intra_rp_constraints!
TulipaEnergyModel.add_expression_terms_inter_rp_constraints!
TulipaEnergyModel.add_expression_terms_intra_rp_constraints!
TulipaEnergyModel.add_expression_units_on_terms_intra_rp_constraints!
TulipaEnergyModel.add_group_constraints!
TulipaEnergyModel.add_ramping_constraints!
TulipaEnergyModel.calculate_annualized_cost
TulipaEnergyModel.calculate_salvage_value
TulipaEnergyModel.calculate_weight_for_investment_discounts
TulipaEnergyModel.calculate_weight_for_investment_discounts
TulipaEnergyModel.compute_assets_partitions!
TulipaEnergyModel.compute_constraints_partitions
TulipaEnergyModel.compute_dual_variables
TulipaEnergyModel.compute_flows_partitions!
TulipaEnergyModel.compute_rp_partition
TulipaEnergyModel.construct_dataframes
TulipaEnergyModel.create_internal_structures
TulipaEnergyModel.create_intervals_for_years
TulipaEnergyModel.create_model
TulipaEnergyModel.create_model!
TulipaEnergyModel.default_parameters
TulipaEnergyModel.filter_graph
TulipaEnergyModel.get_graph_value_or_missing
TulipaEnergyModel.profile_aggregation
TulipaEnergyModel.read_parameters_from_file
TulipaEnergyModel.run_scenario
TulipaEnergyModel.safe_comparison
TulipaEnergyModel.safe_inclusion
TulipaEnergyModel.save_solution_to_file
TulipaEnergyModel.save_solution_to_file
TulipaEnergyModel.solve_model
TulipaEnergyModel.solve_model!
TulipaEnergyModel.solve_model!
TulipaEnergyModel.EnergyProblem
— TypeStructure to hold all parts of an energy problem. It is a wrapper around various other relevant structures. It hides the complexity behind the energy problem, making the usage more friendly, although more verbose.
Fields
graph
: The Graph object that defines the geometry of the energy problem.representative_periods
: A vector of Representative Periods.constraints_partitions
: Dictionaries that connect pairs of asset and representative periods to time partitions (vectors of time blocks)timeframe
: The number of periods of therepresentative_periods
.dataframes
: The data frames used to linearize the variables and constraints. These are used internally in the model only.groups
: The input data of the groups to create constraints that are common to a set of assets in the model.model_parameters
: The model parameters.model
: A JuMP.Model object representing the optimization model.solved
: A boolean indicating whether themodel
has been solved or not.objective_value
: The objective value of the solved problem.termination_status
: The termination status of the optimization model.timings
: Dictionary of elapsed time for various parts of the code (in seconds).
Constructor
EnergyProblem(connection)
: Constructs a newEnergyProblem
object with the given connection. Theconstraints_partitions
field is computed from therepresentative_periods
, and the other fields are initialized with default values.
See the basic example tutorial to see how these can be used.
TulipaEnergyModel.GraphAssetData
— TypeStructure to hold the asset data in the graph.
TulipaEnergyModel.GraphFlowData
— TypeStructure to hold the flow data in the graph.
TulipaEnergyModel.Group
— TypeStructure to hold the group data
TulipaEnergyModel.ModelParameters
— TypeModelParameters(;key = value, ...)
ModelParameters(path; ...)
ModelParameters(connection; ...)
ModelParameters(connection, path; ...)
Structure to hold the model parameters. Some values are defined by default and some required explicit definition.
If path
is passed, it is expected to be a string pointing to a TOML file with a key = value
list of parameters. Explicit keyword arguments take precedence.
If connection
is passed, the default discount_year
is set to the minimum of all milestone years. In other words, we check for the table year_data
for the column year
where the column is_milestone
is true. Explicit keyword arguments take precedence.
If both are passed, then path
has preference. Explicit keyword arguments take precedence.
Parameters
discount_rate::Float64 = 0.0
: The model discount rate.discount_year::Int
: The model discount year.
TulipaEnergyModel.RepresentativePeriod
— TypeStructure to hold the data of one representative period.
TulipaEnergyModel.Timeframe
— TypeStructure to hold the data of the timeframe.
TulipaEnergyModel.Year
— TypeStructure to hold the data of the year.
TulipaEnergyModel._check_initial_storage_level!
— Method_check_initial_storage_level!(df)
Determine the starting value for the initial storage level for interpolating the storage level. If there is no initial storage level given, we will use the final storage level. Otherwise, we use the given initial storage level.
TulipaEnergyModel._construct_inter_rp_dataframes
— Methoddf = _construct_inter_rp_dataframes(assets, graph, years, asset_filter)
Constructs dataframes for inter representative period constraints.
Arguments
assets
: An array of assets.graph
: The energy problem graph with the assets data.asset_filter
: A function that filters assets based on certain criteria.
Returns
A dataframe containing the constructed dataframe for constraints.
TulipaEnergyModel._get_graph_asset_or_flow
— Method_get_graph_asset_or_flow(graph, a)
_get_graph_asset_or_flow(graph, (u, v))
Returns graph[a]
or graph[u, v]
.
TulipaEnergyModel._interpolate_storage_level!
— Method_interpolate_storage_level!(df, time_column::Symbol)
Transform the storage level dataframe from grouped timesteps or periods to incremental ones by interpolation. The starting value is the value of the previous grouped timesteps or periods or the initial value. The ending value is the value for the grouped timesteps or periods.
TulipaEnergyModel._parse_rp_partition
— Function_parse_rp_partition(Val(specification), timestep_string, rp_timesteps)
Parses the timestep_string
according to the specification. The representative period timesteps (rp_timesteps
) might not be used in the computation, but it will be used for validation.
The specification defines what is expected from the timestep_string
:
:uniform
: Thetimestep_string
should be a single number indicating the duration of each block. Examples: "3", "4", "1".:explicit
: Thetimestep_string
should be a semicolon-separated list of integers. Each integer is a duration of a block. Examples: "3;3;3;3", "4;4;4", "1;1;1;1;1;1;1;1;1;1;1;1", and "3;3;4;2".:math
: Thetimestep_string
should be an expression of the formNxD+NxD…
, whereD
is the duration of the block andN
is the number of blocks. Examples: "4x3", "3x4", "12x1", and "2x3+1x4+1x2".
The generated blocks will be ranges (a:b
). The first block starts at 1
, and the last block ends at length(rp_timesteps)
.
The following table summarizes the formats for a rp_timesteps = 1:12
:
Output | :uniform | :explicit | :math |
---|---|---|---|
1:3, 4:6, 7:9, 10:12 | 3 | 3;3;3;3 | 4x3 |
1:4, 5:8, 9:12 | 4 | 4;4;4 | 3x4 |
1:1, 2:2, …, 12:12 | 1 | 1;1;1;1;1;1;1;1;1;1;1;1 | 12x1 |
1:3, 4:6, 7:10, 11:12 | NA | 3;3;4;2 | 2x3+1x4+1x2 |
Examples
using TulipaEnergyModel
TulipaEnergyModel._parse_rp_partition(Val(:uniform), "3", 1:12)
# output
4-element Vector{UnitRange{Int64}}:
1:3
4:6
7:9
10:12
using TulipaEnergyModel
TulipaEnergyModel._parse_rp_partition(Val(:explicit), "4;4;4", 1:12)
# output
3-element Vector{UnitRange{Int64}}:
1:4
5:8
9:12
using TulipaEnergyModel
TulipaEnergyModel._parse_rp_partition(Val(:math), "2x3+1x4+1x2", 1:12)
# output
4-element Vector{UnitRange{Int64}}:
1:3
4:6
7:10
11:12
TulipaEnergyModel.add_expression_is_charging_terms_intra_rp_constraints!
— Methodadd_expression_is_charging_terms_intra_rp_constraints!(df_cons,
df_is_charging,
workspace
)
Computes the is_charging
expressions per row of df_cons
for the constraints that are within (intra) the representative periods.
This function is only used internally in the model.
This strategy is based on the replies in this discourse thread:
- https://discourse.julialang.org/t/help-improving-the-speed-of-a-dataframes-operation/107615/23
TulipaEnergyModel.add_expression_terms_inter_rp_constraints!
— Methodadd_expression_terms_inter_rp_constraints!(df_inter,
df_flows,
df_map,
graph,
representative_periods,
)
Computes the incoming and outgoing expressions per row of df_inter for the constraints that are between (inter) the representative periods.
This function is only used internally in the model.
TulipaEnergyModel.add_expression_terms_intra_rp_constraints!
— Methodadd_expression_terms_intra_rp_constraints!(df_cons,
df_flows,
workspace,
representative_periods,
graph;
use_highest_resolution = true,
multiply_by_duration = true,
)
Computes the incoming and outgoing expressions per row of df_cons for the constraints that are within (intra) the representative periods.
This function is only used internally in the model.
This strategy is based on the replies in this discourse thread:
- https://discourse.julialang.org/t/help-improving-the-speed-of-a-dataframes-operation/107615/23
TulipaEnergyModel.add_expression_units_on_terms_intra_rp_constraints!
— Methodadd_expression_units_on_terms_intra_rp_constraints!(
df_cons,
df_units_on,
workspace,
)
Computes the units_on
expressions per row of df_cons
for the constraints that are within (intra) the representative periods.
This function is only used internally in the model.
This strategy is based on the replies in this discourse thread:
- https://discourse.julialang.org/t/help-improving-the-speed-of-a-dataframes-operation/107615/23
TulipaEnergyModel.add_group_constraints!
— Methodadd_group_constraints!(model, graph, ...)
Adds group constraints for assets that share a common limits or bounds
TulipaEnergyModel.add_ramping_constraints!
— Methodadd_ramping_and_unit_commitment_constraints!(model, graph, ...)
Adds the ramping constraints for producer and conversion assets where ramping = true in assets_data
TulipaEnergyModel.calculate_annualized_cost
— Methodcalculate_annualized_cost(discount_rate, economic_lifetime, investment_cost, years, investable_assets)
Calculates the annualized cost for each asset, both energy assets and transport assets, in each year using provided discount rates, economic lifetimes, and investment costs.
Arguments
discount_rate::Dict
: A dictionary where the key is anasset
or a pair of assets(asset1, asset2)
for transport assets, and the value is the discount rate.economic_lifetime::Dict
: A dictionary where the key is anasset
or a pair of assets(asset1, asset2)
for transport assets, and the value is the economic lifetime.investment_cost::Dict
: A dictionary where the key is a tuple(year, asset)
or(year, (asset1, asset2))
for transport assets, and the value is the investment cost.years::Array
: An array of years to be considered.investable_assets::Dict
: A dictionary where the key is a year, and the value is an array of assets that are relevant for that year.
Returns
- A
Dict
where the keys are tuples(year, asset)
representing the year and the asset, and the values are the calculated annualized cost for each asset in each year.
Formula
The annualized cost for each asset in year is calculated using the formula:
annualized_cost = discount_rate[asset] / (
(1 + discount_rate[asset]) *
(1 - 1 / (1 + discount_rate[asset])^economic_lifetime[asset])
) * investment_cost[(year, asset)]
Example for energy assets
discount_rate = Dict("asset1" => 0.05, "asset2" => 0.07)
economic_lifetime = Dict("asset1" => 10, "asset2" => 15)
investment_cost = Dict((2021, "asset1") => 1000, (2021, "asset2") => 1500,
(2022, "asset1") => 1100, (2022, "asset2") => 1600)
years = [2021, 2022]
investable_assets = Dict(2021 => ["asset1", "asset2"],
2022 => ["asset1"])
costs = calculate_annualized_cost(discount_rate, economic_lifetime, investment_cost, years, investable_assets)
# output
Dict{Tuple{Int64, String}, Float64} with 3 entries:
(2021, "asset1") => 123.338
(2021, "asset2") => 153.918
(2022, "asset1") => 135.671
Example for transport assets
discount_rate = Dict(("asset1", "asset2") => 0.05, ("asset3", "asset4") => 0.07)
economic_lifetime = Dict(("asset1", "asset2") => 10, ("asset3", "asset4") => 15)
investment_cost = Dict((2021, ("asset1", "asset2")) => 1000, (2021, ("asset3", "asset4")) => 1500,
(2022, ("asset1", "asset2")) => 1100, (2022, ("asset3", "asset4")) => 1600)
years = [2021, 2022]
investable_assets = Dict(2021 => [("asset1", "asset2"), ("asset3", "asset4")],
2022 => [("asset1", "asset2")])
costs = calculate_annualized_cost(discount_rate, economic_lifetime, investment_cost, years, investable_assets)
# output
Dict{Tuple{Int64, Tuple{String, String}}, Float64} with 3 entries:
(2022, ("asset1", "asset2")) => 135.671
(2021, ("asset3", "asset4")) => 153.918
(2021, ("asset1", "asset2")) => 123.338
TulipaEnergyModel.calculate_salvage_value
— Methodcalculate_salvage_value(discount_rate,
economic_lifetime,
annualized_cost,
years,
investable_assets,
)
Calculates the salvage value for each asset, both energy assets and transport assets.
Arguments
discount_rate::Dict
: A dictionary where the key is anasset
or a pair of assets(asset1, asset2)
for transport assets, and the value is the discount rate.economic_lifetime::Dict
: A dictionary where the key is anasset
or a pair of assets(asset1, asset2)
for transport assets, and the value is the economic lifetime.annualized_cost::Dict
: ADict
where the keys are tuples(year, asset)
representing the year and the asset, and the values are the annualized cost for each asset in each year.years::Array
: An array of years to be considered.investable_assets::Dict
: A dictionary where the key is a year, and the value is an array of assets that are relevant for that year.
Returns
- A
Dict
where the keys are tuples(year, asset)
representing the year and the asset, and the values are the salvage value for each asset in each year.
Formula
The salvage value for each asset in year is calculated using the formula:
salvagevalue = annualizedcost[(year, asset)] * sum( 1 / (1 + discountrate[asset])^(yearalias - year) for yearalias in salvagevalue_set[(year, asset)] )
Example for energy assets
discount_rate = Dict("asset1" => 0.05, "asset2" => 0.07)
economic_lifetime = Dict("asset1" => 10, "asset2" => 15)
annualized_cost =
Dict((2021, "asset1") => 123.338, (2021, "asset2") => 153.918, (2022, "asset1") => 135.671)
years = [2021, 2022]
investable_assets = Dict(2021 => ["asset1", "asset2"], 2022 => ["asset1"])
salvage_value = calculate_salvage_value(
discount_rate,
economic_lifetime,
annualized_cost,
years,
investable_assets,
)
# output
Dict{Tuple{Int64, String}, Float64} with 3 entries:
(2021, "asset1") => 759.2
(2021, "asset2") => 1202.24
(2022, "asset1") => 964.325
Example for transport assets
discount_rate = Dict(("asset1", "asset2") => 0.05, ("asset3", "asset4") => 0.07)
economic_lifetime = Dict(("asset1", "asset2") => 10, ("asset3", "asset4") => 15)
annualized_cost = Dict(
(2022, ("asset1", "asset2")) => 135.671,
(2021, ("asset3", "asset4")) => 153.918,
(2021, ("asset1", "asset2")) => 123.338,
)
years = [2021, 2022]
investable_assets =
Dict(2021 => [("asset1", "asset2"), ("asset3", "asset4")], 2022 => [("asset1", "asset2")])
salvage_value = calculate_salvage_value(
discount_rate,
economic_lifetime,
annualized_cost,
years,
investable_assets,
)
# output
Dict{Tuple{Int64, Tuple{String, String}}, Float64} with 3 entries:
(2022, ("asset1", "asset2")) => 964.325
(2021, ("asset3", "asset4")) => 1202.24
(2021, ("asset1", "asset2")) => 759.2
TulipaEnergyModel.calculate_weight_for_investment_discounts
— Methodcalculate_weight_for_investment_discounts(social_rate,
discount_year,
salvage_value,
investment_cost,
years,
investable_assets,
)
Calculates the weight for investment discounts for each asset, both energy assets and transport assets.
Arguments
social_rate::Float64
: A value with the social discount rate.discount_year::Int64
: A value with the discount year for all the investments.salvage_value::Dict
: A dictionary where the key is an tuple(year, asset)
or(year, (asset1, asset2))
for transport assets, and the value is the salvage value.investment_cost::Dict
: A dictionary where the key is an tuple(year, asset)
or(year, (asset1, asset2))
for transport assets, and the value is the investment cost.years::Array
: An array of years to be considered.investable_assets::Dict
: A dictionary where the key is a year, and the value is an array of assets that are relevant for that year.
Returns
- A
Dict
where the keys are tuples(year, asset)
representing the year and the asset, and the values are the weights for investment discounts.
Formula
The weight for investment discounts for each asset in year is calculated using the formula:
weightforinvestmentdiscounts = 1 / (1 + socialrate)^(year - discountyear) * (1 - salvagevalue[(year, asset)] / investment_cost[(year, asset)])
Example for energy assets
social_rate = 0.02
discount_year = 2000
salvage_value = Dict(
(2021, "asset1") => 759.1978422,
(2021, "asset2") => 1202.2339859,
(2022, "asset1") => 964.3285406,
)
investment_cost = Dict(
(2021, "asset1") => 1000,
(2021, "asset2") => 1500,
(2022, "asset1") => 1100,
(2022, "asset2") => 1600,
)
years = [2021, 2022]
investable_assets = Dict(2021 => ["asset1", "asset2"], 2022 => ["asset1"])
weights = calculate_weight_for_investment_discounts(
social_rate,
discount_year,
salvage_value,
investment_cost,
years,
investable_assets,
)
# output
Dict{Tuple{Int64, String}, Float64} with 3 entries:
(2021, "asset1") => 0.158875
(2021, "asset2") => 0.130973
(2022, "asset1") => 0.0797796
Example for transport assets
social_rate = 0.02
discount_year = 2000
salvage_value = Dict(
(2022, ("asset1", "asset2")) => 964.325,
(2021, ("asset3", "asset4")) => 1202.24,
(2021, ("asset1", "asset2")) => 759.2,
)
investment_cost = Dict((2021, ("asset1", "asset2")) => 1000, (2021, ("asset3", "asset4")) => 1500,
(2022, ("asset1", "asset2")) => 1100, (2022, ("asset3", "asset4")) => 1600)
years = [2021, 2022]
investable_assets = Dict(2021 => [("asset1", "asset2"), ("asset3", "asset4")],
2022 => [("asset1", "asset2")])
weights = calculate_weight_for_investment_discounts(
social_rate,
discount_year,
salvage_value,
investment_cost,
years,
investable_assets,
)
# output
Dict{Tuple{Int64, Tuple{String, String}}, Float64} with 3 entries:
(2022, ("asset1", "asset2")) => 0.0797817
(2021, ("asset3", "asset4")) => 0.13097
(2021, ("asset1", "asset2")) => 0.158874
TulipaEnergyModel.calculate_weight_for_investment_discounts
— Methodcalculate_weight_for_investment_discounts(graph::MetaGraph,
years,
investable_assets,
assets,
model_parameters,
)
Calculates the weight for investment discounts for each asset, both energy assets and transport assets. Internally calls calculate_annualized_cost
, calculate_salvage_value
, calculate_weight_for_investment_discounts
.
Arguments
graph::MetaGraph
: A graphyears::Array
: An array of years to be considered.investable_assets::Dict
: A dictionary where the key is a year, and the value is an array of assets that are relevant for that year.assets::Array
: An array of assets.model_parameters::ModelParameters
: A model parameters structure.
Returns
- A
Dict
where the keys are tuples(year, asset)
representing the year and the asset, and the values are the weights for investment discounts.
TulipaEnergyModel.compute_assets_partitions!
— Methodcompute_assets_partitions!(partitions, df, a, representative_periods)
Parses the time blocks in the DataFrame df
for the asset a
and every representative period in the timesteps_per_rp
dictionary, modifying the input partitions
.
partitions
must be a dictionary indexed by the representative periods, possibly empty.
timesteps_per_rp
must be a dictionary indexed by rep_period
and its values are the timesteps of that rep_period
.
To obtain the partitions, the columns specification
and partition
from df
are passed to the function _parse_rp_partition
.
TulipaEnergyModel.compute_constraints_partitions
— Methodcons_partitions = compute_constraints_partitions(graph, representative_periods)
Computes the constraints partitions using the assets and flows partitions stored in the graph, and the representative periods.
The function computes the constraints partitions by iterating over the partition dictionary, which specifies the partition strategy for each resolution (i.e., lowest or highest). For each asset and representative period, it calls the compute_rp_partition
function to compute the partition based on the strategy.
TulipaEnergyModel.compute_dual_variables
— Methodcompute_dual_variables(model)
Compute the dual variables for the given model.
If the model does not have dual variables, this function fixes the discrete variables, optimizes the model, and then computes the dual variables.
Arguments
model
: The model for which to compute the dual variables.
Returns
A named tuple containing the dual variables of selected constraints.
TulipaEnergyModel.compute_flows_partitions!
— Methodcompute_flows_partitions!(partitions, df, u, v, representative_periods)
Parses the time blocks in the DataFrame df
for the flow (u, v)
and every representative period in the timesteps_per_rp
dictionary, modifying the input partitions
.
partitions
must be a dictionary indexed by the representative periods, possibly empty.
timesteps_per_rp
must be a dictionary indexed by rep_period
and its values are the timesteps of that rep_period
.
To obtain the partitions, the columns specification
and partition
from df
are passed to the function _parse_rp_partition
.
TulipaEnergyModel.compute_rp_partition
— Methodrp_partition = compute_rp_partition(partitions, :lowest)
Given the timesteps of various flows/assets in the partitions
input, compute the representative period partitions.
Each element of partitions
is a partition with the following assumptions:
- An element is of the form
V = [r₁, r₂, …, rₘ]
, where eachrᵢ
is a rangea:b
. r₁
starts at 1.rᵢ₊₁
starts at the end ofrᵢ
plus 1.rₘ
ends at some valueN
, that is the same for all elements ofpartitions
.
Notice that this implies that they form a disjunct partition of 1:N
.
The output will also be a partition with the conditions above.
Strategies
:lowest
If strategy = :lowest
(default), then the output is constructed greedily, i.e., it selects the next largest breakpoint following the algorithm below:
- Input:
Vᴵ₁, …, Vᴵₚ
, a list of time blocks. Each element ofVᴵⱼ
is a ranger = r.start:r.end
. Output:V
. - Compute the end of the representative period
N
(allVᴵⱼ
should have the same end) - Start with an empty
V = []
- Define the beginning of the range
s = 1
- Define an array with all the next breakpoints
B
such thatBⱼ
is the firstr.end
such thatr.end ≥ s
for eachr ∈ Vᴵⱼ
. - The end of the range will be the
e = max Bⱼ
. - Define
r = s:e
and addr
to the end ofV
. - If
e = N
, then END - Otherwise, define
s = e + 1
and go to step 4.
Examples
partition1 = [1:4, 5:8, 9:12]
partition2 = [1:3, 4:6, 7:9, 10:12]
compute_rp_partition([partition1, partition2], :lowest)
# output
3-element Vector{UnitRange{Int64}}:
1:4
5:8
9:12
partition1 = [1:1, 2:3, 4:6, 7:10, 11:12]
partition2 = [1:2, 3:4, 5:5, 6:7, 8:9, 10:12]
compute_rp_partition([partition1, partition2], :lowest)
# output
5-element Vector{UnitRange{Int64}}:
1:2
3:4
5:6
7:10
11:12
:highest
If strategy = :highest
, then the output selects includes all the breakpoints from the input. Another way of describing it, is to select the minimum end-point instead of the maximum end-point in the :lowest
strategy.
Examples
partition1 = [1:4, 5:8, 9:12]
partition2 = [1:3, 4:6, 7:9, 10:12]
compute_rp_partition([partition1, partition2], :highest)
# output
6-element Vector{UnitRange{Int64}}:
1:3
4:4
5:6
7:8
9:9
10:12
partition1 = [1:1, 2:3, 4:6, 7:10, 11:12]
partition2 = [1:2, 3:4, 5:5, 6:7, 8:9, 10:12]
compute_rp_partition([partition1, partition2], :highest)
# output
10-element Vector{UnitRange{Int64}}:
1:1
2:2
3:3
4:4
5:5
6:6
7:7
8:9
10:10
11:12
TulipaEnergyModel.construct_dataframes
— Methoddataframes = construct_dataframes(
graph,
representative_periods,
constraints_partitions,, IteratorSize
years,
)
Computes the data frames used to linearize the variables and constraints. These are used internally in the model only.
TulipaEnergyModel.create_internal_structures
— Methodgraph, representative_periods, timeframe = create_internal_structures(connection)
Return the graph
, representative_periods
, and timeframe
structures given the input dataframes structure.
The details of these structures are:
graph
: a MetaGraph with the following information:labels(graph)
: All assets.edge_labels(graph)
: All flows, in pair format(u, v)
, whereu
andv
are assets.graph[a]
: ATulipaEnergyModel.GraphAssetData
structure for asseta
.graph[u, v]
: ATulipaEnergyModel.GraphFlowData
structure for flow(u, v)
.
representative_periods
: An array ofTulipaEnergyModel.RepresentativePeriod
ordered by their IDs.timeframe
: Information ofTulipaEnergyModel.Timeframe
.
TulipaEnergyModel.create_intervals_for_years
— Methodcreate_intervals(years)
Create a dictionary of intervals for years
. The interval is assigned to the its starting year. The last interval is 1.
TulipaEnergyModel.create_model!
— Methodcreate_model!(energy_problem; verbose = false)
Create the internal model of an TulipaEnergyModel.EnergyProblem
.
TulipaEnergyModel.create_model
— Methodmodel = create_model(graph, representative_periods, dataframes, timeframe, groups; write_lp_file = false)
Create the energy model given the graph
, representative_periods
, dictionary of dataframes
(created by construct_dataframes
), timeframe, and groups.
TulipaEnergyModel.default_parameters
— Methoddefault_parameters(Val(optimizer_name_symbol))
default_parameters(optimizer)
default_parameters(optimizer_name_symbol)
default_parameters(optimizer_name_string)
Returns the default parameters for a given JuMP optimizer. Falls back to Dict()
for undefined solvers.
Arguments
There are four ways to use this function:
Val(optimizer_name_symbol)
: This uses type dispatch with the specialVal
type. Pass the solver name as a Symbol (e.g.,Val(:HiGHS)
).optimizer
: The JuMP optimizer type (e.g.,HiGHS.Optimizer
).optimizer_name_symbol
oroptimizer_name_string
: Pass the name in Symbol or String format and it will be converted toVal
.
Using Val
is necessary for the dispatch. All other cases will convert the argument and call the Val
version, which might lead to type instability.
Examples
using HiGHS
default_parameters(HiGHS.Optimizer)
# output
Dict{String, Any} with 1 entry:
"output_flag" => false
Another case
default_parameters(Val(:Cbc))
# output
Dict{String, Any} with 1 entry:
"logLevel" => 0
default_parameters(:Cbc) == default_parameters("Cbc") == default_parameters(Val(:Cbc))
# output
true
TulipaEnergyModel.filter_graph
— Methodfilter_graph(graph, elements, value, key)
filter_graph(graph, elements, value, key, year)
Helper function to filter elements (assets or flows) in the graph given a key (and possibly year) and value (or values). In the safest case, this is equivalent to the filters
filter_assets_whose_key_equal_to_value = a -> graph[a].key == value
filter_assets_whose_key_year_equal_to_value = a -> graph[a].key[year] in value
filter_flows_whose_key_equal_to_value = f -> graph[f...].key == value
filter_flows_whose_key_year_equal_to_value = f -> graph[f...].key[year] in value
TulipaEnergyModel.get_graph_value_or_missing
— Methodget_graph_value_or_missing(graph, graph_key, field_key)
get_graph_value_or_missing(graph, graph_key, field_key, year)
Get graph[graph_key].field_key
(or graph[graph_key].field_key[year]
) or return missing
if any of the values do not exist. We also check if graph[graph_key].active[year]
is true if the year
is passed and return missing
otherwise.
TulipaEnergyModel.profile_aggregation
— Methodprofile_aggregation(agg, profiles, key, block, default_value)
Aggregates the profiles[key]
over the block
using the agg
function. If the profile does not exist, uses default_value
instead of each profile value.
profiles
should be a dictionary of profiles, for instance graph[a].profiles
or graph[u, v].profiles
. If profiles[key]
exists, then this function computes the aggregation of profiles[key]
over the range block
using the aggregator agg
, i.e., agg(profiles[key][block])
. If profiles[key]
does not exist, then this substitutes it with a vector of default_value
s.
TulipaEnergyModel.read_parameters_from_file
— Methodread_parameters_from_file(filepath)
Parse the parameters from a file into a dictionary. The keys and values are NOT checked to be valid parameters for any specific solvers.
The file should contain a list of lines of the following type:
key = value
The file is parsed as TOML, which is intuitive. See the example below.
Example
# Creating file
filepath, io = mktemp()
println(io,
"""
true_or_false = true
integer_number = 5
real_number1 = 3.14
big_number = 6.66E06
small_number = 1e-8
string = "something"
"""
)
close(io)
# Reading
read_parameters_from_file(filepath)
# output
Dict{String, Any} with 6 entries:
"string" => "something"
"integer_number" => 5
"small_number" => 1.0e-8
"true_or_false" => true
"real_number1" => 3.14
"big_number" => 6.66e6
TulipaEnergyModel.run_scenario
— Methodenergy_problem = run_scenario(connection; optimizer, parameters, write_lp_file, log_file, show_log)
Run the scenario in the given connection
and return the energy problem.
The optimizer
and parameters
keyword arguments can be used to change the optimizer (the default is HiGHS) and its parameters. The variables are passed to the solve_model
function.
Set write_lp_file = true
to export the problem that is sent to the solver to a file for viewing. Set show_log = false
to silence printing the log while running. Specify a log_file
name to export the log to a file.
TulipaEnergyModel.safe_comparison
— Methodsafe_comparison(graph, a, value, key)
safe_comparison(graph, a, value, key, year)
Check if graph[a].value
(or graph[a].value[year]
) is equal to value
. This function assumes that if graph[a].value
is a dictionary and value
is not, then you made a mistake. This makes it safer, because it will not silently return false
. It also checks for missing.
TulipaEnergyModel.safe_inclusion
— Methodsafe_inclusion(graph, a, value, key)
safe_inclusion(graph, a, value, key, year)
Check if graph[a].value
(or graph[a].value[year]
) is in values
. This correctly check that missing in [missing]
returns false
.
TulipaEnergyModel.save_solution_to_file
— Methodsave_solution_to_file(output_file, graph, solution)
Saves the solution in CSV files inside output_folder
.
The following files are created:
assets-investment.csv
: The format of each row isa,v,p*v
, wherea
is the asset name,v
is the corresponding asset investment value, andp
is the corresponding capacity value. Only investable assets are included.assets-investments-energy.csv
: The format of each row isa,v,p*v
, wherea
is the asset name,v
is the corresponding asset investment value on energy, andp
is the corresponding energy capacity value. Only investable assets with astorage_method_energy
set totrue
are included.flows-investment.csv
: Similar toassets-investment.csv
, but for flows.flows.csv
: The value of each flow, per(from, to)
flow,rp
representative period andtimestep
. Since the flow is in power, the value at a timestep is equal to the value at the corresponding time block, i.e., if flow[1:3] = 30, then flow[1] = flow[2] = flow[3] = 30.storage-level.csv
: The value of each storage level, perasset
,rp
representative period, andtimestep
. Since the storage level is in energy, the value at a timestep is a proportional fraction of the value at the corresponding time block, i.e., if level[1:3] = 30, then level[1] = level[2] = level[3] = 10.
TulipaEnergyModel.save_solution_to_file
— Methodsave_solution_to_file(output_folder, energy_problem)
Saves the solution from energy_problem
in CSV files inside output_file
.
TulipaEnergyModel.solve_model
— Functionsolution = solve_model(model[, optimizer; parameters])
Solve the JuMP model and return the solution. The optimizer
argument should be an MILP solver from the JuMP list of supported solvers. By default we use HiGHS.
The keyword argument parameters
should be passed as a list of key => value
pairs. These can be created manually, obtained using default_parameters
, or read from a file using read_parameters_from_file
.
The solution
object is a mutable struct with the following fields:
assets_investment[a]
: The investment for each asset, indexed on the investable asseta
. To create a traditional array in the order given by the investable assets, one can run[solution.assets_investment[a] for a in labels(graph) if graph[a].investable]
assets_investment_energy[a]
: The investment on energy component for each asset, indexed on the investable asseta
with astorage_method_energy
set totrue
.
To create a traditional array in the order given by the investable assets, one can run
[solution.assets_investment_energy[a] for a in labels(graph) if graph[a].investable && graph[a].storage_method_energy
flows_investment[u, v]
: The investment for each flow, indexed on the investable flow(u, v)
. To create a traditional array in the order given by the investable flows, one can run[solution.flows_investment[(u, v)] for (u, v) in edge_labels(graph) if graph[u, v].investable]
storage_level_intra_rp[a, rp, timesteps_block]
: The storage level for the storage asseta
for a representative periodrp
and a time blocktimesteps_block
. The list of time blocks is defined byconstraints_partitions
, which was used to create the model. To create a vector with all values ofstorage_level_intra_rp
for a givena
andrp
, one can run[solution.storage_level_intra_rp[a, rp, timesteps_block] for timesteps_block in constraints_partitions[:lowest_resolution][(a, rp)]]
storage_level_inter_rp[a, pb]
: The storage level for the storage asseta
for a periods blockpb
. To create a vector with all values ofstorage_level_inter_rp
for a givena
, one can run[solution.storage_level_inter_rp[a, bp] for bp in graph[a].timeframe_partitions[a]]
flow[(u, v), rp, timesteps_block]
: The flow value for a given flow(u, v)
at a given representative periodrp
, and time blocktimesteps_block
. The list of time blocks is defined bygraph[(u, v)].partitions[rp]
. To create a vector with all values offlow
for a given(u, v)
andrp
, one can run[solution.flow[(u, v), rp, timesteps_block] for timesteps_block in graph[u, v].partitions[rp]]
objective_value
: A Float64 with the objective value at the solution.duals
: A NamedTuple containing the dual variables of selected constraints.
Examples
parameters = Dict{String,Any}("presolve" => "on", "time_limit" => 60.0, "output_flag" => true)
solution = solve_model(model, HiGHS.Optimizer; parameters = parameters)
TulipaEnergyModel.solve_model!
— Functionsolution = solve_model!(energy_problem[, optimizer; parameters])
Solve the internal model of an energy_problem
. The solution obtained by calling solve_model
is returned.
TulipaEnergyModel.solve_model!
— Methodsolution = solve_model!(dataframes, model, ...)
Solves the JuMP model
, returns the solution, and modifies dataframes
to include the solution. The modifications made to dataframes
are:
df_flows.solution = solution.flow
df_storage_level_intra_rp.solution = solution.storage_level_intra_rp
df_storage_level_inter_rp.solution = solution.storage_level_inter_rp