Skip to content

Commit

Permalink
Improvement of checks and tests (#12)
Browse files Browse the repository at this point in the history
* Minor rework in the checks for improved understandability

* Rework of test structure
- Moved test utils to separate file
- Added tests that the checks are working
  • Loading branch information
JulStraus committed Mar 4, 2024
1 parent 99215c0 commit 58a00e1
Show file tree
Hide file tree
Showing 7 changed files with 375 additions and 134 deletions.
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-04)

### 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

2 comments on commit 58a00e1

@JulStraus
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JuliaRegistrator
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Registration pull request created: JuliaRegistries/General/102225

Tip: Release Notes

Did you know you can add release notes too? Just add markdown formatted text underneath the comment after the text
"Release notes:" and it will be added to the registry PR, and if TagBot is installed it will also be added to the
release that TagBot creates. i.e.

@JuliaRegistrator register

Release notes:

## Breaking changes

- blah

To add them here just re-invoke and the PR will be updated.

Tagging

After the above pull request is merged, it is recommended that a tag is created on this repository for the registered package version.

This will be done automatically if the Julia TagBot GitHub Action is installed, or can be done manually through the github interface, or via:

git tag -a v0.5.4 -m "<description of version>" 58a00e1fdf2f69650d41df485a8355534a333f1c
git push origin v0.5.4

Please sign in to comment.