Reference
TulipaEnergyModel.EnergyProblemTulipaEnergyModel.GraphAssetDataTulipaEnergyModel.GraphFlowDataTulipaEnergyModel.GroupTulipaEnergyModel.ModelParametersTulipaEnergyModel.RepresentativePeriodTulipaEnergyModel.TimeframeTulipaEnergyModel.YearTulipaEnergyModel._check_initial_storage_level!TulipaEnergyModel._construct_inter_rp_dataframesTulipaEnergyModel._get_graph_asset_or_flowTulipaEnergyModel._interpolate_storage_level!TulipaEnergyModel._parse_rp_partitionTulipaEnergyModel.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_costTulipaEnergyModel.calculate_salvage_valueTulipaEnergyModel.calculate_weight_for_investment_discountsTulipaEnergyModel.calculate_weight_for_investment_discountsTulipaEnergyModel.compute_assets_partitions!TulipaEnergyModel.compute_constraints_partitionsTulipaEnergyModel.compute_dual_variablesTulipaEnergyModel.compute_flows_partitions!TulipaEnergyModel.compute_rp_partitionTulipaEnergyModel.construct_dataframesTulipaEnergyModel.create_internal_structuresTulipaEnergyModel.create_intervals_for_yearsTulipaEnergyModel.create_modelTulipaEnergyModel.create_model!TulipaEnergyModel.default_parametersTulipaEnergyModel.filter_graphTulipaEnergyModel.get_graph_value_or_missingTulipaEnergyModel.profile_aggregationTulipaEnergyModel.read_parameters_from_fileTulipaEnergyModel.run_scenarioTulipaEnergyModel.safe_comparisonTulipaEnergyModel.safe_inclusionTulipaEnergyModel.save_solution_to_fileTulipaEnergyModel.save_solution_to_fileTulipaEnergyModel.solve_modelTulipaEnergyModel.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 themodelhas 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 newEnergyProblemobject with the given connection. Theconstraints_partitionsfield 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_stringshould be a single number indicating the duration of each block. Examples: "3", "4", "1".:explicit: Thetimestep_stringshould 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_stringshould be an expression of the formNxD+NxD…, whereDis the duration of the block andNis 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:12using TulipaEnergyModel
TulipaEnergyModel._parse_rp_partition(Val(:explicit), "4;4;4", 1:12)
# output
3-element Vector{UnitRange{Int64}}:
 1:4
 5:8
 9:12using 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:12TulipaEnergyModel.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 anassetor 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 anassetor 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 
Dictwhere 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.671Example 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.338TulipaEnergyModel.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 anassetor 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 anassetor a pair of assets(asset1, asset2)for transport assets, and the value is the economic lifetime.annualized_cost::Dict: ADictwhere 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 
Dictwhere 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.325Example 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.2TulipaEnergyModel.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 
Dictwhere 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.0797796Example 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.158874TulipaEnergyModel.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 
Dictwhere 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 
Bsuch thatBⱼis the firstr.endsuch thatr.end ≥ sfor eachr ∈ Vᴵⱼ. - The end of the range will be the 
e = max Bⱼ. - Define 
r = s:eand addrto the end ofV. - If 
e = N, then END - Otherwise, define 
s = e + 1and 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:12partition1 = [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:12partition1 = [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:12TulipaEnergyModel.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), whereuandvare assets.graph[a]: ATulipaEnergyModel.GraphAssetDatastructure for asseta.graph[u, v]: ATulipaEnergyModel.GraphFlowDatastructure for flow(u, v).
representative_periods: An array ofTulipaEnergyModel.RepresentativePeriodordered 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, enable_names = true)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 specialValtype. Pass the solver name as a Symbol (e.g.,Val(:HiGHS)).optimizer: The JuMP optimizer type (e.g.,HiGHS.Optimizer).optimizer_name_symboloroptimizer_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" => falseAnother case
default_parameters(Val(:Cbc))
# output
Dict{String, Any} with 1 entry:
  "logLevel" => 0default_parameters(:Cbc) == default_parameters("Cbc") == default_parameters(Val(:Cbc))
# output
trueTulipaEnergyModel.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 valueTulipaEnergyModel.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_values.
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 = valueThe 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.66e6TulipaEnergyModel.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, whereais the asset name,vis the corresponding asset investment value, andpis the corresponding capacity value. Only investable assets are included.assets-investments-energy.csv: The format of each row isa,v,p*v, whereais the asset name,vis the corresponding asset investment value on energy, andpis the corresponding energy capacity value. Only investable assets with astorage_method_energyset totrueare included.flows-investment.csv: Similar toassets-investment.csv, but for flows.flows.csv: The value of each flow, per(from, to)flow,rprepresentative 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,rprepresentative 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 assetawith astorage_method_energyset 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_energyflows_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 assetafor a representative periodrpand 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_rpfor a givenaandrp, 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 assetafor a periods blockpb. To create a vector with all values ofstorage_level_inter_rpfor 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 offlowfor 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.flowdf_storage_level_intra_rp.solution = solution.storage_level_intra_rpdf_storage_level_inter_rp.solution = solution.storage_level_inter_rp