Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adaptation to changes introduced in EMB v0.7 #19

Merged
merged 8 commits into from
May 28, 2024
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
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
Loading