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

Improvement of checks and tests #12

Merged
merged 3 commits into from
Mar 4, 2024
Merged
Show file tree
Hide file tree
Changes from 2 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
13 changes: 9 additions & 4 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Release notes

## Unversioned
## Version 0.5.4 (2024-03-XX)

### Examples

Expand All @@ -9,12 +9,17 @@

### NonDIsRes node

* Moved the capcity constraints through the profile to the function `constraints_capacity(n::NonDisRES, ...)`, and hence, removed the function `EMB.create_node(n::NonDisRES, ...)`.
* Moved the capacity constraints through the profile to the function `EMB.constraints_capacity(n::NonDisRES, ...)`, and hence, removed the function `EMB.create_node(n::NonDisRES, ...)`.

### Minor updates

* Added some checks and tests to the checks.
* Restructured the test folder.

## Version 0.5.3 (2024-01-30)

* Updated the restrictions on the fields individual types to be consistent.
* Added option to not include the field `data` for the individual `TransmissionMode`s.
* Updated the restrictions on the fields of individual types to be consistent.
* Added option to not include the field `data` for the individual introduced `Node`s.

## Version 0.5.2 (2024-01-19)

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 = "EnergyModelsRenewableProducers"
uuid = "b007c34f-ba52-4995-ba37-fffe79fbde35"
authors = ["Sigmund Eggen Holm <[email protected]>, Julian Straus <[email protected]>"]
version = "0.5.3"
version = "0.5.4"

[deps]
EnergyModelsBase = "5d7e687e-f956-46f3-9045-6f5a5fd49f50"
Expand Down
114 changes: 88 additions & 26 deletions src/checks.jl
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,37 @@
This method checks that the *[`NonDisRES`](@ref NonDisRES_public)* node is valid.

## Checks
- The field `profile` is required to be in the range ``[0, 1]`` for all time steps ``t ∈ \\mathcal{T}``.
- The field `cap` is required to be non-negative (similar to the `Source` check).
- The field `opex_fixed` is required to be non-negative (similar to the `Source` check).
- The values of the dictionary `output` are required to be non-negative \
(similar to the `Source` check).
- The field `profile` is required to be in the range ``[0, 1]`` for all time steps \
``t ∈ \\mathcal{T}``.
"""
function EMB.check_node(n::NonDisRES, 𝒯, modeltype::EMB.EnergyModel)
@assert_or_log sum(profile(n, t) ≤ 1 for t ∈ 𝒯) == length(𝒯) "The profile field must be less or equal to 1."
@assert_or_log sum(profile(n, t) ≥ 0 for t ∈ 𝒯) == length(𝒯) "The profile field must be non-negative."

𝒯ᴵⁿᵛ = strategic_periods(𝒯)

@assert_or_log(
sum(capacity(n, t) ≥ 0 for t ∈ 𝒯) == length(𝒯),
"The capacity must be non-negative."
)
@assert_or_log(
sum(opex_fixed(n, t_inv) ≥ 0 for t_inv ∈ 𝒯ᴵⁿᵛ) == length(𝒯ᴵⁿᵛ),
"The fixed OPEX must be non-negative."
)
@assert_or_log(
sum(outputs(n, p) ≥ 0 for p ∈ outputs(n)) == length(outputs(n)),
"The values for the Dictionary `output` must be non-negative."
)
@assert_or_log(
sum(profile(n, t) ≤ 1 for t ∈ 𝒯) == length(𝒯),
"The profile field must be less or equal to 1."
)
@assert_or_log(
sum(profile(n, t) ≥ 0 for t ∈ 𝒯) == length(𝒯),
"The profile field must be non-negative."
)
end

"""
Expand All @@ -17,50 +43,86 @@ end
This method checks that the *[`HydroStorage`](@ref HydroStorage_public)* node is valid.

## Checks
- The field `n.output` can only include a single `Resource`.\n
- The value of the field `rate_cap` is required to be non-negative.\n
- The value of the field `stor_cap` is required to be non-negative.\n
- The value of the field `fixed_opex` is required to be non-negative.\n
- The field `output` can only include a single `Resource`.\n
- The value of the field `output` is required to be smaller or equal to 1.\n
- The value of the field `input` is required to be in the range ``[0, 1]``.\n
- The value of the field `level_init` is required to be in the range \
``[level\\_min, 1] \\cdot stor\\_cap(t)`` for all time steps ``t ∈ \\mathcal{T}``.\n
- The value of the field `level_init` is required to be in the range ``[0, 1]``.\n
- The value of the field `rate_cap` is required to be non-negative.\n
- The value of the field `level_min` is required to be in the range ``[0, 1]``.
"""
function EMB.check_node(n::HydroStorage, 𝒯, modeltype::EMB.EnergyModel)
@assert_or_log length(outputs(n)) == 1 "Only one resource can be stored, so only this one can flow out."

𝒯ᴵⁿᵛ = strategic_periods(𝒯)
cap = capacity(n)

@assert_or_log(
sum(cap.rate[t] < 0 for t ∈ 𝒯) == 0,
"The production capacity in field `rate_cap` has to be non-negative."
)
@assert_or_log(
sum(cap.level[t] < 0 for t ∈ 𝒯) == 0,
"The storage capacity in field `stor_cap` has to be non-negative."
)
@assert_or_log(
sum(opex_fixed(n, t_inv) >= 0 for t_inv ∈ 𝒯ᴵⁿᵛ) == length(𝒯ᴵⁿᵛ),
"The fixed OPEX must be non-negative."
)
@assert_or_log(
length(outputs(n)) == 1,
"Only one resource can be stored, so only this one can flow out."
)

for v ∈ values(n.output)
@assert_or_log v <= 1 "The value of the stored resource in n.output has to be less than or equal to 1."
@assert_or_log(
v ≤ 1,
"The value of the `output` resource has to be less than or equal to 1."
)
@assert_or_log(
v ≥ 0,
"The value of the `output` resource has to be non-negative."
)
end

for v ∈ values(n.input)
@assert_or_log v <= 1 "The values of the input variables has to be less than or equal to 1."
@assert_or_log v >= 0 "The values of the input variables has to be non-negative."
@assert_or_log(
v ≤ 1,
"The values of the input variables have to be less than or equal to 1."
)
@assert_or_log(
v ≥ 0,
"The values of the input variables have to be non-negative."
)
end

@assert_or_log sum(level_init(n, t) <= cap.level[t] for t ∈ 𝒯) == length(𝒯) "The initial reservoir has to be less or equal to the max storage capacity."

for t_inv ∈ strategic_periods(𝒯)
for t ∈ t_inv
@assert_or_log level_init(n, t_inv) <= cap.level[t] "The initial level can not be greater than the dam capacity (" *
string(t) *
")."
end
@assert_or_log(
sum(level_init(n, t) ≤ cap.level[t] for t ∈ 𝒯) == length(𝒯),
"The initial level `level_init` has to be less or equal to the max storage capacity."
)
for t_inv ∈ 𝒯ᴵⁿᵛ

t = first(t_inv)
# Check that the reservoir isn't underfilled from the start.
@assert_or_log level_init(n, t_inv) + level_inflow(n, t) >=
level_min(n, t) * cap.level[t] "The reservoir can't be underfilled from the start (" *
string(t) *
")."
@assert_or_log(
level_init(n, t_inv) + level_inflow(n, t) ≥ level_min(n, t) * cap.level[t],
"The reservoir can't be underfilled from the start (" * string(t) * ").")
end

@assert_or_log sum(level_init(n, t) < 0 for t ∈ 𝒯) == 0 "The level_init can not be negative."

@assert_or_log sum(cap.rate[t] < 0 for t ∈ 𝒯) == 0 "The production capacity n.rate_cap has to be non-negative."
@assert_or_log(
sum(level_init(n, t) < 0 for t ∈ 𝒯) == 0,
"The field `level_init` can not be negative."
)

# level_min
@assert_or_log sum(level_min(n, t) < 0 for t ∈ 𝒯) == 0 "The level_min can not be negative."
@assert_or_log sum(level_min(n, t) > 1 for t ∈ 𝒯) == 0 "The level_min can not be larger than 1."
@assert_or_log(
sum(level_min(n, t) < 0 for t ∈ 𝒯) == 0,
"The field `level_min` can not be negative."
)
@assert_or_log(
sum(level_min(n, t) > 1 for t ∈ 𝒯) == 0,
"The field `level_min` can not be larger than 1."
)
end
60 changes: 1 addition & 59 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -8,66 +8,8 @@ using TimeStruct
const EMB = EnergyModelsBase
const EMRP = EnergyModelsRenewableProducers

CO2 = ResourceEmit("CO2", 1.0)
Power = ResourceCarrier("Power", 0.0)

TEST_ATOL = 1e-6
ROUND_DIGITS = 8
OPTIMIZER = optimizer_with_attributes(HiGHS.Optimizer, MOI.Silent() => true)

function small_graph(source = nothing, sink = nothing; ops = SimpleTimes(24, 2))

products = [Power, CO2]
# Creation of the source and sink module as well as the arrays used for nodes and links
if isnothing(source)
source = RefSource(
2,
FixedProfile(1),
FixedProfile(30),
FixedProfile(10),
Dict(Power => 1),
)
end
if isnothing(sink)
sink = RefSink(
3,
FixedProfile(20),
Dict(:surplus => FixedProfile(0), :deficit => FixedProfile(1e6)),
Dict(Power => 1),
)
end

nodes = [GenAvailability(1, products), source, sink]
links = [
Direct(21, nodes[2], nodes[1], Linear())
Direct(13, nodes[1], nodes[3], Linear())
]

# Creation of the time structure and the used global data
T = TwoLevel(4, 1, ops)
modeltype = OperationalModel(
Dict(CO2 => StrategicProfile([450, 400, 350, 300])),
Dict(CO2 => FixedProfile(0)),
CO2,
)

# Creation of the case dictionary
case = Dict(:nodes => nodes, :links => links, :products => products, :T => T)
return case, modeltype
end

function general_tests(m)
# Check if the solution is optimal.
@testset "optimal solution" begin
@test termination_status(m) == MOI.OPTIMAL

if termination_status(m) != MOI.OPTIMAL
@show termination_status(m)
end
end
end

@testset "RenewableProducers" begin
include("utils.jl")
include("test_nondisres.jl")
include("test_hydro.jl")
include("test_examples.jl")
Expand Down
Loading
Loading