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
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.10.3 (2026-06-10)

### Bug fixes

* Fixed addition bugs with `StrategicProfile` and `TwoLevelTree` in the checks for the investment data.

## Version 0.10.2 (2026-06-09)

### Bug fixes
Expand Down
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name = "EnergyModelsBase"
uuid = "5d7e687e-f956-46f3-9045-6f5a5fd49f50"
authors = ["Lars Hellemo <Lars.Hellemo@sintef.no>, Julian Straus <Julian.Straus@sintef.no>"]
version = "0.10.2"
version = "0.10.3"

[deps]
JuMP = "4076af6c-e467-56ae-b986-b466b2749572"
Expand Down
46 changes: 20 additions & 26 deletions ext/EMIExt/checks.jl
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,8 @@ Performs various checks on investment data introduced within EnergyModelsInvestm

## Checks
- For each field with `TimeProfile`:
- If the `TimeProfile` is a `StrategicProfile`, it will check that the profile is in
accordance with the `TimeStructure`
- If the `TimeProfile` is a `StrategicProfile` or `StrategicStochasticProfile`, it will
check that the profile is in accordance with the `TimeStructure`
- `TimeProfile`s in `InvestmentData` cannot include `OperationalProfile`,
`RepresentativeProfile`, or `ScenarioProfile` as this is not allowed through indexing
on the `TimeProfile`.
Expand All @@ -101,45 +101,39 @@ function check_inv_data(
check_timeprofiles::Bool,
)
𝒯ᴵⁿᵛ = strategic_periods(𝒯)
bool_sp = true
bool = false # Boolean for subprofile checks
bool_sp = true # Boolean

# Check on the individual time profiles
for field_name ∈ fieldnames(typeof(inv_data))
time_profile = getfield(inv_data, field_name)
if isa(time_profile, Union{Investment,LifetimeMode})
for sub_field_name ∈ fieldnames(typeof(time_profile))
sub_time_profile = getfield(time_profile, sub_field_name)
tp = getfield(inv_data, field_name)
if isa(tp, Union{Investment,LifetimeMode})
for sub_field_name ∈ fieldnames(typeof(tp))
stp = getfield(tp, sub_field_name)
submessage =
"are not allowed for the field `" * String(sub_field_name) *
"` of the mode `" * String(field_name) *
"` in the investment data" * message * "."
if isa(sub_time_profile, StrategicProfile) && check_timeprofiles
@assert_or_log(
length(sub_time_profile.vals) == length(𝒯ᴵⁿᵛ),
"Field `" * string(sub_field_name) *
"` does not match the strategic structure."
)
if (isa(stp, StrategicProfile) || isa(stp, StrategicStochasticProfile)) &&
check_timeprofiles
EMB.check_profile(string(sub_field_name), stp, 𝒯; bool)
end
EMB.check_strategic_profile(sub_time_profile, submessage)
EMB.check_strategic_profile(stp, submessage)
end
end
!isa(time_profile, TimeProfile) && continue
isa(time_profile, FixedProfile) && continue
(!isa(tp, TimeProfile) || isa(tp, FixedProfile)) && continue
submessage =
"are not allowed for the field `" * String(field_name) *
"` in the investment data" * message * "."

if isa(time_profile, StrategicProfile) && check_timeprofiles
@assert_or_log(
length(time_profile.vals) == length(𝒯ᴵⁿᵛ),
"Field `" * string(field_name) * "` does not match the strategic " *
"structure in the investment data" * message * "."
)
if (isa(tp, StrategicProfile) || isa(tp, StrategicStochasticProfile)) &&
check_timeprofiles
EMB.check_profile(string(field_name), tp, 𝒯; bool)
end
if field_name == :initial
bool_sp = EMB.check_strategic_profile(time_profile, submessage)
if field_name == :initial || field_name == :max_inst
bool_sp *= EMB.check_strategic_profile(tp, submessage)
else
EMB.check_strategic_profile(time_profile, submessage)
EMB.check_strategic_profile(tp, submessage)
end
end

Expand All @@ -156,7 +150,7 @@ function check_inv_data(
submessage =
"are not allowed for the capacity of the investment data " * message *
", if investments are allowed and the chosen investment type is `NoStartInvData`."
bool_sp = EMB.check_strategic_profile(capacity_profile, submessage)
bool_sp *= EMB.check_strategic_profile(capacity_profile, submessage)
if bool_sp
@assert_or_log(
all(capacity_profile[t_inv] ≤ EMI.max_installed(inv_data, t_inv) for t_inv ∈ 𝒯ᴵⁿᵛ),
Expand Down
37 changes: 20 additions & 17 deletions src/checks.jl
Original file line number Diff line number Diff line change
Expand Up @@ -347,25 +347,28 @@ function check_time_structure(x::AbstractElement, 𝒯)
end

"""
check_profile(fieldname, value::TimeProfile, 𝒯::TwoLevel)
check_profile(fieldname, value::StrategicProfile, 𝒯::TwoLevel)
check_profile(fieldname, value::StrategicStochasticProfile, 𝒯::TwoLevel)
check_profile(fieldname, value::TimeProfile, 𝒯::TwoLevel; bool::Bool=true)
check_profile(fieldname, value::StrategicProfile, 𝒯::TwoLevel; bool::Bool=true)
check_profile(fieldname, value::StrategicStochasticProfile, 𝒯::TwoLevel; bool::Bool=true)

check_profile(fieldname, value::TimeProfile, 𝒯::TwoLevelTree)
check_profile(fieldname, value::StrategicProfile, 𝒯::TwoLevelTree)
check_profile(fieldname, value::StrategicStochasticProfile, 𝒯::TwoLevelTree)
check_profile(fieldname, value::TimeProfile, 𝒯::TwoLevelTree; bool::Bool=true)
check_profile(fieldname, value::StrategicProfile, 𝒯::TwoLevelTree; bool::Bool=true)
check_profile(fieldname, value::StrategicStochasticProfile, 𝒯::TwoLevelTree; bool::Bool=true)

Check that an individual `TimeProfile` corresponds to the time structure `𝒯`. The individual
checks are depending on the profile type and the time structure.

The key word argument `bool` is used to identify whether subprofiles should be checked
(`true` as default) or not (`false`).
"""
function check_profile(fieldname, value::TimeProfile, 𝒯::TwoLevel)
function check_profile(fieldname, value::TimeProfile, 𝒯::TwoLevel; bool::Bool=true)
𝒯ᴵⁿᵛ = strategic_periods(𝒯)
for t_inv ∈ 𝒯ᴵⁿᵛ
bool && for t_inv ∈ 𝒯ᴵⁿᵛ
p_msg = "strategic period $(t_inv.sp)"
check_profile(fieldname, value, t_inv.operational, p_msg)
end
end
function check_profile(fieldname, value::StrategicProfile, 𝒯::TwoLevel)
function check_profile(fieldname, value::StrategicProfile, 𝒯::TwoLevel; bool::Bool=true)
𝒯ᴵⁿᵛ = strategic_periods(𝒯)

len_vals = length(value.vals)
Expand All @@ -380,7 +383,7 @@ function check_profile(fieldname, value::StrategicProfile, 𝒯::TwoLevel)
@assert_or_log(
len_vals == len_ts, "The `TimeProfile` of field `" * string(fieldname) * message
)
for t_inv ∈ 𝒯ᴵⁿᵛ
bool && for t_inv ∈ 𝒯ᴵⁿᵛ
p_msg = "strategic period $(t_inv.sp)"
check_profile(
fieldname,
Expand All @@ -390,7 +393,7 @@ function check_profile(fieldname, value::StrategicProfile, 𝒯::TwoLevel)
)
end
end
function check_profile(fieldname, value::StrategicStochasticProfile, 𝒯::TwoLevel)
function check_profile(fieldname, value::StrategicStochasticProfile, 𝒯::TwoLevel; bool::Bool=true)
@warn(
"Using `StrategicStochasticProfile` with `TwoLevel` is dangerous, " *
"as it may lead to unexpected behaviour. " *
Expand All @@ -401,14 +404,14 @@ function check_profile(fieldname, value::StrategicStochasticProfile, 𝒯::TwoLe
prof = StrategicProfile([op_prof[1] for op_prof ∈ value.vals])
check_profile(fieldname, prof, 𝒯)
end
function check_profile(fieldname, value::TimeProfile, 𝒯::TwoLevelTree)
function check_profile(fieldname, value::TimeProfile, 𝒯::TwoLevelTree; bool::Bool=true)
𝒯ᴵⁿᵛ = strategic_periods(𝒯)
for t_inv ∈ 𝒯ᴵⁿᵛ
bool && for t_inv ∈ 𝒯ᴵⁿᵛ
p_msg = "branch $(t_inv.branch) in strategic period $(t_inv.sp)"
check_profile(fieldname, value, t_inv.operational, p_msg)
end
end
function check_profile(fieldname, value::StrategicProfile, 𝒯::TwoLevelTree)
function check_profile(fieldname, value::StrategicProfile, 𝒯::TwoLevelTree; bool::Bool=true)
𝒯ˢˢᶜ = strategic_scenarios(𝒯)
t_inv_vec = []

Expand All @@ -426,7 +429,7 @@ function check_profile(fieldname, value::StrategicProfile, 𝒯::TwoLevelTree)
@assert_or_log(
len_vals == len_ts, "The `TimeProfile` of field `" * string(fieldname) * message,
)
for t_inv ∈ 𝒯ᴵⁿᵛ
bool && for t_inv ∈ 𝒯ᴵⁿᵛ
t_inv ∈ t_inv_vec && continue
push!(t_inv_vec, t_inv)

Expand All @@ -440,7 +443,7 @@ function check_profile(fieldname, value::StrategicProfile, 𝒯::TwoLevelTree)
end
end
end
function check_profile(fieldname, value::StrategicStochasticProfile, 𝒯::TwoLevelTree)
function check_profile(fieldname, value::StrategicStochasticProfile, 𝒯::TwoLevelTree; bool::Bool=true)
# Check for the number of strategic periods
len_vals = length(value.vals)
len_ts = n_strat_per(𝒯)
Expand Down Expand Up @@ -475,7 +478,7 @@ function check_profile(fieldname, value::StrategicStochasticProfile, 𝒯::TwoLe
end

# Check the sub profiles
for t_inv ∈ strategic_periods(𝒯)
bool && for t_inv ∈ strategic_periods(𝒯)
p_msg = "branch $(t_inv.branch) in strategic period $(t_inv.sp)"
sp_prof = value.vals[minimum([t_inv.sp, length(value.vals)])]
check_profile(
Expand Down
Loading
Loading