Skip to content

Commit

Permalink
Adaptation to changes introduced in EMB v0.7 (#19)
Browse files Browse the repository at this point in the history
* Adjustment to all changes from EMB v0.7
  - Use StorageBehavior for representing the storage behavior
  - Use AbstractStorageParameter for the capacities in charge, level, and discharge
  • Loading branch information
JulStraus authored May 28, 2024
1 parent 12f56f9 commit 86cbc7c
Show file tree
Hide file tree
Showing 19 changed files with 1,153 additions and 501 deletions.
6 changes: 6 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Release notes

## Version 0.6.0 (2024-05-28)

* Adjusted to changes introduced in `EnergyModelsBase` v0.7.
* Remove legacy constructor for `RegHydroStor` and provide a warning for it.
* Added constructors for `HydroStor` not requiring any longer specifying an input dictionary.

## Version 0.5.6 (2024-05-09)

* Provided a contribution section in the documentation.
Expand Down
6 changes: 3 additions & 3 deletions Project.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name = "EnergyModelsRenewableProducers"
uuid = "b007c34f-ba52-4995-ba37-fffe79fbde35"
authors = ["Sigmund Eggen Holm <[email protected]>, Julian Straus <[email protected]>"]
version = "0.5.6"
version = "0.6.0"

[deps]
EnergyModelsBase = "5d7e687e-f956-46f3-9045-6f5a5fd49f50"
Expand All @@ -10,7 +10,7 @@ Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f"
TimeStruct = "f9ed5ce0-9f41-4eaa-96da-f38ab8df101c"

[compat]
EnergyModelsBase = "^0.6.7"
EnergyModelsBase = "^0.7.0"
JuMP = "1.5"
TimeStruct = "^0.7.0"
TimeStruct = "^0.8.0"
julia = "^1.6"
15 changes: 9 additions & 6 deletions docs/make.jl
Original file line number Diff line number Diff line change
Expand Up @@ -27,17 +27,20 @@ makedocs(
pages = [
"Home" => "index.md",
"Manual" => Any[
"Quick Start"=>"manual/quick-start.md",
"Optimization variables"=>"manual/optimization-variables.md",
"Constraint functions"=>"manual/constraint-functions.md",
"Examples"=>"manual/simple-example.md",
"Quick Start" => "manual/quick-start.md",
"Optimization variables" => "manual/optimization-variables.md",
"Constraint functions" => "manual/constraint-functions.md",
"Examples" => "manual/simple-example.md",
"Release notes" => "manual/NEWS.md",
],
"How to" => Any[
"Update models" => "how-to/update-models.md",
"Contribute to EnergyModelsRenewableProducers" => "how-to/contribute.md",
],
"Library" =>
Any["Public"=>"library/public.md", "Internals"=>"library/internals.md"],
"Library" => Any[
"Public" => "library/public.md",
"Internals" => "library/internals.md",
],
],
)

Expand Down
150 changes: 150 additions & 0 deletions docs/src/how-to/update-models.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
# [Update your model to the latest versions](@id update-models)

`EnergyModelsRenewableProducers` is still in a pre-release version.
Hence, there are frequently breaking changes occuring, although we plan to keep backwards compatibility.
This document is designed to provide users with information regarding how they have to adjust their models to keep compatibility to the latest changes.
We will as well implement information regarding the adjustment of extension packages, although this is more difficult due to the vast majority of potential changes.

## Adjustments from 0.4.2

### Key changes for nodal descriptions

Version 0.7 of `EnergyModelsBase` introduced both *storage behaviours* resulting in a rework of the individual approach for calculating the level balance as well as the potential to have charge and discharge capacities through *storage parameters*.

!!! note
The legacy constructors for calls of the composite type of version 0.5 will be included at least until version 0.7.

### [`HydroStor`](@ref)

`HydroStor` was significantly reworked due to the changes in `EnergyModelsBase`
The total rework is provided below.

```julia
# The previous nodal description for a `HydroStor` node was given by:
HydroStor(
id,
rate_cap::TimeProfile,
stor_cap::TimeProfile,

level_init::TimeProfile,
level_inflow::TimeProfile,
level_min::TimeProfile,

opex_var::TimeProfile,
opex_fixed::TimeProfile,
stor_res::ResourceCarrier,
input::Dict{<:Resource, <:Real},
output::Dict{<:Resource, <:Real},
data::Vector{Data},
)

# This translates to the following new version
HydroStor{CyclicStrategic}(
id,
StorCapOpexFixed(stor_cap, opex_fixed),
StorCapOpexVar(rate_cap, opex_var),
level_init,
level_inflow,
level_min,
stor_res,
input,
output,
data,
)
```

### [`PumpedHydroStor`](@ref)

`PumpedHydroStor` was significantly reworked due to the changers in `EnergyModelsBase`
The total rework is provided below.

```julia
# The previous nodal description for a `PumpedHydroStor` node was given by:
PumpedHydroStor(
id,
rate_cap::TimeProfile,
stor_cap::TimeProfile,

level_init::TimeProfile,
level_inflow::TimeProfile,
level_min::TimeProfile,

opex_var::TimeProfile,
opex_var_pump::TimeProfile,
opex_fixed::TimeProfile,
stor_res::ResourceCarrier,
input::Dict{<:Resource, <:Real},
output::Dict{<:Resource, <:Real},
data::Vector{Data},
)

# This translates to the following new version
PumpedHydroStor{CyclicStrategic}(
id,
StorCapOpexVar(rate_cap, opex_var_pump),
StorCapOpexFixed(stor_cap, opex_fixed),
StorCapOpexVar(rate_cap, opex_var),
level_init,
level_inflow,
level_min,
stor_res,
input,
output,
data,
)
```

## Adjustments from 0.4.0 to 0.6.x

### Key changes for nodal descriptions

Version 0.4.1 introduced two new types that replaced the original `RegHydroStor` node with two types called [`PumpedHydroStor`](@ref) and [`HydroStor`](@ref).
The changes allowed for the introduction of a variable OPEX for pumping.
In the translation below, it is assumed that the variable OPEX for pumping is 0.

```julia
# The previous nodal description was given by:
RegHydroStor(
id::Any,
rate_cap::TimeProfile,
stor_cap::TimeProfile,
has_pump::Bool,
level_init::TimeProfile,
level_inflow::TimeProfile,
level_min::TimeProfile,
opex_var::TimeProfile,
opex_fixed::TimeProfile,
stor_res::ResourceCarrier,
input,
output,
Data,
)

# This translates to the following new version if has_pump == true
PumpedHydroStor(
id,
StorCapOpexVar(rate_cap, FixedProfile(0)),
StorCapOpexFixed(stor_cap, opex_fixed),
StorCapOpexVar(rate_cap, opex_var),
level_init,
level_inflow,
level_min,
stor_res,
input,
output,
Data,
)
# and the following version if has_pump == false
HydroStor(
id,
StorCapOpexFixed(stor_cap, opex_fixed),
StorCapOpexVar(rate_cap, opex_var),
level_init,
level_inflow,
level_min,
stor_res,
input,
output,
Data,
)
```
6 changes: 5 additions & 1 deletion docs/src/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,17 @@ Pages = [
"manual/constraint-functions.md",
"manual/simple-example.md"
]
Depth = 1
```

## How to guides

```@contents
Pages = [
"how-to/contribute.md",
"how-to/update-models.md",
]
Depth = 1
```

## Library outline
Expand All @@ -41,5 +44,6 @@ Pages = [
Pages = [
"library/public.md"
"library/internals.md"
]
]
Depth = 1
```
27 changes: 15 additions & 12 deletions docs/src/library/public.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,10 @@ A hydropower plant is much more flexible than, *e.g.*, a wind farm since the wat
Energy can be produced (almost) whenever it is needed.
Some hydropower plants also have pumps installed.
These are used to pump water into the reservoir when excess and cheap energy is available in the network.
`EnergyModelsRenewableProducers` introduces hence two different types representing a regulated hydropower plant ([`HydroStor`](@ref)) and a pumped regulated hydropower plant ([`PumpedHydroStor`](@ref)) without a lower reservoir.
Both types have a `level` and `discharge` capacity while a `PumpedHydroStor` also includes a `charge` capacity.

The field `rate_cap` describes the installed production capacity of the (aggregated) hydropower plant.
The variable `level_init` represents the initial energy available in the reservoir in the beginning of each investment period, while `stor_cap` is the installed storage capacity in the reservoir.
The variable `level_init` represents the initial energy available in the reservoir in the beginning of each investment period.
The variable `level_inflow` describes the inflow into the reservoir (measured in energy units), while `level_min` is the allowed minimum storage level in the dam, given as a ratio of the installed storage capacity of the reservoir at
every operational period.
The required minimum level is enforced by NVE and varies over the year.
Expand All @@ -38,22 +39,24 @@ The resources stored in the hydro storage is set as `stor_res`, similar to a reg
The five last parameters are used in the same way as in `EMB.Storage`.
In the implementation of [`PumpedHydroStor`](@ref), the values set in `input` represents a loss of energy when using the pumps.
A value of `1` means no energy loss, while a value of `0` represents 100% energy loss of that inflow variable.
[`PumpedHydroStor`](@ref) has in addition the field `opex_var_pump::TimeProfile`.
This field corresponds to the variable operational expenditures when pumping water into the storage reservoir.

Since we also want to be able to model hydropower plant nodes *without* pumps, we include the boolean `has_pump` in the type describing hydropower.
For combining the behavior of a hydropower plant with and without a pump, we can disable the inflow of energy by setting the constraint

``\texttt{flow\_in}[n, t, p_{\texttt{Power}}] = 0,``

for the stored resource ``p_{\texttt{Power}}`` for the node ``n`` `::HydroStor`.
To access this variable, we therefore have to let the type `HydroStorage` be a subtype of `EMB.Storage`.

The fields of the different types are listed below:

```@docs
HydroStorage
HydroStor
PumpedHydroStor
```

In recent version increases, we changed the individual fields of the `HydroStorage` nodes as well as their types.
Hence, we still incorporate legacy constructors that can be utilized when having a model in previous versions.
However, we removed one legacy constructor as it is no longer required.
Calling the constructor will provide you now with an error.

This legacy constructor is:

```@docs
RegHydroStor
```

See the section on *[how to update models](@ref update-models)* for further information regarding how you can translate your existing model to the new model.
10 changes: 8 additions & 2 deletions docs/src/manual/NEWS.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
# Release notes

## Unversioned
## Version 0.6.0 (2024-05-28)

* Adjusted to changes introduced in `EnergyModelsBase` v0.7.
* Remove legacy constructor for `RegHydroStor` and provide a warning for it.
* Added constructors for `HydroStor` not requiring any longer specifying an input dictionary.

## Version 0.5.6 (2024-05-09)

* Updated a link in the documentation for the examples.
* Provided a contribution section in the documentation.
* Fixed a link in the documentation for the examples.

## Version 0.5.5 (2024-03-21)

Expand Down
43 changes: 24 additions & 19 deletions docs/src/manual/constraint-functions.md
Original file line number Diff line number Diff line change
@@ -1,39 +1,44 @@
# [Constraint functions](@id constraint_functions)

The [`HydroStorage`](@ref) types dispatch on individual functions from within `EnergyModelsBase` ti extend the functionality
## `NonDisRES` (non-dispatchable renewable energy source)

## Storage level constraints
The introduction of the type [`NonDisRES`](@ref NonDisRES_public) does not require a new `create_node` function.
Instead, it is sufficient to dispatch on the function

All [`HydroStorage`](@ref) subtypes utilize the same function, `constraints_level(m, n::Storage, 𝒯, 𝒫, modeltype::EnergyModel)`, for calling the two relevant subfunctions.
```julia
EMB.constraints_capacity(m, n::NonDisRES, 𝒯::TimeStructure, modeltype::EnergyModel)
```

The function
to introduce the new energy balance using the field `profile` and the variable ``\texttt{curtailment}``.
In this case, we also have to call the function

```julia
EMB.constraints_level_aux(m, n::HydroStorage, 𝒯, 𝒫, modeltype::EnergyModel)
constraints_capacity_installed(m, n, 𝒯, modeltype)
```

is extended to account for both the provision of an initial level at the start of each strategic period as well as modifying the constraint for the variable ``\texttt{stor\_level\_}\Delta\texttt{\_op}`` to account for the introduction of the new variable ``\texttt{hydro\_spill}``.
The former is required for [`HydroStorage`](@ref) subtypes asthe initial level is frequently a function of the season (excluding small scale pumped hydro storage) while the latter is required to include spillage.
to allow for investments when coupled with `EnergyModelsInvestments`.
We do however not need to create new methods for said function.

## `HydroStorage` (regulated hydro storage with or without pump)

The [`HydroStorage`](@ref HydroStorage_public) types utilize the same `create_node` function for introducing new concepts.
In addition, they dispatch on individual functions from within `EnergyModelsBase` to extend the functionality.

The functions

```julia
EMB.constraints_level_sp(m, n::HydroStorage, t_inv, 𝒫, modeltype::EnergyModel)
EMB.constraints_flow_in(m, n::HydroStor, 𝒯::TimeStructure, modeltype::EnergyModel)
EMB.constraints_flow_in(m, n::PumpedHydroStor, 𝒯::TimeStructure, modeltype::EnergyModel)
```

are similar to the function used for `RefStorage{T} where {T<:ResourceCarrier}`.
It is however necessary to reintroduce it due to the declaration for `RefStorage` in `EnergyModelsBase`.
This will most likely be adjusted in later versions, although it will not impact the user directly.

## Operational expenditure constraints
allow for a different behavior of the `HydroStorage` node through fixing the variable ``\texttt{flow\\_in}`` in the case of a [`HydroStor`](@ref) node to 0 and limiting it in the case of a [`PumpedHydroStor`](@ref) to installed charge capacity through the variable ``\texttt{stor\\_charge\\_use}``.

Variable operational expenditure (OPEX) constraints are slightly different defined in the case of [`HydroStor`](@ref) and [`PumpedHydroStor`](@ref) nodes.
Hence, dispatch is required on the individual constraints:
All `HydroStorage` subtypes utilize the introduced level balances from `EnergyModelsBase`.
MOdification to the level balance is achieved through overloading

```julia
EMB.constraints_opex_var(m, n::HydroStor, 𝒯::TimeStructure, modeltype::EnergyModel)
EMB.constraints_opex_var(m, n::PumpedHydroStor, 𝒯::TimeStructure, modeltype::EnergyModel)
EMB.constraints_level_aux(m, n::HydroStorage, 𝒯, 𝒫, modeltype::EnergyModel)
```

Within a [`HydroStor`](@ref) node, the variable OPEX is defined *via* the outflow from the hydropower plant, contrary to the definition of a `RefStorage` node in which the variable OPEX is defined *via* the inflow.
A [`PumpedHydroStor`](@ref) has contributions by both the inflow (through the field `opex_var_pump`) and the outflow (through the field `opex_var`).
to account for both the provision of an initial level at the start of each strategic period as well as modifying the constraint for the variable ``\texttt{stor\_level\_}\Delta\texttt{\_op}`` to account for the introduction of the new variable ``\texttt{hydro\_spill}``.
The former is required for [`HydroStorage`](@ref) subtypes as the initial level is frequently a function of the season (excluding small scale pumped hydro storage) while the latter is required to include spillage.
2 changes: 1 addition & 1 deletion docs/src/manual/optimization-variables.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ The spillage is introduced to allow for an overflow from a reservoir if the infl

The variable is used in the following constraint [`EMB.constraints_level_aux`](@ref),

``\texttt{stor\_level\_}\Delta\texttt{\_op}[n, t] = \texttt{level\_inflow}(n, t) + \texttt{inputs}(n, p_{\texttt{Power}}) \cdot \texttt{flow\_in}[n, t] + \texttt{stor\_rate\_use}[n, t] - \texttt{hydro\_spill}[n, t]``
``\texttt{stor\_level\_}\Delta\texttt{\_op}[n, t] = \texttt{level\_inflow}(n, t) + \texttt{inputs}(n, p_{\texttt{Power}}) \cdot \texttt{flow\_in}[n, t] + \texttt{stor\_discharge\_use}[n, t] - \texttt{hydro\_spill}[n, t]``

for the stored resource ``p_{\texttt{Power}}``.

Expand Down
14 changes: 9 additions & 5 deletions examples/simple_hydro_power.jl
Original file line number Diff line number Diff line change
Expand Up @@ -62,15 +62,19 @@ function generate_example_data()
)

# Create a regulated hydro power plant without storage capacity
hydro = HydroStor(
hydro = HydroStor{CyclicStrategic}(
"hydropower", # Node ID
FixedProfile(2.0), # Rate capacity in MW
FixedProfile(90), # Storage capacity in MWh
StorCapOpexFixed(FixedProfile(90), FixedProfile(3)),
# Line above for the storage level:
# Argument 1: Storage capacity in MWh
# Argument 2: Fixed OPEX in EUR/8h
StorCapOpexVar(FixedProfile(2.0), FixedProfile(8)),
# Line above for the discharge rate:
# Argument 1: Rate capacity in MW
# Argument 2: Variable OPEX in EUR/MWh
FixedProfile(10), # Initial storage level in MWh
FixedProfile(1), # Inflow to the Node in MW
FixedProfile(0.0), # Minimum storage level as fraction
FixedProfile(8), # Variable OPEX in EUR/MWh
FixedProfile(3), # Fixed OPEX in EUR/8h
Power, # Stored resource
Dict(Power => 0.9), # Input to the power plant, irrelevant in this case
Dict(Power => 1), # Output from the Node, in this gase, Power
Expand Down
Loading

2 comments on commit 86cbc7c

@JulStraus
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JuliaRegistrator
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Registration pull request created: JuliaRegistries/General/107781

Tip: Release Notes

Did you know you can add release notes too? Just add markdown formatted text underneath the comment after the text
"Release notes:" and it will be added to the registry PR, and if TagBot is installed it will also be added to the
release that TagBot creates. i.e.

@JuliaRegistrator register

Release notes:

## Breaking changes

- blah

To add them here just re-invoke and the PR will be updated.

Tagging

After the above pull request is merged, it is recommended that a tag is created on this repository for the registered package version.

This will be done automatically if the Julia TagBot GitHub Action is installed, or can be done manually through the github interface, or via:

git tag -a v0.6.0 -m "<description of version>" 86cbc7ccc0f355b2196dbdc2a2bf9c320d08693b
git push origin v0.6.0

Please sign in to comment.