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

Piecewise linear basin profile approx in allocation #2108

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
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
103 changes: 103 additions & 0 deletions core/src/allocation_init.jl
Original file line number Diff line number Diff line change
Expand Up @@ -394,6 +394,109 @@
return nothing
end

function add_basin_profiles!(

Check warning on line 397 in core/src/allocation_init.jl

View check run for this annotation

Codecov / codecov/patch

core/src/allocation_init.jl#L397

Added line #L397 was not covered by tests
problem::JuMP.Model,
p::Parameters,
subnetwork_id::Int32,
)::Nothing
(; graph, basin) = p
(; node_id, storage_to_level) = basin
n_samples_per_interval = 5

Check warning on line 404 in core/src/allocation_init.jl

View check run for this annotation

Codecov / codecov/patch

core/src/allocation_init.jl#L402-L404

Added lines #L402 - L404 were not covered by tests

# TODO: This function is getting quite large, better split it up in a function
# adding variables and a function adding constraints

# Basin node IDs within the current subnetwork
node_ids = filter(id -> graph[id].subnetwork_id == subnetwork_id, node_id)

Check warning on line 410 in core/src/allocation_init.jl

View check run for this annotation

Codecov / codecov/patch

core/src/allocation_init.jl#L410

Added line #L410 was not covered by tests

# Define the storages and levels of the basin
problem[:basin_storage] = JuMP.@variable(problem, basin_storage[node_ids] >= 0)
problem[:basin_level] = JuMP.@variable(problem, basin_level[node_ids] >= 0)

Check warning on line 414 in core/src/allocation_init.jl

View check run for this annotation

Codecov / codecov/patch

core/src/allocation_init.jl#L413-L414

Added lines #L413 - L414 were not covered by tests

# The number of points in the piecewise linear approximation
# of the level(storage) relationship
n_points = Dict{NodeID, Int}()

Check warning on line 418 in core/src/allocation_init.jl

View check run for this annotation

Codecov / codecov/patch

core/src/allocation_init.jl#L418

Added line #L418 was not covered by tests

# The data for the piecewise linear basin profile approximations
storages = Dict{NodeID, Vector{Float64}}()
levels = Dict{NodeID, Vector{Float64}}()

Check warning on line 422 in core/src/allocation_init.jl

View check run for this annotation

Codecov / codecov/patch

core/src/allocation_init.jl#L421-L422

Added lines #L421 - L422 were not covered by tests

for id in node_ids
itp = storage_to_level[id.idx]
(graph[id].subnetwork_id != subnetwork_id) && continue

Check warning on line 426 in core/src/allocation_init.jl

View check run for this annotation

Codecov / codecov/patch

core/src/allocation_init.jl#L424-L426

Added lines #L424 - L426 were not covered by tests

# TODO: What do to with extrapolation?
# Get (storage, level) points for linear approximation by evaluating the smooth
# interpolation at n_samples_per_interval evenly spaced points between the data points
storage = unique(

Check warning on line 431 in core/src/allocation_init.jl

View check run for this annotation

Codecov / codecov/patch

core/src/allocation_init.jl#L431

Added line #L431 was not covered by tests
vcat(
[
itp.(
range(itp.t[i], itp.t[i + 1]; length = n_samples_per_interval + 1)
) for i in 1:(length(itp.t) - 1)
]...,
),
)
level = itp.(storage)

Check warning on line 440 in core/src/allocation_init.jl

View check run for this annotation

Codecov / codecov/patch

core/src/allocation_init.jl#L440

Added line #L440 was not covered by tests

storages[id] = storage
levels[id] = level
n_points[id] = length(storage)
end

Check warning on line 445 in core/src/allocation_init.jl

View check run for this annotation

Codecov / codecov/patch

core/src/allocation_init.jl#L442-L445

Added lines #L442 - L445 were not covered by tests

# Define auxiliary variables for the basin profiles within this subnetwork
indices_points =
Iterators.flatten(map(id -> ((id, i) for i in 1:n_points[id]), node_ids))
problem[:aux_basin_profile] =
JuMP.@variable(problem, aux_basin_profile[indices_points] >= 0)

Check warning on line 451 in core/src/allocation_init.jl

View check run for this annotation

Codecov / codecov/patch

core/src/allocation_init.jl#L448-L451

Added lines #L448 - L451 were not covered by tests

# Unity sum of auxiliary variables
problem[:aux_basin_profile_unity_sum] = JuMP.@constraint(

Check warning on line 454 in core/src/allocation_init.jl

View check run for this annotation

Codecov / codecov/patch

core/src/allocation_init.jl#L454

Added line #L454 was not covered by tests
problem,
[id = node_ids],
sum(aux_basin_profile[(id, i)] for i in 1:n_points[id]) == 1,
base_name = "Basin_profile_aux_sum"
)

# Define binary variables for in which interval the storage lies
indices_intervals =
Iterators.flatten(map(id -> ((id, i) for i in 1:(n_points[id] - 1)), node_ids))
intv_bool_basin_profile =
JuMP.@variable(problem, intv_bool_basin_profile[indices_intervals], Bin)
problem[:intv_bool_basin_profile] = intv_bool_basin_profile

Check warning on line 466 in core/src/allocation_init.jl

View check run for this annotation

Codecov / codecov/patch

core/src/allocation_init.jl#L462-L466

Added lines #L462 - L466 were not covered by tests

# The sum of the binary variables per basin is 1 => the storage can only lie in one interval
problem[:intv_bool_basin_profile_sum] = JuMP.@constraint(

Check warning on line 469 in core/src/allocation_init.jl

View check run for this annotation

Codecov / codecov/patch

core/src/allocation_init.jl#L469

Added line #L469 was not covered by tests
problem,
[id = node_ids],
sum(intv_bool_basin_profile[(id, i)] for i in 1:(n_points[id] - 1)) == 1,
base_name = "intv_bool_basin_profile_sum"
)

# The constraints describing the piecewise linear approximation of the basin profiles
problem[:basin_profile_storage] = JuMP.@constraint(

Check warning on line 477 in core/src/allocation_init.jl

View check run for this annotation

Codecov / codecov/patch

core/src/allocation_init.jl#L477

Added line #L477 was not covered by tests
problem,
[id = node_ids],
sum(
intv_bool_basin_profile[(id, i)] * (
storages[id][i] * aux_basin_profile[(id, i)] +
storages[id][i + 1] * aux_basin_profile[(id, i + 1)]
) for i in 1:(n_points[id] - 1)
) == basin_storage[id]
)
problem[:basin_profile_level] = JuMP.@constraint(

Check warning on line 487 in core/src/allocation_init.jl

View check run for this annotation

Codecov / codecov/patch

core/src/allocation_init.jl#L487

Added line #L487 was not covered by tests
problem,
[id = node_ids],
sum(
intv_bool_basin_profile[(id, i)] * (
levels[id][i] * aux_basin_profile[(id, i)] +
levels[id][i + 1] * aux_basin_profile[(id, i + 1)]
) for i in 1:(n_points[id] - 1)
) == basin_level[id]
)
return nothing

Check warning on line 497 in core/src/allocation_init.jl

View check run for this annotation

Codecov / codecov/patch

core/src/allocation_init.jl#L497

Added line #L497 was not covered by tests
end

"""
Construct the allocation problem for the current subnetwork as a JuMP model.
"""
Expand Down