PeriodDemandSink node
PeriodDemandSink
nodes represent flexible demand sinks where demand must be fulfilled within defined periods (e.g. daily or weekly), rather than in each individual operational time step. A period is thus a consecutive range of operational periods, that together will model, e.g., a day or a week etc.
This node can, e.g., be combined with MinUpDownTimeNode
, to allow production to be moved to the time of the day when it is cheapest because of, e.g., energy or production costs.
This node is included in an example to demonstrate flexible demand.
This node is designed for uniform or repetitive duration of operational periods. Irregular durations may cause misalignment of shifted loads, especially if the field period_length
does not align with the chosen SimpleTimes
structure representing the operational periods.
Introduced type and its fields
The PeriodDemandSink
node extends the Sink
functionality by introducing aggregated demand over fixed-length periods. This is useful for representing flexible loads like electric vehicle charging or industrial batch processes, where exact timing of delivery is flexible.
PeriodDemandSink
is defined as a subtype of AbstractPeriodDemandSink
, and constraints are put on this supertype. By subtyping AbstractPeriodDemandSink
, you can easily extend the functionality of this node.
Standard fields
The standard fields are given as:
id
:
The fieldid
is only used for providing a name to the node.cap::TimeProfile
:
The maximum amount of demand that can be met in each operational period. This acts as a capacity on instantaneous delivery, whileperiod_demand
enforces total energy delivered within the chosen period.penalty::Dict{Symbol,<:TimeProfile}
:
The penalty dictionary is used for providing penalties for soft constraints to allow for both over and under delivering the demand.
It must include the fields:surplus
and:deficit
. In addition, it is crucial that the sum of both values is larger than 0 to avoid an unconstrained model.Chosen values The current implementation does not represent the proper cost due to the summation. Instead, you must consider the duration of an operational period and the field
period_length
when providing a value. In this case, the value should be multiplied by $1/period\_length(n) \times duration(t)$.input::Dict{<:Resource,<:Real}
:
The fieldinput
includesResource
s with their corresponding conversion factors as dictionaries.
All values have to be non-negative.data::Vector{Data}
:
An entry for providing additional data to the model. In the current version, it is used for both providingEmissionsData
and additional investment data whenEnergyModelsInvestments
is used.Included constructor The field
data
is not required as we include a constructor when the value is excluded.Using `CaptureData` As a
Sink
node does not have any output, it is not possible to utilizeCaptureData
. If you still plan to specify it, you will receive an error in the model building.
Unlike RefSink
, the delivery of demand here is flexible within each demand period. This is helpful for modeling demand that can shift within a day or week.
Additional fields
AbstractPeriodDemandSink
s require additional fields to specify both the periods and their respective demands:
period_length::Int
:
Defines how many operational periods are included in a single demand period.
For instance, if the duration of the operational periods is 1 hour andperiod_length = 24
, then each demand period spans one day. The demand of this node (for a given day, see below) must then be filled on a daily basis, without any restrictions on when during the day the demand must be filled.period_demand::Vector{<:Real}
:
The total demand to be met during each demand period. The length of this vector should match the number of periods (e.g., days) in the time structure. If the time structure represents on year with hourly resolution, this vector must then have 365 elements.
These fields are at the 2ⁿᵈ and 3ʳᵈ position below the field id
as shown in PeriodDemandSink
.
Mathematical description
In the following mathematical equations, we use the name for variables and functions used in the model. Variables are in general represented as
$\texttt{var\_example}[index_1, index_2]$
with square brackets, while functions are represented as
$func\_example(index_1, index_2)$
with paranthesis.
Variables
Standard variables
The PeriodDemandSink
nodes utilize all standard variables from a Sink
node, as described on the page Optimization variables. The variables include:
- $\texttt{opex\_var}$
- $\texttt{opex\_fixed}$
- $\texttt{cap\_use}$
- $\texttt{cap\_inst}$
- $\texttt{flow\_out}$
- $\texttt{sink\_surplus}$
- $\texttt{sink\_deficit}$
- $\texttt{emissions\_node}$ if
EmissionsData
is added to the fielddata
Additional variables
AbstractPeriodDemandSink
nodes declare in addition several variables through dispatching on the method EnergyModelsBase.variables_element()
for including constraints for deficits and surplus for individual resources as well as what the fraction satisfied by each resource. These variables are for a AbstractPeriodDemandSink
node $n$ in demand periods $i$:
- $\texttt{demand\_sink\_surplus}[n, i]$:
Surplus of energy delivered beyond the requiredperiod_demand
in demand periodi
. - $\texttt{demand\_sink\_deficit}[n, i]$:
Deficit of energy delivered relative to theperiod_demand
in periodi
.
Constraints
The following sections omit the direct inclusion of the vector of AbstractPeriodDemandSink
nodes. Instead, it is implicitly assumed that the constraints are valid $\forall n ∈ N$ for all AbstractPeriodDemandSink
types if not stated differently. In addition, all constraints are valid $\forall t \in T$ (that is in all operational periods) or $\forall t_{inv} \in T^{Inv}$ (that is in all investment periods).
Standard constraints
AbstractPeriodDemandSink
utilize in general the standard constraints that are implemented for a Sink
node as described in the documentaiton of EnergyModelsBase
. These standard constraints are:
constraints_capacity_installed
:\[\texttt{cap\_inst}[n, t] = capacity(n, t)\]
Using investments The function
constraints_capacity_installed
is also used inEnergyModelsInvestments
to incorporate the potential for investment. Nodes with investments are then no longer constrained by the parameter capacity.constraints_flow_in
:\[\texttt{flow\_in}[n, t, p] = inputs(n, p) \times \texttt{cap\_use}[n, t] \qquad \forall p \in inputs(n)\]
constraints_opex_fixed
:
The current implementation fixes the fixed operating expenses of a sink to 0.\[\texttt{opex\_fixed}[n, t_{inv}] = 0\]
constraints_data
:
This function is only called for specified additional data, see above.
The function constraints_capacity
is extended with a new method to account for the calculation of the period demand deficit and surplus through:
\[\texttt{cap\_use}[n, t] + \texttt{sink\_deficit}[n, t] = \texttt{cap\_inst}[n, t] + \texttt{sink\_surplus}[n, t]\]
\[\begin{aligned} \texttt{demand\_sink\_deficit}[n, i] + & \sum_{t \in P_i} \texttt{cap\_use}[n,t] = \\ & \texttt{demand\_sink\_surplus}[n, i] + period\_demand(n, i) \end{aligned}\]
where $P_i$ is the set of operational periods in demand period $i$.
As a consequence, constraints_opex_var
requires as well a new method as we only consider the deficit within a complete period:
\[\begin{aligned} \texttt{opex\_var}[n, t_{inv}] = \sum_{t ∈ t_{inv}}(& \texttt{demand\_sink\_surplus}[n, i_t] \times \texttt{surplus\_penalty}(n, t) + \\ & \texttt{demand\_sink\_deficit}[n, i_t] \times \texttt{deficit\_penalty}(n, t)) \times \\ & scale\_op\_sp(t_{inv}, t) \end{aligned}\]
where $i_t$ is the period index such that $t \in P_{i_t}$.
The function $scale\_op\_sp(t_{inv}, t)$ calculates the scaling factor between operational and investment periods. It also takes into account potential operational scenarios and their probability as well as representative periods.