Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion NEWS.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Release notes

## Unversioned
## Version 0.4.0 (2026-07-XX)

### Breaking changes

Expand All @@ -14,6 +14,11 @@
* 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.
* Rewriting requires adjustment of the parameters due to changed meaning of some of the values.

### New node `StratPeriodDemandSink`

* Introduced new node type `StratPeriodDemandSink` as subtype of `AbstractPeriodDemandSink`.
* Node to be used for strategic demands and lower and upper bounds for satisfying the demand within a demand period.

## Version 0.3.0 (2026-04-16)

### Breaking changes
Expand Down
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name = "EnergyModelsFlex"
uuid = "a81b9388-333d-4b63-81f2-910b060b544c"
authors = ["Sigrid Aunsmo, Sigmund Eggen Holm, Jon Vegard Venås, and Per Åslid"]
version = "0.3.0"
version = "0.4.0"

[deps]
EnergyModelsBase = "5d7e687e-f956-46f3-9045-6f5a5fd49f50"
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
> As a consequence, it is advised to read the documentation for each node to identify their usefulness.
> Is is planned to remove some nodes and rewrite the behaviour of other nodes to improve their flexibility.
>
> Among others, using `PeriodDemandSink` and `CapacityCostLink` in combination with `EnergyModelsGUI` results in errors when trying to access fields that have as values `PartitionProfile`.
> Among others, using `PeriodDemandSink`, `StratPeriodDemandSink`, and `CapacityCostLink` in combination with `EnergyModelsGUI` results in errors when trying to access fields that have as values `PartitionProfile`.
> The same holds for variables that are defined over `PeriodPartition`s where you cannot see the results.

## Usage
Expand Down
1 change: 1 addition & 0 deletions docs/make.jl
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ makedocs(
],
"Sink nodes"=>Any[
"PeriodDemandSink"=>"nodes/sink/perioddemand.md",
"StratPeriodDemandSink"=>"nodes/sink/stratperioddemand.md",
"LoadShiftingNode"=>"nodes/sink/loadshiftingnode.md",
"MultipleInputSink"=>"nodes/sink/multipleinputsink.md",
"AbstractMultipleInputSinkStrat"=>"nodes/sink/multipleinputsinkstrat.md",
Expand Down
3 changes: 2 additions & 1 deletion docs/src/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ This package provides several node types that extend the EnergyModelsX interface

### Sink Nodes

- [`PeriodDemandSink`](@ref nodes-perioddemandsink): Allows demand to be met flexibly within a defined time period (e.g. daily energy use).
- [`PeriodDemandSink`](@ref nodes-perioddemandsink): Allows demand to be met flexibly within a defined demand period (*e.g.*, daily energy use).
- [`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.
- [`LoadShiftingNode`](@ref nodes-loadshiftingnode): Supports discrete batch shifting across time within allowed work shifts.
- [`MultipleInputSink`](@ref nodes-mul_in_sink): Enables flexible use of multiple input resources to meet demand.
- [`BinaryMultipleInputSinkStrat`](@ref nodes-mul_in_sink_strat): Input choice from multiple fuels using binary (exclusive) decisions per period.
Expand Down
16 changes: 12 additions & 4 deletions docs/src/library/internals/methods-fields.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
Pages = ["methods-fields.md"]
```

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

```@docs
EMF.period_demand
Expand All @@ -16,13 +16,21 @@ EMF.periods(n::EMF.AbstractPeriodDemandSink, ts::TS.TimeStructure)
EMF.number_of_periods
```

## [`ActivationCostNode` types](@id lib-int-met_field-ActivationCostNode)
## [`StratPeriodDemandSink` node](@id lib-int-met_field-StratPeriodDemandSink)

```@docs
EMF.strategic_demand
EMF.period_demand_min
EMF.period_demand_max
```

## [`ActivationCostNode` node](@id lib-int-met_field-ActivationCostNode)

```@docs
EMF.activation_consumption
```

## [`CapacityCostLink` types](@id lib-int-met_field-CapacityCostLink)
## [`CapacityCostLink` node](@id lib-int-met_field-CapacityCostLink)

```@docs
EMF.cap_price
Expand All @@ -31,7 +39,7 @@ EMF.periods(l::CapacityCostLink, ts::TS.TimeStructure)
EMF.cap_resource
```

## [`Combustion` types](@id lib-int-met_field-Combustion)
## [`Combustion` node](@id lib-int-met_field-Combustion)

```@docs
EMF.limits
Expand Down
3 changes: 2 additions & 1 deletion docs/src/library/public.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ The following sink node types are implemented in the `EnergyModelsFlex`:

```@docs
PeriodDemandSink
StratPeriodDemandSink
MultipleInputSink
BinaryMultipleInputSinkStrat
ContinuousMultipleInputSinkStrat
Expand All @@ -20,7 +21,7 @@ LoadShiftingNode

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

The following source node type is implemented in the `EnergyModelsFlex`:
The following source node type are implemented in the `EnergyModelsFlex`:

```@docs
PayAsProducedPPA
Expand Down
32 changes: 15 additions & 17 deletions docs/src/nodes/sink/perioddemand.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
# [PeriodDemandSink node](@id nodes-perioddemandsink)

[`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.
**A *period* is thus a consecutive range of operational periods, that together will model, *e.g.*, a day or a week etc.**
A *demand period* is a consecutive range of operational periods, that together will model, *e.g.*, a day, a week or comparable.

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.

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

!!! warning "TimeStructure for node"
This node is designed for **uniform or repetitive duration of operational periods**.
This node requires considerations of the operational time structure and the chosen demand period duration.
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.

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

!!! warning "Chosen values"
The implementation is relative to the chosen `period_duration` (see below).
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.
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.

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

- **`period_duration::TimeProfile`**:\
Defines the total duration of a single demand period.\
Defines the total duration of the demand periods.
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.
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.\
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).
It cannot be specified as `OperationalProfile`.

- **`period_demand::TimeProfile`**:\
The total demand to be met during each demand period.
The length of this time profile should match the number of periods (*e.g.*, days) in the time structure.
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.
The length of this time profile should match the number of demand periods (*e.g.*, days) in the time structure.
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.\
It cannot be specified as `OperationalProfile`.

It is best to utilize the [`PartitionProfile`](@extref TimeStruct.PartitionProfile) type if the demand is varying.
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.
It cannot be specified as `OperationalProfile`.
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.

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

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

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

[`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.
These variables are for a [`AbstractPeriodDemandSink`](@ref EnergyModelsFlex.AbstractPeriodDemandSink) node ``n`` in demand periods ``t_pd``:
[`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.

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

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

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

```math
\begin{aligned}
\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}) + \\
& \texttt{demand\_sink\_deficit}[n, t_{pd}] \times \texttt{deficit\_penalty}(n, t_{pd})) \times \\
\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}) + {}\\
& \phantom{\sum_{t_{pd} ∈ periods(t_{inv})}(} \texttt{demand\_sink\_deficit}[n, t_{pd}] \times \texttt{deficit\_penalty}(n, t_{pd})) \times {} \\
& scale\_op\_sp(t_{inv}, first(t_{pd})) / duration(first(t_{pd}))
\end{aligned}
```
Expand Down
Loading
Loading