Skip to content

Commit 889f8aa

Browse files
authored
New node StratPeriodDemandSink (#14)
* New sink based on `PeriodDemandSink` * Demand now specified per strategic period * Individual penalties for demand periods
1 parent 18a8d2c commit 889f8aa

15 files changed

Lines changed: 1164 additions & 175 deletions

NEWS.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Release notes
22

3-
## Unversioned
3+
## Version 0.4.0 (2026-07-XX)
44

55
### Breaking changes
66

@@ -14,6 +14,11 @@
1414
* Rewrote `CapacityCostLink` with `PeriodPartition` (introduced in *[`TimeStruct` 0.9.12](https://github.com/sintefore/TimeStruct.jl/releases/tag/v0.9.12)*) to increase flexibility of the link with respect to the time structure.
1515
* Rewriting requires adjustment of the parameters due to changed meaning of some of the values.
1616

17+
### New node `StratPeriodDemandSink`
18+
19+
* Introduced new node type `StratPeriodDemandSink` as subtype of `AbstractPeriodDemandSink`.
20+
* Node to be used for strategic demands and lower and upper bounds for satisfying the demand within a demand period.
21+
1722
## Version 0.3.0 (2026-04-16)
1823

1924
### Breaking changes

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name = "EnergyModelsFlex"
22
uuid = "a81b9388-333d-4b63-81f2-910b060b544c"
33
authors = ["Sigrid Aunsmo, Sigmund Eggen Holm, Jon Vegard Venås, and Per Åslid"]
4-
version = "0.3.0"
4+
version = "0.4.0"
55

66
[deps]
77
EnergyModelsBase = "5d7e687e-f956-46f3-9045-6f5a5fd49f50"

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
> As a consequence, it is advised to read the documentation for each node to identify their usefulness.
1616
> Is is planned to remove some nodes and rewrite the behaviour of other nodes to improve their flexibility.
1717
>
18-
> Among others, using `PeriodDemandSink` and `CapacityCostLink` in combination with `EnergyModelsGUI` results in errors when trying to access fields that have as values `PartitionProfile`.
18+
> Among others, using `PeriodDemandSink`, `StratPeriodDemandSink`, and `CapacityCostLink` in combination with `EnergyModelsGUI` results in errors when trying to access fields that have as values `PartitionProfile`.
1919
> The same holds for variables that are defined over `PeriodPartition`s where you cannot see the results.
2020
2121
## Usage

docs/make.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ makedocs(
5959
],
6060
"Sink nodes"=>Any[
6161
"PeriodDemandSink"=>"nodes/sink/perioddemand.md",
62+
"StratPeriodDemandSink"=>"nodes/sink/stratperioddemand.md",
6263
"LoadShiftingNode"=>"nodes/sink/loadshiftingnode.md",
6364
"MultipleInputSink"=>"nodes/sink/multipleinputsink.md",
6465
"AbstractMultipleInputSinkStrat"=>"nodes/sink/multipleinputsinkstrat.md",

docs/src/index.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@ This package provides several node types that extend the EnergyModelsX interface
3030

3131
### Sink Nodes
3232

33-
- [`PeriodDemandSink`](@ref nodes-perioddemandsink): Allows demand to be met flexibly within a defined time period (e.g. daily energy use).
33+
- [`PeriodDemandSink`](@ref nodes-perioddemandsink): Allows demand to be met flexibly within a defined demand period (*e.g.*, daily energy use).
34+
- [`StratPeriodDemandSink`](@ref nodes-stratperioddemandsink): a variation of `PeriodDemandSink` where the demand must be satisfied within a strategic period with bound on the utilization in demand periods.
3435
- [`LoadShiftingNode`](@ref nodes-loadshiftingnode): Supports discrete batch shifting across time within allowed work shifts.
3536
- [`MultipleInputSink`](@ref nodes-mul_in_sink): Enables flexible use of multiple input resources to meet demand.
3637
- [`BinaryMultipleInputSinkStrat`](@ref nodes-mul_in_sink_strat): Input choice from multiple fuels using binary (exclusive) decisions per period.

docs/src/library/internals/methods-fields.md

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
Pages = ["methods-fields.md"]
88
```
99

10-
## [`PeriodDemandSink` types](@id lib-int-met_field-PeriodDemandSink)
10+
## [`PeriodDemandSink` node](@id lib-int-met_field-PeriodDemandSink)
1111

1212
```@docs
1313
EMF.period_demand
@@ -16,13 +16,21 @@ EMF.periods(n::EMF.AbstractPeriodDemandSink, ts::TS.TimeStructure)
1616
EMF.number_of_periods
1717
```
1818

19-
## [`ActivationCostNode` types](@id lib-int-met_field-ActivationCostNode)
19+
## [`StratPeriodDemandSink` node](@id lib-int-met_field-StratPeriodDemandSink)
20+
21+
```@docs
22+
EMF.strategic_demand
23+
EMF.period_demand_min
24+
EMF.period_demand_max
25+
```
26+
27+
## [`ActivationCostNode` node](@id lib-int-met_field-ActivationCostNode)
2028

2129
```@docs
2230
EMF.activation_consumption
2331
```
2432

25-
## [`CapacityCostLink` types](@id lib-int-met_field-CapacityCostLink)
33+
## [`CapacityCostLink` node](@id lib-int-met_field-CapacityCostLink)
2634

2735
```@docs
2836
EMF.cap_price
@@ -31,7 +39,7 @@ EMF.periods(l::CapacityCostLink, ts::TS.TimeStructure)
3139
EMF.cap_resource
3240
```
3341

34-
## [`Combustion` types](@id lib-int-met_field-Combustion)
42+
## [`Combustion` node](@id lib-int-met_field-Combustion)
3543

3644
```@docs
3745
EMF.limits

docs/src/library/public.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ The following sink node types are implemented in the `EnergyModelsFlex`:
1212

1313
```@docs
1414
PeriodDemandSink
15+
StratPeriodDemandSink
1516
MultipleInputSink
1617
BinaryMultipleInputSinkStrat
1718
ContinuousMultipleInputSinkStrat
@@ -20,7 +21,7 @@ LoadShiftingNode
2021

2122
## [Source `Node` types](@id lib-pub-source-node)
2223

23-
The following source node type is implemented in the `EnergyModelsFlex`:
24+
The following source node type are implemented in the `EnergyModelsFlex`:
2425

2526
```@docs
2627
PayAsProducedPPA

docs/src/nodes/sink/perioddemand.md

Lines changed: 15 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
# [PeriodDemandSink node](@id nodes-perioddemandsink)
22

33
[`PeriodDemandSink`](@ref) 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.
4-
**A *period* is thus a consecutive range of operational periods, that together will model, *e.g.*, a day or a week etc.**
4+
A *demand period* is a consecutive range of operational periods, that together will model, *e.g.*, a day, a week or comparable.
55

66
This node can, *e.g.*, be combined with [`MinUpDownTimeNode`](@ref), to allow production to be moved to the time of the day when it is cheapest because of, *e.g.*, energy or production costs.
77

88
!!! tip "Example"
9-
This node is included in an [example](@ref examples-flexible_demand) to demonstrate flexible demand.
9+
This node is included in an *[example](@ref examples-flexible_demand)* to demonstrate flexible demand.
1010

1111
!!! warning "TimeStructure for node"
12-
This node is designed for **uniform or repetitive duration of operational periods**.
12+
This node requires considerations of the operational time structure and the chosen demand period duration.
1313
Irregular durations may cause misalignment of shifted loads, especially if the field `period_duration` does not align with the chosen [`SimpleTimes`](@extref TimeStruct.SimpleTimes) structure representing the operational periods.
1414

1515
!!! warning "`PeriodDemandSink` and `EnergyModelsGUI`"
@@ -41,8 +41,7 @@ The standard fields are given as:
4141
In addition, it is crucial that the sum of both values in each demand period is larger than 0 to avoid an unconstrained model.
4242

4343
!!! warning "Chosen values"
44-
The implementation is relative to the chosen `period_duration` (see below).
45-
If the period duration is ``24``, then the cost is for the unsatisfied demand within the ``24`` demand period, multiplied with the probability and the repetitons within a strategic period.
44+
The implementation for the demand period is relative to the chosen duration of a strategic period while the demand period deficit and surplus is scaled to a strategic period in the calculation.
4645

4746
- **`input::Dict{<:Resource,<:Real}`**:\
4847
The field `input` includes [`Resource`](@extref EnergyModelsBase.Resource)s with their corresponding conversion factors as dictionaries.\
@@ -65,23 +64,23 @@ The standard fields are given as:
6564
[`AbstractPeriodDemandSink`](@ref EnergyModelsFlex.AbstractPeriodDemandSink)s require additional fields to specify both the periods and their respective demands:
6665

6766
- **`period_duration::TimeProfile`**:\
68-
Defines the total duration of a single demand period.\
67+
Defines the total duration of the demand periods.
6968
For instance, if the duration of 1 of the operational time structure is 1 hour and `period_duration = FixedProfile(24)`, then each demand period spans one day.
7069
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 given the available capacity.\
7170
Due to a constructor, it can either be specified as number (the same duration in all demand periods), as a vector (varying duration of each demand period), or as a time profile (*e.g.*, varying period durations due to varying operational time structures).
7271
It cannot be specified as `OperationalProfile`.
7372

7473
- **`period_demand::TimeProfile`**:\
7574
The total demand to be met during each demand period.
76-
The length of this time profile should match the number of periods (*e.g.*, days) in the time structure.
77-
If the time structure represents one year with hourly resolution and the demand periods correspond to a day, this time profile must then have 365 elements.
75+
The length of this time profile should match the number of demand periods (*e.g.*, days) in the time structure.
76+
If the time structure represents one year with hourly resolution and the demand periods correspond to a day, this time profile must then have 365 elements.\
77+
It cannot be specified as `OperationalProfile`.
7878

7979
It is best to utilize the [`PartitionProfile`](@extref TimeStruct.PartitionProfile) type if the demand is varying.
80-
If it is constant, you can also utilize [`StrategicProfile`][@extref TimeStruct.StrategicProfile], [`RepresentativeProfile`][@extref TimeStruct.RepresentativeProfile], or [`ScenarioProfile`][@extref TimeStruct.ScenarioProfile], depending on your chosen time structure.
81-
It cannot be specified as `OperationalProfile`.
80+
If it is constant, you can also utilize [`StrategicProfile`](@extref TimeStruct.StrategicProfile), [`RepresentativeProfile`](@extref TimeStruct.RepresentativeProfile), or [`ScenarioProfile`](@extref TimeStruct.ScenarioProfile), depending on your chosen time structure.
8281

8382
!!! warning "Time consistency"
84-
Ensure that the `period_demand` time profile length aligns with the operational time horizon duration divided by `period_duration`
83+
Ensure that the `period_demand` time profile length aligns with the periods specified by `period_duration`.
8584
Mismatches can lead to indexing errors or inconsistent demand enforcement.
8685

8786
These fields are at the 3ʳᵈ and 4ᵗʰ position below the field `cap` as shown in [`PeriodDemandSink`](@ref).
@@ -117,13 +116,12 @@ The variables include:
117116

118117
#### [Additional variables](@id nodes-perioddemandsink-math-add)
119118

120-
[`AbstractPeriodDemandSink`](@ref EnergyModelsFlex.AbstractPeriodDemandSink) nodes declare in addition several variables through dispatching on the method [`EnergyModelsBase.variables_element()`](@ref) for including constraints for deficits and surplus for individual resources as well as what the fraction satisfied by each resource.
121-
These variables are for a [`AbstractPeriodDemandSink`](@ref EnergyModelsFlex.AbstractPeriodDemandSink) node ``n`` in demand periods ``t_pd``:
119+
[`AbstractPeriodDemandSink`](@ref EnergyModelsFlex.AbstractPeriodDemandSink) nodes declare in addition several variables through dispatching on the method [`EnergyModelsBase.variables_element()`](@ref) for including constraints for deficits and surplus for individual demand periods.
122120

123121
- ``\texttt{demand\_sink\_surplus}[n, t_pd]``:\
124-
Surplus of energy delivered beyond the required `period_demand` of demand period `t_pd` .
122+
Surplus of energy delivered beyond the required `period_demand` in demand period `t_pd` .
125123
- ``\texttt{demand\_sink\_deficit}[n, t_pd]``:\
126-
Deficit of energy delivered relative to the `period_demand` of demand period `t_pd` .
124+
Deficit of energy delivered relative to the `period_demand` in demand period `t_pd` .
127125

128126
### [Constraints](@id nodes-perioddemandsink-math-con)
129127

@@ -193,8 +191,8 @@ As a consequence, `constraints_opex_var` requires as well a new method as we onl
193191

194192
```math
195193
\begin{aligned}
196-
\texttt{opex\_var}[n, t_{inv}] = \sum_{t_{pd} ∈ periods(t_{inv})}(& \texttt{demand\_sink\_surplus}[n, t_{pd}] \times \texttt{surplus\_penalty}(n, t_{pd}) + \\
197-
& \texttt{demand\_sink\_deficit}[n, t_{pd}] \times \texttt{deficit\_penalty}(n, t_{pd})) \times \\
194+
\texttt{opex\_var}[n, t_{inv}] = & \sum_{t_{pd} ∈ periods(t_{inv})}(\texttt{demand\_sink\_surplus}[n, t_{pd}] \times \texttt{surplus\_penalty}(n, t_{pd}) + {}\\
195+
& \phantom{\sum_{t_{pd} ∈ periods(t_{inv})}(} \texttt{demand\_sink\_deficit}[n, t_{pd}] \times \texttt{deficit\_penalty}(n, t_{pd})) \times {} \\
198196
& scale\_op\_sp(t_{inv}, first(t_{pd})) / duration(first(t_{pd}))
199197
\end{aligned}
200198
```

0 commit comments

Comments
 (0)