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
10 changes: 10 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
# Release notes

## Version 0.11.6 (2026-04-15)

### Add support for resource-specific constraint functions

* Introduced support for resource specific constraint functions as introduced in [`EnergyModelsBase` v0.9.5](https://github.com/EnergyModelsX/EnergyModelsBase.jl/releases/tag/v0.9.5)
* Both `EMB.variables_flow` (for `TransmissionMode`s and `Area`s) and `EMB.constraints_couple` now iterate over type-segmented resource vectors and call dedicated extension functions per segment:
* [`variables_flow_resource`](https://energymodelsx.github.io/EnergyModelsBase.jl/stable/library/internals/functions/#EnergyModelsBase.variables_flow_resource): can be implemented in extension packages for a `Vector` of a specific `Resource` subtype together with either a `Vector{<:TransmissionMode}` or a `Vector{<:Area}` to create additional JuMP variables for that resource.
* [`constraints_couple_resource`](https://energymodelsx.github.io/EnergyModelsBase.jl/stable/library/internals/functions/#EnergyModelsBase.constraints_couple_resource): can be implemented in extension packages for a `Vector` of a specific `Resource` subtype to add coupling constraints between areas and transmission modes for that resource.
* Default fallback methods are added to allow for resources without additional variables.

## Version 0.11.5 (2026-01-06)

### Adjustments
Expand Down
5 changes: 2 additions & 3 deletions Project.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name = "EnergyModelsGeography"
uuid = "3f775d88-a4da-46c4-a2cc-aa9f16db6708"
authors = ["Espen Flo Bødal <Espen.Bodal@sintef.no>"]
version = "0.11.5"
version = "0.11.6"

[deps]
EnergyModelsBase = "5d7e687e-f956-46f3-9045-6f5a5fd49f50"
Expand All @@ -16,8 +16,7 @@ EnergyModelsInvestments = "fca3f8eb-b383-437d-8e7b-aac76bb2004f"
EMIExt = "EnergyModelsInvestments"

[compat]
EnergyModelsBase = "0.9.1"
EnergyModelsInvestments = "0.8"
EnergyModelsBase = "0.9.5"
SparseVariables = "0.7.3"
JuMP = "1.5"
TimeStruct = "0.9"
Expand Down
1 change: 1 addition & 0 deletions docs/make.jl
Comment thread
espenbodal marked this conversation as resolved.
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ makedocs(
],
"How to" => Any[
"Update models" => "how-to/update-models.md",
"Extend resource functionality" => "how-to/extend-resource-functionality.md",
"Contribute to EnergyModelsGeography" => "how-to/contribute.md",
],
"Library" => Any[
Expand Down
164 changes: 164 additions & 0 deletions docs/src/how-to/extend-resource-functionality.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
# [Extend Resource functionality](@id how_to-res_funct)

This guide is the `EnergyModelsGeography` counterpart to the resource functionality *[introduced in `EnergyModelsBase`](@extref EnergyModelsBase how_to-res_funct)*.
It shows how that same pattern is used for geography-specific coupling through a concrete example from `test_resource_flow.jl`: a `PotentialPower` resource with dedicated flow
variables and coupling constraints.

!!! warning
While we allow resource variable introduction for [`Area`](@ref)s, we strongly advise against introducing new variables for an `Area`.
It is instead easier to access in the function [`EMB.constraints_couple_resource`](@ref) the relevant `Availability` node as outlined below.

This approach allows you to couple the local energy system with the transmission modes with respect to the extra variables.

## [Practical example: `PotentialPower`](@id how_to-res_funct-example)

The goal is to track a resource-specific "potential" flow in parallel with standard transmission flow and enforce a mode-specific loss factor.

### 1. Define the resource and mode

!!! tip
You can use the same resource type as declared in `EnergyModelsBase` or any other package.
This corresponds to *[step 1 in the example of `EnergyModelsBase`](@extref EnergyModelsBase how_to-res_funct-example)*.

```julia
struct PotentialPower <: Resource
id::String
co2_int::Float64
potential_lower::Float64
potential_upper::Float64
end

EMB.is_resource_emit(::PotentialPower) = false
lower_limit(p::PotentialPower) = p.potential_lower
upper_limit(p::PotentialPower) = p.potential_upper

struct PotentialLossMode{T <: PotentialPower} <: TransmissionMode
id::String
resource::T
trans_cap::TimeProfile
trans_loss::TimeProfile
opex_var::TimeProfile
opex_fixed::TimeProfile
directions::Int
data::Vector{Data}
loss_factor::Float64
end
```

### 2. Add resource-specific variables

Implement `EMB.variables_flow_resource` for both [`Area`] and [`Node`] to introduce new variables.

```julia
function EMB.variables_flow_resource(
m,
ℳ::Vector{<:TransmissionMode},
𝒫::Vector{<:PotentialPower},
𝒯,
modeltype::EnergyModel,
)
ℳᵖ = filter(tm -> any(p -> p ∈ 𝒫, inputs(tm)) || any(p -> p ∈ 𝒫, outputs(tm)), ℳ)

@variable(
m,
lower_limit(p) <=
energy_potential_trans_in[tm ∈ ℳᵖ, 𝒯, p ∈ intersect(inputs(tm), 𝒫)] <=
upper_limit(p)
)
@variable(
m,
lower_limit(p) <=
energy_potential_trans_out[tm ∈ ℳᵖ, 𝒯, p ∈ intersect(outputs(tm), 𝒫)] <=
upper_limit(p)
)
end

function EMB.variables_flow_resource(
m,
𝒩::Vector{<:Node},
𝒫::Vector{<:PotentialPower},
𝒯,
modeltype::EnergyModel,
)
@variable(m, lower_limit(p) <= energy_potential_node_in[n ∈ 𝒩, 𝒯, p ∈ 𝒫] <= upper_limit(p))
@variable(m, lower_limit(p) <= energy_potential_node_out[n ∈ 𝒩, 𝒯, p ∈ 𝒫] <= upper_limit(p))
end
```

### 3. Use the new variables in the function

Apply the resource-specific variable in the function [`EMB.constraints_resource`](@ref).
You must be careful when defining the internal constraints due to potential changes in the variables.

```julia
function EMB.constraints_resource(
m,
tm::PotentialLossMode,
𝒯::TimeStructure,
𝒫::Vector{<:PotentialPower},
modeltype::EnergyModel,
)
@constraint(m, [t ∈ 𝒯, p ∈ outputs(tm)],
m[:energy_potential_trans_out][tm, t, p] ==
tm.loss_factor * m[:energy_potential_trans_in][tm, t, p]
)
end
```

### 4. Couple variables between area and transmission mode

Map area-level variables to transmission-level variables with
`EMG.constraints_couple_resource`.

```julia
function EMG.constraints_couple_resource(
m,
𝒜::Vector{<:Area},
ℒᵗʳᵃⁿˢ::Vector{<:Transmission},
𝒫::Vector{<:PotentialPower},
𝒯,
modeltype::EnergyModel,
)
for a ∈ 𝒜, p ∈ 𝒫
ℒᶠʳᵒᵐ, ℒᵗᵒ = EMG.trans_sub(ℒᵗʳᵃⁿˢ, a)
ℳᶠʳᵒᵐ = EMG.modes_sub(ℒᶠʳᵒᵐ, p)
ℳᵗᵒ = EMG.modes_sub(ℒᵗᵒ, p)

if !isempty(ℳᶠʳᵒᵐ)
@constraint(m, [t ∈ 𝒯],
m[:energy_potential_node_out][availability_node(a), t, p] ==
sum(m[:energy_potential_trans_in][tm, t, p] for tm ∈ ℳᶠʳᵒᵐ)
)
end

if !isempty(ℳᵗᵒ)
@constraint(m, [t ∈ 𝒯],
m[:energy_potential_node_in][availability_node(a), t, p] ==
sum(m[:energy_potential_trans_out][tm, t, p] for tm ∈ ℳᵗᵒ)
)
end
end
end
```

### 5. What this gives you

- Bounded resource-specific transmission variables.
- Explicit coupling between area and transmission representation.
- Mode-specific transformations (here: potential loss factor) without changing core code.

## Other useful applications

The same extension pattern is useful whenever transport quality matters, not only quantity.

- District heating networks: track temperature state (supply/return quality) and enforce
temperature-dependent delivery constraints.
- Natural gas networks: track pressure-related transport limits and represent gas mixtures
(e.g., hydrogen blending constraints across corridors).
- Any carrier with quality degradation: track concentration, purity, or state-of-charge style
attributes with resource-specific balance equations.

## See also

- [`update-models`](@ref how_to-update)
- [`Constraint functions`](@ref man-con)
2 changes: 1 addition & 1 deletion docs/src/how-to/update-models.md
Comment thread
espenbodal marked this conversation as resolved.
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ Furthermore, we reworked the design for inclusion of emission and OPEX variables
### [Modes with emissions](@id how_to-update-10-emissions)

It is now necessary to provide a new method to the function [`EnergyModelsBase.has_emissions`](@ref) if you plan to include [`TransmissionMode`](@ref)s with emissions instead of a separate function declared within `EnergyModelsGeography`.
In addition, the function `emission` was renamed to [`EnergyModelsGeography.emissions`](@ref) and, if not called with a `TimePeriod` as input argument, returns a `TimeProfile` instead of a Real.
In addition, the function `emission` was renamed to [`emissions`](@ref EnergyModelsGeography.emissions) and, if not called with a `TimePeriod` as input argument, returns a `TimeProfile` instead of a Real.

## [Adjustments from 0.9.x](@id how_to-update-09)

Expand Down
1 change: 1 addition & 0 deletions docs/src/index.md
Comment thread
espenbodal marked this conversation as resolved.
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ Depth = 1
```@contents
Pages = [
"how-to/update-models.md",
"how-to/extend-resource-functionality.md",
"how-to/contribute.md",
]
Depth = 1
Expand Down
3 changes: 3 additions & 0 deletions docs/src/library/internals/methods_EMB.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,17 @@ EMB.create_node
EMB.objective_operational
EMB.emissions_operational
EMB.constraints_elements
EMB.constraints_resource
EMB.constraints_couple
EMB.constraints_couple_resource
```

## [Variable methods](@id lib-int-met_emb-var)

```@docs
EMB.variables_capacity
EMB.variables_flow
EMB.variables_flow_resource
EMB.variables_opex
EMB.variables_capex(m, ℒᵗʳᵃⁿˢ::Vector{Transmission}, 𝒳, 𝒯, modeltype::EnergyModel)
EMB.variables_elements
Expand Down
4 changes: 4 additions & 0 deletions docs/src/library/public/area.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# [`Area`](@id lib-pub-area)

```@meta
CurrentModule = EnergyModelsGeography
```

A geographical `Area` consist of a location and a connection to a local energy system **via** a specialized `Availability` node called `GeoAvailability`.
The specialized `Availability` node is required to modify the energy/mass balance to allow for imports and exports.
Constraints related to the area keep track of a resource's export and import to the local system and exchange with other areas.
Expand Down
4 changes: 4 additions & 0 deletions docs/src/library/public/case_element.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# [Case description](@id lib-pub-case)

```@meta
CurrentModule = EnergyModelsGeography
```

## Index

```@index
Expand Down
4 changes: 4 additions & 0 deletions docs/src/library/public/mode.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# [`TransmissionMode`](@id lib-pub-mode)

```@meta
CurrentModule = EnergyModelsGeography
```

`TransmissionMode` describes how resources are transported, for example by dynamic transmission modes on ship, truck or railway (represented generically by `RefDynamic`, although not implemented in the current version) or by static transmission modes on overhead power lines or gas pipelines (respresented generically by `RefStatic`).
`TransmissionMode`s includes capacity limits (`trans_cap`), losses (`trans_loss`) and directions (`directions`) for the generic transmission modes `RefDynamic` and `RefStatic`.
More specialized `TransmissionModes` such as subtypes of the abstract type `PipeMode` can convert one `inlet` resource to another `outlet` resource.
Expand Down
3 changes: 3 additions & 0 deletions docs/src/manual/constraint-functions.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ The general approach is similar to `EnergyModelsBase`.
Bidirectional transport requires at the time being the introduciton of an *if*-loop.
In later implementation, it is planned to also use dispatch for this analysis as well.

For resource-specific extensions of area-transmission coupling, see the Section on *[Extend Resource functionality](@ref how_to-res_funct)*.
This extension is called from the default implementation of `EMB.constraints_couple` for each resource-type segment.

## [Capacity constraints](@id man-con-cap)

```julia
Expand Down
Loading
Loading