EnergyModelsX
EnergyModelsX (EMX) is a flexible multi-horizon energy system optimization framework using JuMP. It utilizes TimeStruct from the beginning and influenced some of the added features of TimeStruct. Its implementation is outlined in the following sections.
Implementation of the core structures
The core package EnergyModelsBase differentiates between variables indexed over strategic periods and variables indexed over operational periods, similar to the battery sizing example. As an example, consider the following two functions for variable declaration for a given 𝒯::TimeStructure:
variables_capacity(m, 𝒩::Vector{<:Node}, 𝒳ᵛᵉᶜ, 𝒯, modeltype::EnergyModel)with, among others,@variable(m, cap_use[𝒩ᶜᵃᵖ, 𝒯] >= 0) @variable(m, cap_inst[𝒩ᶜᵃᵖ, 𝒯] >= 0)variables_opex(m, 𝒩::Vector{<:Node}, 𝒳ᵛᵉᶜ, 𝒯, modeltype::EnergyModel)with, among others,𝒯ᴵⁿᵛ = strategic_periods(𝒯) @variable(m, opex_var[𝒩ᵒᵖᵉˣ, 𝒯ᴵⁿᵛ])
These two functions highlight the simplicity of using TimeStruct, as the individual time structures allow for iterations within JuMP macros. In addition, it simplifies the index sets, as changing the number of intermediate time structures through incorporating, e.g., OperationalScenarios, does not require changes to the variable declarations. The change in the structure through the incorporation of the operational scenarios is instead available within the individual TimePeriod type.
The same is also true for constraint functions, as shown in constraints_capacity(m, n::Node, 𝒯::TimeStructure, modeltype::EnergyModel):
@constraint(m, [t ∈ 𝒯], m[:cap_use][n, t] <= m[:cap_inst][n, t])Variables whose values are dependent on the previous operational period are only available for Storage nodes. Its implementation is rather complex, but it is entirely relying on the withprev functionality to decide whether it is the first operational period in a different TimeStructure as well as how it must behave in this situation.
The individual operational periods are linked in EnergyModelsBase through the internal function scale_op_sp for the multiplication
\[duration(t) * multiple\_strat(t_{inv}, t) * probability(t)\]
where $t$ corresponds to an operational period and $t_{inv}$ to a strategic period.
Advanced utilization
withprev and its application
As outlined, neither OperationalScenarios nor RepresentativePeriods were available in the initial development of EMX. However, the adjustments toward including these were limited to constraints that require the previous periods, that is, constraints declared through the withprev functionality. All other constraints did not require any changes, as the respective TimePeriods included the required information.
The implementation of the Storage level balance provides an example of how TimeStruct can be used. It iterates through all potential subtypes of the given TimeStructure. Multiple dispatch is included to identify whether the TimeStructure includes OperationalScenarios or RepresentativePeriods. If this is the case, additional constraints can be incorporated. Due to the iteration (and storing) of all previous periods, it is possible to identify exactly what constraint should be utilized for the first operational period in a given TimeStructure.
While it can be tempting to design models from the initial stage to include both operational scenarios and representative periods, it is beneficial to avoid including these. TimeStruct simplifies the introduction of either TimeStructure at a later stage. It is hence significantly easier to develop a model without considering complex time structures.
If you do not have any constraints depending on the previous periods, you do not even have to make any changes to your model when including OperationalScenarios or RepresentativePeriods.
Chunks for unit commitment
The functionality of TimeStruct for chunks based on the minimum required time is utilized in the unit commitment constraints of Reformer nodes in EnergyModelsHydrogen. In this context, we require a minimum time for starting the node, shutting the node down, and when the node is offline due to limitations in the dynamics of the chemical plant. The used approach is similar to the Storage level balance, utilizing the withprev functionality for the majority of the time structures. However, once at the lowest level, chunk_duration is used in addition to provide limits on changes between the different states. The eltypes of the iterator allow for further iterations to have access to the number of operational periods.
Including strategic uncertainty
EMX was not tested to also include strategic uncertainty as described in TwoLevelTree structure. The basic functionalities of EnergyModelsBase and the majority of the developed packages do, however, not have any problems with incorporating strategic uncertainty, as the strategic periods are not linked. In this case, the internal structure of TimeStruct allows the direct inclusion of strategic uncertainty without changes to the model. The exception is when using the withprev functionality on strategic periods, as is the case for the investments in EnergyModelsInvestments. However, it is expected that the inclusion does not require any changes directly to the code structure due to the function overload on the withprev functionality.