Reference

TulipaEnergyModel.EnergyProblemType

Structure 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 the representative_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 the model 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 new EnergyProblem object with the given connection. The constraints_partitions field is computed from the representative_periods, and the other fields are initialized with default values.

See the basic example tutorial to see how these can be used.

source
TulipaEnergyModel.ModelParametersType
ModelParameters(;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.
source
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.

source
TulipaEnergyModel._construct_inter_rp_dataframesMethod
df = _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.

source
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.

source
TulipaEnergyModel._parse_rp_partitionFunction
_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: The timestep_string should be a single number indicating the duration of each block. Examples: "3", "4", "1".
  • :explicit: The timestep_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: The timestep_string should be an expression of the form NxD+NxD…, where D is the duration of the block and N 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:1233;3;3;34x3
1:4, 5:8, 9:1244;4;43x4
1:1, 2:2, …, 12:1211;1;1;1;1;1;1;1;1;1;1;112x1
1:3, 4:6, 7:10, 11:12NA3;3;4;22x3+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
source
TulipaEnergyModel.add_expression_is_charging_terms_intra_rp_constraints!Method
add_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
source
TulipaEnergyModel.add_expression_terms_inter_rp_constraints!Method
add_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.

source
TulipaEnergyModel.add_expression_terms_intra_rp_constraints!Method
add_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
source
TulipaEnergyModel.add_expression_units_on_terms_intra_rp_constraints!Method
add_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
source
TulipaEnergyModel.calculate_annualized_costMethod
calculate_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 an asset 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 an asset 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
source
TulipaEnergyModel.calculate_salvage_valueMethod
calculate_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 an asset 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 an asset or a pair of assets (asset1, asset2) for transport assets, and the value is the economic lifetime.
  • annualized_cost::Dict: A Dict 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
source
TulipaEnergyModel.calculate_weight_for_investment_discountsMethod
calculate_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
source
TulipaEnergyModel.calculate_weight_for_investment_discountsMethod
calculate_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 graph
  • 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.
  • 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.
source
TulipaEnergyModel.compute_assets_partitions!Method
compute_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.

source
TulipaEnergyModel.compute_constraints_partitionsMethod
cons_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.

source
TulipaEnergyModel.compute_dual_variablesMethod
compute_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.

source
TulipaEnergyModel.compute_flows_partitions!Method
compute_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.

source
TulipaEnergyModel.compute_rp_partitionMethod
rp_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 each rᵢ is a range a:b.
  • r₁ starts at 1.
  • rᵢ₊₁ starts at the end of rᵢ plus 1.
  • rₘ ends at some value N, that is the same for all elements of partitions.

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:

  1. Input: Vᴵ₁, …, Vᴵₚ, a list of time blocks. Each element of Vᴵⱼ is a range r = r.start:r.end. Output: V.
  2. Compute the end of the representative period N (all Vᴵⱼ should have the same end)
  3. Start with an empty V = []
  4. Define the beginning of the range s = 1
  5. Define an array with all the next breakpoints B such that Bⱼ is the first r.end such that r.end ≥ s for each r ∈ Vᴵⱼ.
  6. The end of the range will be the e = max Bⱼ.
  7. Define r = s:e and add r to the end of V.
  8. If e = N, then END
  9. 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
source
TulipaEnergyModel.construct_dataframesMethod
dataframes = 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.

source
TulipaEnergyModel.create_internal_structuresMethod
graph, 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:

source
TulipaEnergyModel.create_modelMethod
model = 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.

source
TulipaEnergyModel.default_parametersMethod
default_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 special Val type. Pass the solver name as a Symbol (e.g., Val(:HiGHS)).
  • optimizer: The JuMP optimizer type (e.g., HiGHS.Optimizer).
  • optimizer_name_symbol or optimizer_name_string: Pass the name in Symbol or String format and it will be converted to Val.

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
source
TulipaEnergyModel.filter_graphMethod
filter_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
source
TulipaEnergyModel.get_graph_value_or_missingMethod
get_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.

source
TulipaEnergyModel.profile_aggregationMethod
profile_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.

source
TulipaEnergyModel.read_parameters_from_fileMethod
read_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
source
TulipaEnergyModel.run_scenarioMethod
energy_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.

source
TulipaEnergyModel.safe_comparisonMethod
safe_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.

source
TulipaEnergyModel.safe_inclusionMethod
safe_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.

source
TulipaEnergyModel.save_solution_to_fileMethod
save_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 is a,v,p*v, where a is the asset name, v is the corresponding asset investment value, and p is the corresponding capacity value. Only investable assets are included.
  • assets-investments-energy.csv: The format of each row is a,v,p*v, where a is the asset name, v is the corresponding asset investment value on energy, and p is the corresponding energy capacity value. Only investable assets with a storage_method_energy set to true are included.
  • flows-investment.csv: Similar to assets-investment.csv, but for flows.
  • flows.csv: The value of each flow, per (from, to) flow, rp representative period and timestep. 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, per asset, rp representative period, and timestep. 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.
source
TulipaEnergyModel.solve_modelFunction
solution = 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 asset a. 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 asset a with a storage_method_energy set to true.

    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 asset a for a representative period rp and a time block timesteps_block. The list of time blocks is defined by constraints_partitions, which was used to create the model. To create a vector with all values of storage_level_intra_rp for a given a and rp, 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 asset a for a periods block pb. To create a vector with all values of storage_level_inter_rp for a given a, 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 period rp, and time block timesteps_block. The list of time blocks is defined by graph[(u, v)].partitions[rp]. To create a vector with all values of flow for a given (u, v) and rp, 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)
source
TulipaEnergyModel.solve_model!Method
solution = 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
source