Skip to content

Commit 19d149a

Browse files
authored
Bugfix related to checks for TwoLevelTree and StrategicStochasticProfile (#92)
* Checks for StrategicProfile and TwoLevelTree for opex_fixed and emission_limit * Checks for StrategicStochasticProfile for variables indexed over t_inv * Changed objective value in `EMIExt` test
1 parent fe585ff commit 19d149a

5 files changed

Lines changed: 55 additions & 38 deletions

File tree

NEWS.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
# Release notes
22

3+
## Version 0.10.2 (2026-06-09)
4+
5+
### Bug fixes
6+
7+
* Fixed a bug for using `StrategicProfile` in the field `opex_fixed` of a node and `TwoLevelTree` as time structure.
8+
* Fixed a bug for using `StrategicProfile` in the field `emission_limit` of a modeltype and `TwoLevelTree` as time structure.
9+
* Fixed bug in function `check_strategic_profile` when utilizing `StrategicStochasticProfile`.
10+
311
## Version 0.10.1 (2026-04-14)
412

513
### Minor updates

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name = "EnergyModelsBase"
22
uuid = "5d7e687e-f956-46f3-9045-6f5a5fd49f50"
33
authors = ["Lars Hellemo <Lars.Hellemo@sintef.no>, Julian Straus <Julian.Straus@sintef.no>"]
4-
version = "0.10.1"
4+
version = "0.10.2"
55

66
[deps]
77
JuMP = "4076af6c-e467-56ae-b986-b466b2749572"

src/checks.jl

Lines changed: 13 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -296,7 +296,6 @@ Checks the `modeltype` .
296296
"""
297297
function check_model(case, modeltype::EnergyModel, check_timeprofiles::Bool)
298298
𝒯 = get_time_struct(case)
299-
𝒯ᴵⁿᵛ = strategic_periods(𝒯)
300299

301300
# Check for inclusion of all emission resources
302301
for p get_products(case)
@@ -310,27 +309,21 @@ function check_model(case, modeltype::EnergyModel, check_timeprofiles::Bool)
310309
end
311310

312311
for p keys(emission_limit(modeltype))
313-
em_limit = emission_limit(modeltype, p)
314-
# Check for the strategic periods
315-
if isa(em_limit, StrategicProfile) && check_timeprofiles
316-
@assert_or_log(
317-
length(em_limit.vals) == length(𝒯ᴵⁿᵛ),
318-
"The timeprofile provided for resource `" *
319-
string(p) *
320-
"` in the field " *
321-
"`emission_limit` does not match the strategic structure."
322-
)
323-
end
324-
325312
# Check for potential indexing problems
313+
em_limit = emission_limit(modeltype, p)
326314
message =
327315
"are not allowed for the resource `" *
328316
string(p) *
329317
"` in the dictionary " *
330318
"`emission_limit`."
331319
check_strategic_profile(em_limit, message)
320+
321+
# Check for the strategic periods
322+
check_timeprofiles || continue
323+
check_profile("emission_limit[" * string(p) * "]", em_limit, 𝒯)
332324
end
333325

326+
# Check for the strategic periods
334327
for p keys(emission_price(modeltype))
335328
em_price = emission_price(modeltype, p)
336329
check_timeprofiles || continue
@@ -662,6 +655,13 @@ function check_strategic_profile(time_profile::TimeProfile, message::String)
662655
for l1_profile time_profile.vals
663656
sub_msg = "in strategic profiles " * message
664657
bool_sp = check_strat_sub_profile(l1_profile, sub_msg, bool_sp)
658+
!bool_sp && break
659+
end
660+
elseif isa(time_profile, StrategicStochasticProfile)
661+
for sp_array time_profile.vals, l1_profile sp_array
662+
sub_msg = "in strategic stochastic profiles " * message
663+
bool_sp = check_strat_sub_profile(l1_profile, sub_msg, bool_sp)
664+
!bool_sp && break
665665
end
666666
end
667667

@@ -1017,14 +1017,6 @@ returns a `TimeProfile`.
10171017
periods.
10181018
"""
10191019
function check_fixed_opex(n, 𝒯ᴵⁿᵛ, check_timeprofiles::Bool)
1020-
if isa(opex_fixed(n), StrategicProfile) && check_timeprofiles
1021-
@assert_or_log(
1022-
length(opex_fixed(n).vals) == length(𝒯ᴵⁿᵛ),
1023-
"The timeprofile provided for the field `opex_fixed` does not match the " *
1024-
"strategic structure."
1025-
)
1026-
end
1027-
10281020
# Check for potential indexing problems
10291021
message = "are not allowed for the field `opex_fixed`."
10301022
bool_sp = check_strategic_profile(opex_fixed(n), message)

test/test_checks.jl

Lines changed: 30 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -462,6 +462,9 @@ end
462462
StrategicProfile([OperationalProfile([5])]),
463463
StrategicProfile([ScenarioProfile([5])]),
464464
StrategicProfile([RepresentativeProfile([5])]),
465+
StrategicStochasticProfile([[OperationalProfile([5])]]),
466+
StrategicStochasticProfile([[ScenarioProfile([5])]]),
467+
StrategicStochasticProfile([[RepresentativeProfile([5])]]),
465468
]
466469
for tp profiles
467470
@test_throws AssertionError EMB.check_strategic_profile(tp, "")
@@ -514,26 +517,24 @@ end
514517
end
515518

516519
@testset "Checks - Nodes" begin
517-
518520
# Resources used in the checks
519521
NG = ResourceEmit("NG", 0.2)
520522
Power = ResourceCarrier("Power", 0.0)
521523
CO2 = ResourceEmit("CO2", 1.0)
522524
aux = ResourceCarrier("aux", 0.0)
523525

524526
# Function for setting up the system for testing `Sink` and `Source`
525-
function simple_graph(;
527+
function check_graph_src_snk(;
526528
src_cap::TimeProfile = FixedProfile(10),
527529
src_opex_var::TimeProfile = FixedProfile(10),
528530
src_opex_fixed::TimeProfile = FixedProfile(0),
529531
src_output::Dict = Dict(Power => 1),
530532
snk_cap::TimeProfile = OperationalProfile([6, 8, 10, 6, 8]),
531533
snk_pen::Dict = Dict(:surplus => FixedProfile(4), :deficit => FixedProfile(10)),
532534
snk_input::Dict = Dict(Power => 1),
535+
T = TwoLevel(2, 2, SimpleTimes(5, 2); op_per_strat = 10),
533536
)
534537
resources = [Power, CO2]
535-
ops = SimpleTimes(5, 2)
536-
T = TwoLevel(2, 2, ops; op_per_strat = 10)
537538

538539
source =
539540
RefSource(
@@ -566,39 +567,55 @@ end
566567
# Sink used in the analysis
567568
# Test that a wrong capacity is caught by the checks.
568569
src_cap = FixedProfile(-4)
569-
@test_throws AssertionError simple_graph(;src_cap)
570+
@test_throws AssertionError check_graph_src_snk(;src_cap)
570571

571572
# Test that a wrong output dictionary is caught by the checks.
572573
src_output = Dict(Power => -1)
573-
@test_throws AssertionError simple_graph(;src_output)
574+
@test_throws AssertionError check_graph_src_snk(;src_output)
574575

575576
# Test that a wrong fixed OPEX is caught by the checks.
576577
src_opex_fixed = FixedProfile(-5)
577-
@test_throws AssertionError simple_graph(;src_opex_fixed)
578+
@test_throws AssertionError check_graph_src_snk(; src_opex_fixed)
578579

579-
# Test that a wrong profile for fixed OPEX is caught by the checks.
580+
# Test that a wrong profile for fixed OPEX is caught by the checks both with a
581+
# `TwoLevelTree` and `TwoLevel` time structure
580582
# - check_fixed_opex(n::Node, 𝒯ᴵⁿᵛ, check_timeprofiles::Bool)
581583
src_opex_fixed = StrategicProfile([1])
582-
@test_throws AssertionError simple_graph(;src_opex_fixed)
584+
@test_throws AssertionError check_graph_src_snk(; src_opex_fixed)
583585
src_opex_fixed = OperationalProfile([1])
584-
@test_throws AssertionError simple_graph(;src_opex_fixed)
586+
@test_throws AssertionError check_graph_src_snk(; src_opex_fixed)
587+
src_opex_fixed = StrategicProfile([OperationalProfile([1]), OperationalProfile([1])])
588+
@test_throws AssertionError check_graph_src_snk(; src_opex_fixed)
589+
590+
T = TwoLevelTree(2, [2], SimpleTimes(5, 1); op_per_strat = 10.)
591+
src_opex_fixed = StrategicProfile([1])
592+
@test_throws AssertionError check_graph_src_snk(; src_opex_fixed, T)
593+
src_opex_fixed = StrategicProfile([1, 2, 3])
594+
@test_throws AssertionError check_graph_src_snk(; src_opex_fixed, T)
595+
src_opex_fixed = OperationalProfile([1])
596+
@test_throws AssertionError check_graph_src_snk(; src_opex_fixed, T)
597+
src_opex_fixed = StrategicStochasticProfile([
598+
[OperationalProfile([1])],
599+
[OperationalProfile([1]), OperationalProfile([1])],
600+
])
601+
@test_throws AssertionError check_graph_src_snk(; src_opex_fixed, T)
585602
end
586603

587604
# Test that the fields of a Sink are correctly checked
588605
# - check_node(n::Sink, 𝒯, modeltype::EnergyModel)
589606
@testset "Sink" begin
590607
# Test that an inconsistent Sink.penalty dictionaries is caught by the checks.
591608
snk_pen = Dict(:surplus => FixedProfile(4), :def => FixedProfile(2))
592-
@test_throws AssertionError simple_graph(;snk_pen)
609+
@test_throws AssertionError check_graph_src_snk(;snk_pen)
593610

594611
# The penalties in this Sink node lead to an infeasible optimum. Test that the
595612
# checks forbids it.
596613
snk_pen = Dict(:surplus => FixedProfile(-4), :deficit => FixedProfile(2))
597-
@test_throws AssertionError simple_graph(;snk_pen)
614+
@test_throws AssertionError check_graph_src_snk(;snk_pen)
598615

599616
# Check that a wrong capacity in a sink is caught by the checks.
600617
snk_cap = OperationalProfile(-[6, 8, 10, 6, 8])
601-
@test_throws AssertionError simple_graph(;snk_cap)
618+
@test_throws AssertionError check_graph_src_snk(;snk_cap)
602619
end
603620

604621
# Function for setting up the system for testing a `NetworkNode`

test/test_investments.jl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -227,8 +227,9 @@ using EnergyModelsInvestments
227227
# Test results
228228
# (-724 compared to 0.5.x as RefStorage as emission source does not require a charge
229229
# capacity any longer in 0.7.x)
230-
# (-10736 compared to 0.9.x due to the potential of early retirment)
231-
@test round(objective_value(m)) -313360.0
230+
# (-10736 compared to 0.9.x due to the potential of early retirement)
231+
# (-16689 compared to 10.1.x due to the bugfix 0.9.1 in EMI)
232+
@test round(objective_value(m)) -296671.0
232233

233234
# Test that investments are happening
234235
𝒯ᴵⁿᵛ = strategic_periods(get_time_struct(case))
@@ -247,7 +248,6 @@ using EnergyModelsInvestments
247248
@test sum(
248249
sum(value.(m[:stor_charge_add][n, t_inv]) > 0 for n 𝒩ᶜʰᵃʳᵍᵉ)
249250
for t_inv 𝒯ᴵⁿᵛ) > 0
250-
251251
end
252252

253253
@testset "Link - OPEX and investments" begin

0 commit comments

Comments
 (0)