Optimization variables
EnergyModelsBase creates a variety of default variables for the individual nodes and edges. These default variables are in general also created when new Nodes or Links are developed. It is not necessary to utilize all of the default variables in the individual nodes. It is, however, recommended to include constraints or fixes in this situation using either the @constraint macro or, alternatively, the JuMP function fix(x, value). The latter is the recommended approach.
The majority of the variables in EnergyModelsBase are rate variables. This implies that they are calculated for either an operational period duration of 1, when indexed over operational period $t$, or a strategic period duration of 1, when indexed over strategic period $t_\texttt{inv}$. Typical units for rates are MW for energy streams, tonne/hour for mass streams, tonne/year for strategic emissions, and €/year for operating expenses. In this example, the duration of an operational period of 1 corresponds to an hour, while the duration of a strategic period of 1 corresponds to a year.
Variables that are energy/mass based have that property highlighted in the documentation below. In the standard implementation of EnergyModelsBase, this is only the case for the level of a Storage node, the change of level of the node, and its installed capacity.
Rate variables can also be translated to mass/energy variables. As an example, the total quantity of resource $p$ that flows into node $n$ during operational period $t$ can be calculated by
m[:flow_in][n, t, p] * duration(t)The multiplication then leads to an energy/mass quantity instead of an energy/mass flow.
The coupling of strategic and operational periods can be achieved through the function scale_op_sp(t_inv, t). This function allows for considering the scaling of the operational periods within a strategic period.
Variables are in general not scaled. The reason for this approach is to simplify the comparison of individual values independently of the chosen time structure. The following rules apply:
- Variables do not consider the branch probability when utilizing the
TwoLevelTreetime structure. This is, e.g., the case for the strategic emission variables or the variable operating expenses that apply per scenario/branch. The branch probability is, however, included when calculating the objective function. - Operational variables do not consider the number of repetitions introduced through the parameter
op_per_stratas described within the section on strategic periods. This also holds when usingRepresentativePeriods. - Operational variables do not consider the probability of an operational scenario when using
OperationalScenarios.
However, representative variables (indexed over a representative period) include the number of repetitions of the representative period. This may be changed in the next breaking release to be consistent with all other variables.
Operational cost variables
Operational cost variables are included to account for operating expenses (OPEX) of the model. These costs are purely dependent on either the use or the installed capacity of a node $n$. All nodes $n$ (except Availability-nodes) have the following variables representing the operational costs of the nodes:
- $\texttt{opex\_var}[n, t_\texttt{inv}]$: Variable OPEX of node $n$ in strategic period $t_\texttt{inv}$.
- $\texttt{opex\_fixed}[n, t_\texttt{inv}]$: Fixed OPEX of node $n$ in strategic period $t_\texttt{inv}$.
The variable OPEX is a cost that derives from using a technology. It is calculated using the function constraints_opex_var. In general, it represents unmodelled feed to a process and the associated costs. Examples are catalyst replacement, cooling water or process water. The variable OPEX can also be utilized to provide values for a profit through using a technology.
The fixed OPEX is a cost that is independent of the usage of a technology. Instead, it is only dependent on the installed capacity. It is calculated using the function constraints_opex_fixed. It represents fixed costs such as labour costs, maintenance, insurance, and taxes.
We also introduce the potential for links with operational costs. By default, links do not introduce new variables. Operational cost variables are only created for a link $l$ if the function has_opex(n::Link) returns true. The following link variables are then declared representing the operational costs of the links:
- $\texttt{link\_opex\_var}[l, t_\texttt{inv}]$: Variable OPEX of link $l$ in strategic period $t_\texttt{inv}$.
- $\texttt{link\_opex\_fixed}[l, t_\texttt{inv}]$: Fixed OPEX of link $l$ in strategic period $t_\texttt{inv}$.
All links introduced in EnergyModelsBase do not allow for operational costs. If you plan to introduce a link with operational costs, you have to create a new method for the function has_opex for your introduced link.
Capacity variables
Capacity variables focus on both the capacity usage and installed capacity. The capacity variables are also created for all nodes except for Availability nodes. Capacity variables are differentiated between Storage nodes and all other Nodes. The implementation of the capacity variables allows for a time-varying capacity during an operational period for inclusion of variations in the demand in Sink nodes. It is, however, not possible to invest in a time-varying capacity.
The following capacity variables are created for node types other than Storage:
- $\texttt{cap\_use}[n, t]$: Absolute capacity usage of node $n$ at operational period $t$, and
- $\texttt{cap\_inst}[n, t]$: Installed capacity of node $n$ at operational period $t$.
The capacity usage $\texttt{cap\_use}$ is the utilization of the installed capacity. It is declared in absolute values to avoid bilinearities when investing in capacities. It is normally constrained by the variable $\texttt{cap\_inst}$ of the individual nodes, except for Sink nodes.
The capacity variables for Storage nodes differentiate between storage capacity (stored energy in the Storage node) and rate of storage (storage rate of a Storage node). The latter is furthermore differentiated between charging and discharging a Storage node. A key reason for this approach is that it is in general possible to invest both in the storage rate (e.g., the AC-DC transformer required in battery storage) as well as in the storage capacity (e.g., the number of cells in battery storage). The same also holds for pumped hydro storage and storage of gases, where there is a further differentiation between the maximum charging and discharging rates. The differentiation leads to the following variables for Storage nodes:
- $\texttt{stor\_level}[n, t]$: Absolute level of energy/mass stored in a
Storagenode $n$ at operational period $t$ with a typical unit of GWh or t, - $\texttt{stor\_level\_inst}[n, t]$: Installed storage capacity in a
Storagenode $n$ at operational period $t$ , that is the upper bound for the variable $\texttt{stor\_level}[n, t]$, with a typical unit of GWh or t, - $\texttt{stor\_charge\_use}[n, t]$: Usage of the charging rate of a
Storagenode $n$ at operational period $t$ with a typical unit of GW or t/h, - $\texttt{stor\_charge\_inst}[n, t]$: Maximum available charging rate of a
Storagenode $n$ at operational period $t$, that is the upper bound for the variable $\texttt{stor\_charge\_use}[n, t]$, with a typical unit of GW or t/h. - $\texttt{stor\_discharge\_use}[n, t]$: Usage of the discharging rate of a
Storagenode $n$ at operational period $t$ with a typical unit of GW or t/h, and - $\texttt{stor\_discharge\_inst}[n, t]$: Maximum available discharging rate of a
Storagenode $n$ at operational period $t$, that is the upper bound for the variable $\texttt{stor\_discharge\_use}[n, t]$, with a typical unit of GW or t/h.
It is not necessary that a Storage node has a charge and discharge capacity. It is possible to not specify a capacity for charging and discharging. In this instance, the variables for the installed capacities are omitted, and the charging and discharging usage is unlimited.
The storage level is always defined for the end of the operational period it is indexed over. In addition, there are three variables for the storage level that behave slightly differently:
- $\texttt{stor\_level\_Δ\_op}[n, t]$: Change of the absolute level of energy/mass stored in a
Storagenode $n$ in operational period $t$ with a typical unit of GWh or t, and - $\texttt{stor\_level\_Δ\_rp}[n, t_{rp}]$: Change of the absolute level of energy/mass stored in a
Storagenode $n$ in representative period $t_{rp}$ with a typical unit of GWh or t, and - $\texttt{stor\_level\_Δ\_sp}[n, t_\texttt{inv}]$: Change of the absolute level of energy/mass stored in a
Storagenode $n$ in strategic period $t_\texttt{inv}$ with a typical unit of GWh or t.
These variables are introduced to track the change in the storage level during an operational period, a representative period, or an investment period, respectively. They can be considered as helper variables to account for the duration of the operational period as well as the total change within a representative period. $\texttt{stor\_level\_Δ\_rp}$ is only declared if the TimeStructure includes RepresentativePeriods while $\texttt{stor\_level\_Δ\_sp}$ is introduced as an empty SparseVariables container. The application of RepresentativePeriods is explained in How to use TimeStruct.jl. The utilization of $\texttt{stor\_level\_Δ\_sp}$ requires including the following loop in the function variables_node for the given Storage array 𝒩ˢᵘᵇ::Vector{<:NewStorageNode}:
for t_inv ∈ 𝒯ᴵⁿᵛ, n ∈ 𝒩ˢᵘᵇ
insertvar!(m[:stor_level_Δ_sp], n, t_inv)
endWe also introduce the potential for links with capacities. By default, links do not introduce new variables. The capacity variable is only created for a link $l$ if the function has_capacity(n::Link) returns true. The following link variable is then declared to represent the capacity of links:
- $\texttt{link\_cap\_inst}[l, t]$: Installed capacity of link $l$ at operational period $t$.
All links introduced in EnergyModelsBase do not allow for a capacity limiting the transfer. If you plan to introduce a link with a capacity, you have to create a new method for the function has_capacity for your introduced link.
The variables $\texttt{cap\_inst}$, $\texttt{stor\_charge\_inst}$, $\texttt{stor\_level\_inst}$, $\texttt{stor\_discharge\_inst}$, and $\texttt{link\_cap\_inst}$ are used in EnergyModelsInvestment to allow for investments in the capacity of individual nodes.
Flow variables
Flow variables correspond to the input to and output from both technology nodes and links. They are always positive to avoid backflow.
The following flow variables are defined for the nodes:
- $\texttt{flow\_in}[n, t, p]$ represents the flow rate of resource $p$ into node $n$ at operational period $t$. It is created for subtypes of
NetworkNodeandSinkbased on the fieldinputof the composite type. - $\texttt{flow\_out}[n, t, p]$ represents the flow rate of resource $p$ out of node $n$ at operational period $t$. It is created for subtypes of
SourceandNetworkNodebased on the fieldoutputof the composite type.
Links also have corresponding flow variables given by:
- $\texttt{link\_in}[l, t, p]$ represents the flow rate of resource $p$ into link $l$ at operational period $t$, and
- $\texttt{link\_out}[l, t, p]$ represents the flow rate of resource $p$ out of link $l$ at operational period $t$.
The resource index $p$ is created based on the intersection of the output of the input node $n_{in}$ and the input of the output node $n_{out}$ through the function EMB.link_res(l::Link). Mathematically, this is given by
$\mathcal{P}^{link} = \mathcal{P}^{n^{out}_{in}} \cap \mathcal{P}^{n^{in}_{out}}.$
It is also possible to create a new method for this function to limit the resources a link can transport.
Emission variables
Emission variables are used to account for emissions of the individual technologies. Resources that can be emitted are defined through the type ResourceEmit. Nodes do not necessarily have associated emission variables. Emission variables are only created for a node $n$ if the function has_emissions(n::EMB.Node) returns true. This is the case for all nodes that have EmissionsData within their field data, as well as for a RefStorage node if a ResourceEmit is stored. The following node variable is then declared for all emission resource 𝒫ᵉᵐ:
- $\texttt{emissions\_node}[n, t, p_\texttt{em}]$: Emissions of node $n$ at operational period $t$ of emission resource $p_\texttt{em}$.
Similarly, it is not necessary that links have associated emission variables. Emission variables are only created for a link $l$ if the function has_emissions(n::Link) returns true. The following link variable is then declared for all emission resource 𝒫ᵉᵐ:
- $\texttt{emissions\_link}[n, t, p_\texttt{em}]$: Emissions of link $l$ at operational period $t$ of emission resource $p_\texttt{em}$.
All links introduced in EnergyModelsBase do not allow for emissions. If you plan to introduce a link with emissions, you have to create a new method for the function has_emissions for your introduced link.
We have not implemented a similar approach as for nodes. It is however planned to allow for transmission emissions in the near future, similar to the concept employed for process emissions for nodes.
In addition, EnergyModelsBase declares the following variables for the global emissions:
- $\texttt{emissions\_total}[t, p_\texttt{em}]$: Total emissions of
ResourceEmit$p_\texttt{em}$ in operational period $t$, and - $\texttt{emissions\_strategic}[t_\texttt{inv}, p_\texttt{em}]$: Total emissions of
ResourceEmit$p_\texttt{em}$ in strategic period $t_\texttt{inv}$.
These emission variables introduce limits on the total emissions of a resource through the field emission_limit of an EnergyModel in the function EMB.variables_emission.
Sink variables
Sink nodes are somewhat different from the other nodes because they have additional variables associated with them. A key point here is to keep the overall mass balance intact while allowing for both overfulfilling and not meeting the demand. These variables are:
- $\texttt{sink\_surplus}[n, t]$: Surplus of energy/mass to
Sink$n$ at operational period $t$, and - $\texttt{sink\_deficit}[n, t]$: Deficit of energy/mass to
Sink$n$ at operational period $t$.
The surplus in a sink corresponds to the energy/mass that is supplied to the sink in addition to the demand. The deficit in a sink corresponds to the energy/mass that is not supplied to the sink although the demand is specified. Both variables correspond to slack variables of the optimization problem. They simplify the problem and can make certain types of formulations feasible. It is possible to provide penalties for both surplus and deficit. This is implemented through the field penalty in the RefSource node.
Node types and respective variables
As outlined in the introduction, EnergyModelsBase declares different variables for each Node. These variables are for the individual nodes given in the subsections below.
Source
- $\texttt{opex\_var}$
- $\texttt{opex\_fixed}$
- $\texttt{cap\_use}$
- $\texttt{cap\_inst}$
- $\texttt{flow\_out}$
- $\texttt{emissions\_node}$ if
EmissionsDatais added to the fielddata
NetworkNode, except for Storage
- $\texttt{opex\_var}$
- $\texttt{opex\_fixed}$
- $\texttt{cap\_use}$
- $\texttt{cap\_inst}$
- $\texttt{flow\_in}$
- $\texttt{flow\_out}$
- $\texttt{emissions\_node}$ if
EmissionsDatais added to the fielddata
Storage
- $\texttt{opex\_var}$
- $\texttt{opex\_fixed}$
- $\texttt{stor\_level}$
- $\texttt{stor\_level\_inst}$
- $\texttt{stor\_charge\_use}$
- $\texttt{stor\_charge\_inst}$, if the
Storagenode has a field:chargewith theStorageParameterscorresponding to capacity storage parameters - $\texttt{stor\_discharge\_use}$
- $\texttt{stor\_discharge\_inst}$, if the
Storagenode has a field:dischargewith theStorageParameterscorresponding to capacity storage parameters - $\texttt{stor\_level\_Δ\_op}$
- $\texttt{stor\_level\_Δ\_rp}$ if the
TimeStructincludesRepresentativePeriods - $\texttt{stor\_level\_Δ\_sp}$ if the function
variables_nodeis declared for the newStoragetype - $\texttt{flow\_in}$
- $\texttt{flow\_out}$
- $\texttt{emissions\_node}$ if
ResourceEmitis stored
Sink
- $\texttt{opex\_var}$
- $\texttt{opex\_fixed}$
- $\texttt{cap\_use}$
- $\texttt{cap\_inst}$
- $\texttt{flow\_in}$
- $\texttt{sink\_surplus}$
- $\texttt{sink\_deficit}$
- $\texttt{emissions\_node}$ if
EmissionsDatais added to the fielddata