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

Introduce flat demand vector and expose via BMI #1081

Merged
merged 3 commits into from
Feb 8, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
15 changes: 11 additions & 4 deletions core/src/allocation.jl
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"""Find the edges from the main network to a subnetwork."""
function find_subnetwork_connections!(p::Parameters)::Nothing
(; allocation, graph, user) = p
n_priorities = length(user.demand[1])
n_priorities = length(user.priorities)
(; subnetwork_demands, subnetwork_allocateds) = allocation
for node_id in graph[].node_ids[1]
for outflow_id in outflow_ids(graph, node_id)
Expand Down Expand Up @@ -846,7 +846,7 @@ function set_objective_priority!(
)::Nothing
(; objective_type, problem, allocation_network_id) = allocation_model
(; graph, user, allocation) = p
(; demand, node_id) = user
(; demand, demand_itp, demand_from_timeseries, node_id) = user
(; main_network_connections, subnetwork_demands) = allocation
edge_ids = graph[].edge_ids[allocation_network_id]

Expand Down Expand Up @@ -878,7 +878,14 @@ function set_objective_priority!(
end

user_idx = findsorted(node_id, node_id_user)
d = demand[user_idx][priority_idx](t)

if demand_from_timeseries[user_idx]
d = demand_itp[user_idx][priority_idx](t)
set_user_demand!(user, node_id_user, priority_idx, d)
else
d = get_user_demand(user, node_id_user, priority_idx)
end

demand_max = max(demand_max, d)
add_user_term!(ex, edge_id, objective_type, d, allocation_model)
end
Expand Down Expand Up @@ -947,7 +954,7 @@ function assign_allocations!(
push!(record.allocation_network_id, allocation_model.allocation_network_id)
push!(record.user_node_id, Int(user_node_id))
push!(record.priority, user.priorities[priority_idx])
push!(record.demand, user.demand[user_idx][priority_idx](t))
push!(record.demand, user.demand[user_idx])
push!(record.allocated, allocated)
# TODO: This is now the last abstraction before the allocation update,
# should be the average abstraction since the last allocation solve
Expand Down
2 changes: 2 additions & 0 deletions core/src/bmi.jl
Original file line number Diff line number Diff line change
Expand Up @@ -681,6 +681,8 @@
model.integrator.p.basin.drainage
elseif name == "subgrid_level"
model.integrator.p.subgrid.level
elseif name == "demand"
model.integrator.p.demand

Check warning on line 685 in core/src/bmi.jl

View check run for this annotation

Codecov / codecov/patch

core/src/bmi.jl#L685

Added line #L685 was not covered by tests
Comment on lines +684 to +685
Copy link
Contributor

Choose a reason for hiding this comment

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

Would be good to add a test on the ribasim_api side.

else
error("Unknown variable $name")
end
Expand Down
45 changes: 25 additions & 20 deletions core/src/create.jl
Original file line number Diff line number Diff line change
Expand Up @@ -637,7 +637,7 @@ function User(db::DB, config::Config)::User
static = load_structvector(db, config, UserStaticV1)
time = load_structvector(db, config, UserTimeV1)

static_node_ids, time_node_ids, node_ids, node_names, valid =
static_node_ids, time_node_ids, node_ids, _, valid =
static_and_time_node_ids(db, static, time, "User")

if !valid
Expand All @@ -650,7 +650,7 @@ function User(db::DB, config::Config)::User
active = BitVector()
min_level = Float64[]
return_factor = Float64[]
interpolations = Vector{ScalarInterpolation}[]
demand_itp = Vector{ScalarInterpolation}[]

errors = false
trivial_timespan = [nextfloat(-Inf), prevfloat(Inf)]
Expand All @@ -662,25 +662,34 @@ function User(db::DB, config::Config)::User
group in IterTools.groupby(row -> row.priority, time)
)

demand = Float64[]

# Whether the demand of a user node is given by a timeseries
demand_from_timeseries = BitVector()

for node_id in node_ids
first_row = nothing
demand = Vector{ScalarInterpolation}()
demand_itp_node_id = Vector{ScalarInterpolation}()

if node_id in static_node_ids
push!(demand_from_timeseries, false)
rows = searchsorted(static.node_id, node_id)
static_id = view(static, rows)
for p in priorities
idx = findsorted(static_id.priority, p)
demand_p = !isnothing(idx) ? static_id[idx].demand : 0.0
demand_p_itp = LinearInterpolation([demand_p, demand_p], trivial_timespan)
push!(demand, demand_p_itp)
push!(demand_itp_node_id, demand_p_itp)
push!(demand, demand_p)
end
push!(interpolations, demand)
push!(demand_itp, demand_itp_node_id)
first_row = first(static_id)
is_active = coalesce(first_row.active, true)

elseif node_id in time_node_ids
push!(demand_from_timeseries, true)
for p in priorities
push!(demand, 0.0)
if p in keys(time_priority_dict)
demand_p_itp, is_valid = get_scalar_interpolation(
config.starttime,
Expand All @@ -691,17 +700,17 @@ function User(db::DB, config::Config)::User
default_value = 0.0,
)
if is_valid
push!(demand, demand_p_itp)
push!(demand_itp_node_id, demand_p_itp)
else
@error "The demand(t) relationship for User #$node_id of priority $p from the time table has repeated timestamps, this can not be interpolated."
errors = true
end
else
demand_p_itp = LinearInterpolation([0.0, 0.0], trivial_timespan)
push!(demand, demand_p_itp)
push!(demand_itp_node_id, demand_p_itp)
end
end
push!(interpolations, demand)
push!(demand_itp, demand_itp_node_id)

first_row_idx = searchsortedfirst(time.node_id, node_id)
first_row = time[first_row_idx]
Expand Down Expand Up @@ -736,26 +745,22 @@ function User(db::DB, config::Config)::User
abstracted = Float64[],
)

record = (
time = Vector{Float64}(),
allocation_network_id = Vector{Int}(),
user_node_id = Vector{Int}(),
priority = Vector{Int}(),
demand = Vector{Float64}(),
allocated = Vector{Float64}(),
abstracted = Vector{Float64}(),
)
node_ids = NodeID.(node_ids)

return User(
NodeID.(node_ids),
user = User(
SouthEndMusic marked this conversation as resolved.
Show resolved Hide resolved
node_ids,
active,
interpolations,
demand,
demand_itp,
demand_from_timeseries,
allocated,
return_factor,
min_level,
priorities,
record,
)

return user
SouthEndMusic marked this conversation as resolved.
Show resolved Hide resolved
end

function Subgrid(db::DB, config::Config, basin::Basin)::Subgrid
Expand Down
14 changes: 10 additions & 4 deletions core/src/solve.jl
Original file line number Diff line number Diff line change
Expand Up @@ -432,7 +432,9 @@ record: Collected data of allocation optimizations for output file.
struct User <: AbstractParameterNode
node_id::Vector{NodeID}
active::BitVector
demand::Vector{Vector{ScalarInterpolation}}
demand::Vector{Float64}
demand_itp::Vector{Vector{ScalarInterpolation}}
demand_from_timeseries::BitVector
allocated::Vector{Vector{Float64}}
return_factor::Vector{Float64}
min_level::Vector{Float64}
Expand All @@ -451,17 +453,21 @@ struct User <: AbstractParameterNode
node_id,
active,
demand,
demand_itp,
demand_from_timeseries,
allocated,
return_factor,
min_level,
priorities,
record,
)
if valid_demand(node_id, demand, priorities)
if valid_demand(node_id, demand_itp, priorities)
return new(
node_id,
active,
demand,
demand_itp,
demand_from_timeseries,
allocated,
return_factor,
min_level,
Expand Down Expand Up @@ -805,7 +811,7 @@ function formulate_flow!(
t::Number,
)::Nothing
(; graph, basin) = p
(; node_id, allocated, demand, active, return_factor, min_level) = user
(; node_id, allocated, active, demand_itp, return_factor, min_level) = user

for (i, id) in enumerate(node_id)
src_id = inflow_id(graph, id)
Expand All @@ -822,7 +828,7 @@ function formulate_flow!(
# If allocation is not optimized then allocated = Inf, so the result is always
# effectively allocated = demand.
for priority_idx in eachindex(allocated[i])
alloc = min(allocated[i][priority_idx], demand[i][priority_idx](t))
alloc = min(allocated[i][priority_idx], demand_itp[i][priority_idx](t))
q += alloc
end

Expand Down
20 changes: 20 additions & 0 deletions core/src/utils.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1282,3 +1282,23 @@ end
function is_main_network(allocation_network_id::Int)::Bool
return allocation_network_id == 1
end

function get_user_demand(user::User, node_id::NodeID, priority_idx::Int)::Float64
(; demand) = user
user_idx = findsorted(user.node_id, node_id)
n_priorities = length(user.priorities)
return demand[(user_idx - 1) * n_priorities + priority_idx]
end

function set_user_demand!(
user::User,
node_id::NodeID,
priority_idx::Int,
value::Float64,
)::Nothing
Comment on lines +1286 to +1298
Copy link
Member

Choose a reason for hiding this comment

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

Could use tests.

(; demand) = user
user_idx = findsorted(user.node_id, node_id)
n_priorities = length(user.priorities)
demand[(user_idx - 1) * n_priorities + priority_idx] = value
return nothing
end
4 changes: 2 additions & 2 deletions core/src/validation.jl
Original file line number Diff line number Diff line change
Expand Up @@ -639,14 +639,14 @@ end

function valid_demand(
node_id::Vector{NodeID},
demand::Vector{
demand_itp::Vector{
Vector{LinearInterpolation{Vector{Float64}, Vector{Float64}, true, Float64}},
},
priorities::Vector{Int},
)::Bool
errors = false

for (col, id) in zip(demand, node_id)
for (col, id) in zip(demand_itp, node_id)
for (demand_p_itp, p_itp) in zip(col, priorities)
if any(demand_p_itp.u .< 0.0)
@error "Demand of user node $id with priority $p_itp should be non-negative"
Expand Down
2 changes: 2 additions & 0 deletions core/test/validation_test.jl
Original file line number Diff line number Diff line change
Expand Up @@ -411,7 +411,9 @@ end
@test_throws "Invalid demand" Ribasim.User(
[Ribasim.NodeID(1)],
[true],
[0.0],
[[LinearInterpolation([-5.0, -5.0], [-1.8, 1.8])]],
[true],
[0.0, -0.0],
[0.9],
[0.9],
Expand Down
Loading