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,11 @@
# Release notes

## Version 0.2.3 (2026-07-xx)
## Version 0.2.3 (2026-09-29)

### Included support for `PartitionProfile`

* Support for `PartitionProfile` required significant structural rework.
* It requires the user to create a new method to an existing function to extract the durations of the individual partitions.

### Improved results saving

Expand Down
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,5 +28,5 @@ EnergyModelsBase = "0.10"
EnergyModelsGeography = "0.12"
JuMP = "1.24"
ParametricOptInterface = "0.9"
TimeStruct = "0.9.1"
TimeStruct = "0.9.12"
julia = "1.10"
1 change: 1 addition & 0 deletions docs/Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4"
DocumenterCitations = "daee34ce-89f3-4625-b898-19384cb65244"
DocumenterInterLinks = "d12716ef-a0f6-4df4-a9f1-a5a34e75c656"
EnergyModelsBase = "5d7e687e-f956-46f3-9045-6f5a5fd49f50"
EnergyModelsFlex = "a81b9388-333d-4b63-81f2-910b060b544c"
EnergyModelsGeography = "3f775d88-a4da-46c4-a2cc-aa9f16db6708"
EnergyModelsRecedingHorizon = "37ec17de-8561-4b6b-83ee-0529988fea82"
JuMP = "4076af6c-e467-56ae-b986-b466b2749572"
Expand Down
2 changes: 2 additions & 0 deletions docs/make.jl
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ using DocumenterCitations

using TimeStruct
using EnergyModelsBase
using EnergyModelsFlex
using EnergyModelsGeography
using EnergyModelsRecedingHorizon

Expand All @@ -31,6 +32,7 @@ links = InterLinks(
"TimeStruct" => "https://sintefore.github.io/TimeStruct.jl/stable/",
"EnergyModelsBase" => "https://energymodelsx.github.io/EnergyModelsBase.jl/stable/",
"EnergyModelsGeography" => "https://energymodelsx.github.io/EnergyModelsGeography.jl/stable/",
"EnergyModelsFlex" => "https://energymodelsx.github.io/EnergyModelsFlex.jl/stable/",
)

bib = CitationBibliography(joinpath(@__DIR__, "src", "references.bib"))
Expand Down
49 changes: 48 additions & 1 deletion docs/src/how-to/adapt-emx-elem.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# [Adapt an EMX element](@id how_to-adapt_elem)

This package is based on [`EnergyModelsBase`](https://energymodelsx.github.io/EnergyModelsBase.jl/), and therefore several aspects of creating a new element are covered in *[its documentation](@extref EnergyModelsBase how_to-create_node)*.
In this page, we will focus on the specific requirements for the introduction of new [`AbstractElement`](@extref EnergyModelsBase.AbstractElement)s in the receding horizon framework.
On this page, we focus on the specific requirements for the introduction of new [`AbstractElement`](@extref EnergyModelsBase.AbstractElement)s in the receding horizon framework.

## [Initialization settings](@id how_to-adapt_elem-init)

Expand All @@ -26,3 +26,50 @@ This is not needed for elements that can use [`InitData`](@ref) and its default
For elements with specific constructor needs, it is necessary to dispatch on `Accessors.ConstructionBase.constructorof`.
This includes elements with parametric types for which the parametric input cannot be deduced from the fields (*e.g.*, `Storage` nodes), as well as elements with inner constructors.
An example of how this can be done is shown in the package for the `Storage` node (see the [`constructorof` dispatch](@ref EMRH.Accessors.ConstructionBase.constructorof)).

## [Elements with partitions](@id how_to-adapt_elem-part)

If you have an [`AbstractElement`](@extref EnergyModelsBase.AbstractElement) that utilizes the concept of partitions *via* [`PartitionProfile`](@extref TimeStruct.PartitionProfile), you must create a new method for the function [`period_duration`](@ref EMRH.period_duration) for your type, which identifies the durations of the partitions.

As an example, consider the node [`PeriodDemandSink`](@extref EnergyModelsFlex.PeriodDemandSink), in which we have demand periods based on partitions.
This node extracts the durations of the partitions using the `EnergyModelsFlex` internal function [`period_duration`](@extref EnergyModelsFlex.period_duration-Tuple{EnergyModelsFlex.AbstractPeriodDemandSink}).

In this situation, you must declare a new method for [`period_duration`](@ref EMRH.period_duration) (the `EnergyModelsRecedingHorizon` function) as

```julia
const EMRH = EnergyModelsRecedingHorizon
const EMF = EnergyModelsFlex

EMRH.period_duration(n::EMF.AbstractPeriodDemandSink) = EMF.period_duration(n)
```

!!! warning "Caveats of period partitions"
It is **not** possible to specify a `FixedProfile` for the duration of the period partitions due to the way the horizons are calculated.

Using partitions is experimental:

1. You must be careful that the structure of the chosen partitions are in line with both the optimization and implementation horizon such that partitions are not split in the receding horizon optimization problems.
2. Partitions must be of the same length in all horizons if you utilize the `ParametricOptInterface` extension.
This is checked in the receding horizon validation routines.

See the examples below:

```julia
# Time structure and chosen horizon
𝒯 = TwoLevel(1, 1, SimpleTimes(15, 1))
ℋ = PeriodHorizons(15, 1, 8, 4)

# Consistent profiles
pprof_1 = PartitionProfile([2, 2, 2, 2, 2, 2, 2, 1])
pprof_2 = PartitionProfile([4, 4, 4, 3])

# Inconsistent profile
pprof_wrong_1 = PartitionProfile([3, 3, 3, 3, 3])

# Inconsistent profile for the ParametricOptInterface extension
pprof_wrong_2 = PartitionProfile([4, 2, 2, 4, 2, 1])
```

* The profile `pprof_wrong_1` is inconsistent for the receding horizon framework as the individual partitions are not fully included in both the implementation and optimization horizons.
* The profile `pprof_wrong_2` is inconsistent for the `ParametricOptInterface` extension as the partitions are different in the different horizons while it would work for the base design.
The profiles would, however, be consistent for a standard perfect foresight model.
6 changes: 6 additions & 0 deletions docs/src/library/internals/methods-EMB.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,9 @@ EnergyModelsBase.get_elements_vec
EnergyModelsBase.get_nodes
EnergyModelsBase.get_links
```

In addition, a new method is created for `get_time_struct` to extract the original time structure from the `UpdateCase`:

```@docs
EnergyModelsBase.get_time_struct
```
7 changes: 7 additions & 0 deletions docs/src/library/internals/methods-EMRH.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,13 @@ EMRH._create_lens_for_field
EMRH._path_type
```

### [Horizon consistency check functions](@id lib-int-check)

```@docs
EMRH._check_period_partitions
EMRH._check_horizon
```

### [Functions for resetting values](@id lib-int-reset)

```@docs
Expand Down
2 changes: 1 addition & 1 deletion docs/src/library/internals/reference-EMGExt.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,5 +69,5 @@ EMG.get_transmissions
## [ParametricOptInterface](@id lib-int-EMGExt-POI)

```@docs
POIExt._update_parameter!(m, res_type::EMRH.InitReset{EMGExt.TransInitDataPath}, opers::Vector)
POIExt._update_parameter!(m, 𝒰::EMRH.UpdateCase, res_type::EMRH.InitReset{EMGExt.TransInitDataPath}, opers::Vector)
```
2 changes: 1 addition & 1 deletion docs/src/library/internals/reference-POIExt.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ update_model!

```@docs
_update_parameter_values!
_update_parameter!(m, res_type::EMRH.ElementReset, opers::Vector)
_update_parameter!(m, 𝒰::EMRH.UpdateCase, res_type::EMRH.ElementReset, opers::Vector)
```

## [EnergyModelsRecedingHorizon](@id lib-int-POIExt-EMRH)
Expand Down
28 changes: 22 additions & 6 deletions docs/src/library/internals/reset.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,13 @@ Pages = ["reset.md"]

## [Paths](@id lib-int-reset-path)

Path are internal types that are used for the identification of a reset.
Paths are internal types that are used for the identification of a reset.
They are used internally for dispatch.

```@docs
EMRH.AbstractPath
EMRH.OperPath
EMRH.PartitionPath
EMRH.ElementPath
EMRH.TimeWeightPath
EMRH.AbstractInitDataPath
Expand All @@ -35,10 +36,17 @@ EMRH.model_key(idp::EMRH.InitDataPath)
Reset types are introduced to differentiate on what must be reset in a given element.
They are used internally for dispatch.

!!! note "Resetting new values"
If you **must** reset a different type of value than outlined below, you must also create a new type.

In this case, you **must** also create a new method for [`ResetType`](@ref EMRH.ResetType) as this function is used internally.

```@docs
EMRH.AbstractReset
EMRH.ElementReset
EMRH.EmptyReset
EMRH.OperReset
EMRH.PartitionReset
EMRH.ElementReset
EMRH.TimeWeightReset
EMRH.InitReset
EMRH.ResetType
Expand All @@ -50,11 +58,19 @@ The following function is included for identification purposes:
EMRH.is_init_reset
```

!!! note "Resetting new values"
If you **must** reset a different type of value than outlined below, you must also create a new type.
If you create a new `AbstractPath`, it is not neccesary to create a new type.
The following function is included to identify the individual partitions when creating a `PartitionReset`.

In this case, you **must** also create a new method for [`ResetType`](@ref EMRH.ResetType) as this function is used internally.
```@docs
EMRH.period_duration
```

The following types were included to create a parametric type for `PartitionReset` to describe the behavior of the `AbstractReset`:

```@docs
EMRH.AbstractResetBehavior
EMRH.StandResBehav
EMRH.NoResBehav
```

## [Substitution types](@id lib-int-reset-sub)

Expand Down
2 changes: 1 addition & 1 deletion ext/EMGExt/structures/node.jl
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ When an abstract reset `ri` for a Transmission corridor`l` and a data path `idp`
instead of the vector of `AbstractSub` vectors `𝒮ᵛᵉᶜᵢₙ`, it updates the value in
AbstractReset `ri` of AbstractElement `x` with the value of the variable specified by the
`key` of the `idp` in the last period of `opers_implᵣₕ` for the `TransmissionMode` specified
by the the `idx` of `idp`.
by the `idx` of `idp`.
"""
function EMRH.update_init_data!(
m,
Expand Down
2 changes: 1 addition & 1 deletion ext/EMGPOIExt/EMGPOIExt.jl
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module EMGPOIExt
using EnergyModelsBase
using EnergyModelsGeography
using EnergyModelsRecedingHorizon
using .EnergyModelsRecedingHorizon: RecHorEnergyModel, InitReset
using .EnergyModelsRecedingHorizon: RecHorEnergyModel, UpdateCase, InitReset

using JuMP
using ParametricOptInterface
Expand Down
4 changes: 2 additions & 2 deletions ext/EMGPOIExt/utils/case_setup.jl
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@

"""
POIExt._update_parameter!(m, res_type::InitReset{EMGExt.TransInitDataPath}, opers::Vector)
POIExt._update_parameter!(m, 𝒰::UpdateCase, res_type::InitReset{EMGExt.TransInitDataPath}, opers::Vector)

Update the parameter value for a given `InitReset{EMGExt.TransInitDataPath}` to the provided
value of the [`InitReset`](@ref EnergyModelsRecedingHorizon.InitReset) type.
"""
POIExt._update_parameter!(m, res_type::InitReset{EMGExt.TransInitDataPath}, opers::Vector) =
POIExt._update_parameter!(m, 𝒰::UpdateCase, res_type::InitReset{EMGExt.TransInitDataPath}, opers::Vector) =
MOI.set(m, POI.ParameterValue(), res_type.var, res_type.val)
3 changes: 2 additions & 1 deletion ext/POIExt/POIExt.jl
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ module POIExt
using EnergyModelsBase
using EnergyModelsRecedingHorizon
using .EnergyModelsRecedingHorizon: RecHorEnergyModel, UpdateCase, AbstractSub,
ElementReset, InitReset, TimeWeightReset, OperReset,
ElementReset, InitReset, TimeWeightReset, OperReset, PartitionReset, EmptyReset,
StandResBehav, NoResBehav,
_create_updatetype, _add_elements!, update_results!, update_init_data!, _update_case_types!,
update_future_value!, get_sub_model, get_sub_products, get_sub_elements_vec, get_sub_ele,
updated, original, resets, _initialize_loop_variables, _add_mapping!
Expand Down
4 changes: 3 additions & 1 deletion ext/POIExt/model.jl
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ function EMRH.run_model_rh(
n_𝒽 = length(ℋ)
op_per_strat = 𝒯.op_per_strat


# Assert that the horizon is functioning with the POI implementation.
horizon_duration = all(
durations(𝒽) == durations(𝒽₀) for
Expand All @@ -44,6 +43,9 @@ function EMRH.run_model_rh(
# Create the `UpdateCase` based on the original `Case` structure
𝒰 = _create_updatetype(case, modeltype)

# Identify problems with potential `period_duration`s in the time structure
EMRH._check_period_partitions(𝒰, ℋ, optimizer)

# Extract the time structure from the case to identify the used operational periods
# and the receding horizon time structure
if use_op_per_strat
Expand Down
Loading
Loading