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

Node activity #425

Merged
merged 9 commits into from
Jul 17, 2023
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
35 changes: 25 additions & 10 deletions core/src/create.jl
Original file line number Diff line number Diff line change
Expand Up @@ -48,14 +48,16 @@ function parse_static(
# Index in the output vectors for this node ID
node_idx = 1

is_controllable = hasfield(static_type, :control_state)

for row in static
if node_id != row.node_id
node_idx += 1
node_id = row.node_id
end

# If this row is a control state, add it to the control mapping
if !ismissing(row.control_state)
if is_controllable && !ismissing(row.control_state)
control_values = NamedTuple{columnnames_variables}(values(row)[mask])
control_mapping[(row.node_id, row.control_state)] = control_values
end
Expand Down Expand Up @@ -99,17 +101,19 @@ end

function LinearResistance(db::DB, config::Config)::LinearResistance
static = load_structvector(db, config, LinearResistanceStaticV1)
defaults = (;)
defaults = (; active = true)
static_parsed = parse_static(static, db, "LinearResistance", defaults)
return LinearResistance(
static_parsed.node_id,
static_parsed.active,
static_parsed.resistance,
static_parsed.control_mapping,
)
end

function TabulatedRatingCurve(db::DB, config::Config)::TabulatedRatingCurve
static = load_structvector(db, config, TabulatedRatingCurveStaticV1)
active = coalesce.(static.active, true)
time = load_structvector(db, config, TabulatedRatingCurveTimeV1)

static_node_ids = Set(static.node_id)
Expand All @@ -134,15 +138,16 @@ function TabulatedRatingCurve(db::DB, config::Config)::TabulatedRatingCurve
end
push!(interpolations, interpolation)
end
return TabulatedRatingCurve(node_ids, interpolations, time)
return TabulatedRatingCurve(node_ids, active, interpolations, time)
end

function ManningResistance(db::DB, config::Config)::ManningResistance
static = load_structvector(db, config, ManningResistanceStaticV1)
defaults = (;)
defaults = (; active = true)
static_parsed = parse_static(static, db, "ManningResistance", defaults)
return ManningResistance(
static_parsed.node_id,
static_parsed.active,
static_parsed.length,
static_parsed.manning_n,
static_parsed.profile_width,
Expand All @@ -153,33 +158,42 @@ end

function FractionalFlow(db::DB, config::Config)::FractionalFlow
static = load_structvector(db, config, FractionalFlowStaticV1)
defaults = (;)
defaults = (; active = true)
static_parsed = parse_static(static, db, "FractionalFlow", defaults)
return FractionalFlow(
static_parsed.node_id,
static_parsed.active,
static_parsed.fraction,
static_parsed.control_mapping,
)
end

function LevelBoundary(db::DB, config::Config)::LevelBoundary
static = load_structvector(db, config, LevelBoundaryStaticV1)
return LevelBoundary(static.node_id, static.level)
defaults = (; active = true)
static_parsed = parse_static(static, db, "LevelBoundary", defaults)
return LevelBoundary(static_parsed.node_id, static_parsed.active, static_parsed.level)
end

function FlowBoundary(db::DB, config::Config)::FlowBoundary
static = load_structvector(db, config, FlowBoundaryStaticV1)
return FlowBoundary(static.node_id, static.flow_rate)
defaults = (; active = true)
static_parsed = parse_static(static, db, "FlowBoundary", defaults)
return FlowBoundary(
static_parsed.node_id,
static_parsed.active,
static_parsed.flow_rate,
)
end

function Pump(db::DB, config::Config)::Pump
static = load_structvector(db, config, PumpStaticV1)

defaults = (; min_flow_rate = 0.0, max_flow_rate = NaN)
defaults = (; min_flow_rate = 0.0, max_flow_rate = NaN, active = true)
static_parsed = parse_static(static, db, "Pump", defaults)

return Pump(
static_parsed.node_id,
static_parsed.active,
static_parsed.flow_rate,
static_parsed.min_flow_rate,
static_parsed.max_flow_rate,
Expand Down Expand Up @@ -279,13 +293,14 @@ end

function PidControl(db::DB, config::Config)::PidControl
static = load_structvector(db, config, PidControlStaticV1)
defaults = (proportional = NaN, integral = NaN, derivative = NaN)
defaults = (active = true, proportional = NaN, integral = NaN, derivative = NaN)
static_parsed = parse_static(static, db, "PidControl", defaults)

error = zero(static_parsed.node_id)

return PidControl(
static_parsed.node_id,
static_parsed.active,
static_parsed.listen_node_id,
static_parsed.proportional,
static_parsed.integral,
Expand Down
91 changes: 72 additions & 19 deletions core/src/solve.jl
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@
"""
struct TabulatedRatingCurve{C} <: AbstractParameterNode
node_id::Vector{Int}
active::BitVector
Copy link
Member

Choose a reason for hiding this comment

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

Shall we simply use Vector{Bool} here? It uses more memory, but should be faster to read and write, since bits don't need to be shuffled around.

Copy link
Contributor

Choose a reason for hiding this comment

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

I was talking with Bart about this, the reason I suggested BitVector is that he was using a function that returned a BitVector, so no conversion step was necessary that way.

But if you think Vector{Bool} is faster, we should probably switch.

Copy link
Member

Choose a reason for hiding this comment

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

Ok let's keep it the same. If it causes issues or shows up in profiling we can switch later.

Apparently the performance is not so clear cut: https://discourse.julialang.org/t/bitvector-vs-vector-bool-as-default-on-comparison-operations/49431

tables::Vector{Interpolation}
time::StructVector{TabulatedRatingCurveTimeV1, C, Int}
end
Expand All @@ -138,6 +139,7 @@
"""
struct LinearResistance <: AbstractParameterNode
node_id::Vector{Int}
active::BitVector
resistance::Vector{Float64}
control_mapping::Dict{Tuple{Int, String}, NamedTuple}
end
Expand Down Expand Up @@ -177,6 +179,7 @@
"""
struct ManningResistance <: AbstractParameterNode
node_id::Vector{Int}
active::BitVector
length::Vector{Float64}
manning_n::Vector{Float64}
profile_width::Vector{Float64}
Expand All @@ -193,6 +196,7 @@
"""
struct FractionalFlow <: AbstractParameterNode
node_id::Vector{Int}
active::BitVector
fraction::Vector{Float64}
control_mapping::Dict{Tuple{Int, String}, NamedTuple}
end
Expand All @@ -204,6 +208,7 @@
"""
struct LevelBoundary <: AbstractParameterNode
node_id::Vector{Int}
active::BitVector
level::Vector{Float64}
end

Expand All @@ -213,6 +218,7 @@
"""
struct FlowBoundary <: AbstractParameterNode
node_id::Vector{Int}
active::BitVector
flow_rate::Vector{Float64}
end

Expand All @@ -223,6 +229,7 @@
"""
struct Pump <: AbstractParameterNode
node_id::Vector{Int}
active::BitVector
flow_rate::Vector{Float64}
min_flow_rate::Vector{Float64}
max_flow_rate::Vector{Float64}
Expand Down Expand Up @@ -262,6 +269,7 @@

struct PidControl <: AbstractParameterNode
node_id::Vector{Int}
active::BitVector
listen_node_id::Vector{Int}
proportional::Vector{Float64}
integral::Vector{Float64}
Expand Down Expand Up @@ -331,6 +339,7 @@
end

function continuous_control!(
u::ComponentVector{Float64},
du::ComponentVector{Float64},
pid_control::PidControl,
p::Parameters,
Expand All @@ -342,11 +351,22 @@
(; min_flow_rate, max_flow_rate) = pump
(; dstorage) = basin
(; graph_control) = connectivity
(; node_id, proportional, integral, derivative, listen_node_id, error) = pid_control
(; node_id, active, proportional, integral, derivative, listen_node_id, error) =
pid_control

get_error!(pid_control, p)

for (i, id) in enumerate(node_id)
controlled_node_id = only(outneighbors(graph_control, id))
# TODO: support the use of id_index
controlled_node_idx = findfirst(pump.node_id .== controlled_node_id)

if !active[i]
du.integral[i] = 0.0
u.integral[i] = 0.0
return

Check warning on line 367 in core/src/solve.jl

View check run for this annotation

Codecov / codecov/patch

core/src/solve.jl#L365-L367

Added lines #L365 - L367 were not covered by tests
end

du.integral[i] = error[i]

listened_node_id = listen_node_id[i]
Expand Down Expand Up @@ -378,9 +398,6 @@
flow_rate = min(flow_rate, max_flow_rate[i])
end

controlled_node_id = only(outneighbors(graph_control, id))
# TODO: support the use of id_index
controlled_node_idx = findfirst(pump.node_id .== controlled_node_id)
pump.flow_rate[controlled_node_idx] = flow_rate
end
return nothing
Expand All @@ -392,13 +409,19 @@
function formulate!(linear_resistance::LinearResistance, p::Parameters)::Nothing
(; connectivity) = p
(; graph_flow, flow) = connectivity
(; node_id, resistance) = linear_resistance
(; node_id, active, resistance) = linear_resistance
for (i, id) in enumerate(node_id)
basin_a_id = only(inneighbors(graph_flow, id))
basin_b_id = only(outneighbors(graph_flow, id))
q = (get_level(p, basin_a_id) - get_level(p, basin_b_id)) / resistance[i]
flow[basin_a_id, id] = q
flow[id, basin_b_id] = q

if active[i]
q = (get_level(p, basin_a_id) - get_level(p, basin_b_id)) / resistance[i]
flow[basin_a_id, id] = q
flow[id, basin_b_id] = q
else
flow[basin_a_id, id] = 0.0
flow[id, basin_b_id] = 0.0

Check warning on line 423 in core/src/solve.jl

View check run for this annotation

Codecov / codecov/patch

core/src/solve.jl#L422-L423

Added lines #L422 - L423 were not covered by tests
end
end
return nothing
end
Expand All @@ -409,11 +432,17 @@
function formulate!(tabulated_rating_curve::TabulatedRatingCurve, p::Parameters)::Nothing
(; connectivity) = p
(; graph_flow, flow) = connectivity
(; node_id, tables) = tabulated_rating_curve
(; node_id, active, tables) = tabulated_rating_curve
for (i, id) in enumerate(node_id)
upstream_basin_id = only(inneighbors(graph_flow, id))
downstream_ids = outneighbors(graph_flow, id)
q = tables[i](get_level(p, upstream_basin_id))

if active[i]
q = tables[i](get_level(p, upstream_basin_id))
else
q = 0.0

Check warning on line 443 in core/src/solve.jl

View check run for this annotation

Codecov / codecov/patch

core/src/solve.jl#L443

Added line #L443 was not covered by tests
end

flow[upstream_basin_id, id] = q
for downstream_id in downstream_ids
flow[id, downstream_id] = q
Expand Down Expand Up @@ -464,11 +493,18 @@
function formulate!(manning_resistance::ManningResistance, p::Parameters)::Nothing
(; basin, connectivity) = p
(; graph_flow, flow) = connectivity
(; node_id, length, manning_n, profile_width, profile_slope) = manning_resistance
(; node_id, active, length, manning_n, profile_width, profile_slope) =
manning_resistance
for (i, id) in enumerate(node_id)
basin_a_id = only(inneighbors(graph_flow, id))
basin_b_id = only(outneighbors(graph_flow, id))

if !active[i]
flow[basin_a_id, id] = 0.0
flow[id, basin_b_id] = 0.0
continue

Check warning on line 505 in core/src/solve.jl

View check run for this annotation

Codecov / codecov/patch

core/src/solve.jl#L503-L505

Added lines #L503 - L505 were not covered by tests
end

h_a = get_level(p, basin_a_id)
h_b = get_level(p, basin_b_id)
bottom_a, bottom_b = basin_bottoms(basin, basin_a_id, basin_b_id, id)
Expand Down Expand Up @@ -507,11 +543,16 @@
function formulate!(fractional_flow::FractionalFlow, p::Parameters)::Nothing
(; connectivity) = p
(; graph_flow, flow) = connectivity
(; node_id, fraction) = fractional_flow
(; node_id, active, fraction) = fractional_flow
for (i, id) in enumerate(node_id)
upstream_id = only(inneighbors(graph_flow, id))
downstream_id = only(outneighbors(graph_flow, id))
flow[id, downstream_id] = flow[upstream_id, id] * fraction[i]

if active[i]
upstream_id = only(inneighbors(graph_flow, id))
flow[id, downstream_id] = flow[upstream_id, id] * fraction[i]
else
flow[id, downstream_id] = 0.0

Check warning on line 554 in core/src/solve.jl

View check run for this annotation

Codecov / codecov/patch

core/src/solve.jl#L554

Added line #L554 was not covered by tests
end
end
return nothing
end
Expand All @@ -523,11 +564,16 @@
)::Nothing
(; connectivity, basin) = p
(; graph_flow, flow) = connectivity
(; node_id, flow_rate) = flow_boundary
(; node_id, active, flow_rate) = flow_boundary

for (id, rate) in zip(node_id, flow_rate)
for (id, isactive, rate) in zip(node_id, active, flow_rate)
# Requirement: edge points away from the flow boundary
for dst_id in outneighbors(graph_flow, id)
if !isactive
flow[id, dst_id] = 0.0
continue

Check warning on line 574 in core/src/solve.jl

View check run for this annotation

Codecov / codecov/patch

core/src/solve.jl#L573-L574

Added lines #L573 - L574 were not covered by tests
end

# Adding water is always possible
if rate >= 0
flow[id, dst_id] = rate
Expand All @@ -547,10 +593,17 @@
function formulate!(pump::Pump, p::Parameters, storage::AbstractVector{Float64})::Nothing
(; connectivity, basin, level_boundary) = p
(; graph_flow, flow) = connectivity
(; node_id, flow_rate) = pump
for (id, rate) in zip(node_id, flow_rate)
(; node_id, active, flow_rate) = pump
for (id, isactive, rate) in zip(node_id, active, flow_rate)
src_id = only(inneighbors(graph_flow, id))
dst_id = only(outneighbors(graph_flow, id))

if !isactive
flow[src_id, id] = 0.0
flow[id, dst_id] = 0.0
continue

Check warning on line 604 in core/src/solve.jl

View check run for this annotation

Codecov / codecov/patch

core/src/solve.jl#L602-L604

Added lines #L602 - L604 were not covered by tests
end

# negative flow_rate means pumping against edge direction
intake_id = rate >= 0 ? src_id : dst_id

Expand Down Expand Up @@ -633,7 +686,7 @@
formulate!(du, basin, storage, t)

# PID control (does not set flows)
continuous_control!(du, pid_control, p, integral)
continuous_control!(u, du, pid_control, p, integral)

# First formulate intermediate flows
formulate_flows!(p, storage)
Expand Down
Loading