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

UPD: Decomposition of node-connecting components, addition of quadratic formulations #111

Merged
merged 14 commits into from
Oct 12, 2020
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
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
WaterModels.jl Change Log
=========================

### v0.6.0
- Decoupled `check_valve` and `shutoff_valve` objects from `pipe` objects.
- Introduced a new `valve` component.
- Introduced a new `short_pipe` component.
- Renamed `pressure_reducing_valve` to `regulator`.
- Decomposed component constraints a bit more.
- Implemented `QRD` and `CQRD` formulations.

### v0.5.0
- Rename formulation types.

Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ This decoupling enables the definition of a wide variety of optimization formula
* CRD - continuous (convex) relaxation- and direction-based formulation
* LA - linear approximation-based formulation
* LRD - linear relaxation- and direction-based formulation
* QRD - nonconvex quadratic relaxation- and direction-based formulation
* CQRD - convex quadratic relaxation- and direction-based formulation

## Documentation
The package [documentation](https://lanl-ansi.github.io/WaterModels.jl/latest) includes a [quick start guide](https://lanl-ansi.github.io/WaterModels.jl/latest/quickguide).
Expand Down
3 changes: 3 additions & 0 deletions src/WaterModels.jl
Original file line number Diff line number Diff line change
Expand Up @@ -61,13 +61,16 @@ include("form/crd.jl")
include("form/la.jl")
include("form/lrd.jl")
include("form/nc.jl")
include("form/qrd.jl")
include("form/cqrd.jl")

include("prob/wf.jl")
include("prob/owf.jl")
include("prob/des.jl")

include("util/unbinarize.jl")
include("util/obbt.jl")
include("util/compute_cuts.jl")

include("core/export.jl")
end
29 changes: 11 additions & 18 deletions src/core/base.jl
Original file line number Diff line number Diff line change
Expand Up @@ -137,13 +137,13 @@ system-wide values that need to be computed globally.

Some of the common keys include:

* `:pipe` -- the set of pipes in the network without valves,
* `:check_valve` -- the set of pipes in the network with check valves,
* `:shutoff_valve` -- the set of pipes in the network wiith shutoff valves,
* `:pressure_reducing_valve` -- the set of pressure reducing valves in the network,
* `:pipe` -- the set of pipes in the network,
* `:pump` -- the set of pumps in the network,
* `:regulator` -- the set of regulators in the network,
* `:short_pipe` -- the set of short pipes in the network,
* `:valve` -- the set of valves in the network,
* `:node` -- the set of all nodes in the network,
* `:junction` -- the set of junctions in the network,
* `:demand` -- the set of demands in the network,
* `:reservoir` -- the set of reservoirs in the network,
* `:tank` -- the set of tanks in the network
"""
Expand All @@ -155,32 +155,25 @@ end
function _ref_add_core!(nw_refs::Dict{Int,<:Any})
for (nw, ref) in nw_refs
# Collect dispatchable and nondispatchable nodal components in the network.
ref[:dispatchable_junction] = filter(x -> x.second["dispatchable"], ref[:junction])
ref[:nondispatchable_junction] = filter(x -> !x.second["dispatchable"], ref[:junction])
ref[:dispatchable_demand] = filter(x -> x.second["dispatchable"], ref[:demand])
ref[:nondispatchable_demand] = filter(x -> !x.second["dispatchable"], ref[:demand])

# Compute resistances for pipe-type components in the network.
ref[:resistance] = calc_resistances(ref[:pipe], ref[:viscosity], ref[:head_loss])
ref[:resistance_cost] =
calc_resistance_costs(ref[:pipe], ref[:viscosity], ref[:head_loss])

# TODO: Check and shutoff valves should not have pipe properties.
ref[:check_valve] = filter(x -> x.second["has_check_valve"], ref[:pipe])
ref[:shutoff_valve] = filter(x -> x.second["has_shutoff_valve"], ref[:pipe])
ref[:pipe] = filter(x -> !x.second["has_check_valve"], ref[:pipe])
ref[:pipe] = filter(x -> !x.second["has_shutoff_valve"], ref[:pipe])
ref[:des_pipe] = filter(is_des_pipe, ref[:pipe])
ref[:pipe] = filter(!is_des_pipe, ref[:pipe])

# Create mappings of "from" and "to" arcs for link- (i.e., edge-) type components.
for name in
["check_valve", "shutoff_valve", "pipe", "pressure_reducing_valve", "pump"]
for name in ["pipe", "pump", "regulator", "short_pipe", "valve"]
fr_sym, to_sym = Symbol(name * "_fr"), Symbol(name * "_to")
ref[fr_sym] = [(i, c["node_fr"], c["node_to"]) for (i, c) in ref[Symbol(name)]]
ref[to_sym] = [(i, c["node_to"], c["node_fr"]) for (i, c) in ref[Symbol(name)]]
ref[fr_sym] = [(a, c["node_fr"], c["node_to"]) for (a, c) in ref[Symbol(name)]]
ref[to_sym] = [(a, c["node_to"], c["node_fr"]) for (a, c) in ref[Symbol(name)]]
end

# Set up dictionaries mapping node indices to attached component indices.
for name in ["junction", "tank", "reservoir"]
for name in ["demand", "tank", "reservoir"]
name_sym = Symbol("node_" * name)
ref[name_sym] = Dict{Int,Array{Int,1}}(i => Int[] for (i, node) in ref[:node])

Expand Down
105 changes: 54 additions & 51 deletions src/core/constraint.jl
Original file line number Diff line number Diff line change
Expand Up @@ -3,84 +3,87 @@
#########################################################################


"""
constraint_reservoir_head(wm, n, i, head)
"""
function constraint_reservoir_head(wm::AbstractWaterModel, n::Int, i::Int, head::Float64)
h = var(wm, n, :h, i)
c = JuMP.@constraint(wm.model, h == head)
con(wm, n, :reservoir_head)[i] = c
end


"""
constraint_flow_conservation(
wm, n, i, check_valve_fr, check_valve_to, pipe_fr, pipe_to, des_pipe_fr,
des_pipe_to, pump_fr, pump_to, pressure_reducing_valve_fr,
pressure_reducing_valve_to, shutoff_valve_fr, shutoff_valve_to, reservoirs, tanks,
dispatachable_junctions, fixed_demand)
wm, n, i, pipe_fr, pipe_to, des_pipe_fr, des_pipe_to, pump_fr, pump_to,
regulator_fr, regulator_to, short_pipe_fr, short_pipe_to, valve_fr, valve_to,
reservoirs, tanks, dispatachable_demands, fixed_demand)

Adds a constraint that ensures flow conservation at a node in the network.
"""
function constraint_flow_conservation(
wm::AbstractWaterModel, n::Int, i::Int, check_valve_fr::Array{Int64,1},
check_valve_to::Array{Int64,1}, pipe_fr::Array{Int64,1}, pipe_to::Array{Int64,1},
des_pipe_fr::Array{Int64,1}, des_pipe_to::Array{Int64,1}, pump_fr::Array{Int64,1},
pump_to::Array{Int64,1}, pressure_reducing_valve_fr::Array{Int64,1},
pressure_reducing_valve_to::Array{Int64,1}, shutoff_valve_fr::Array{Int64,1},
shutoff_valve_to::Array{Int64,1}, reservoirs::Array{Int64,1}, tanks::Array{Int64,1},
dispatchable_junctions::Array{Int64,1}, fixed_demand::Float64)
wm::AbstractWaterModel, n::Int, i::Int, pipe_fr::Array{Int64,1},
pipe_to::Array{Int64,1}, des_pipe_fr::Array{Int64,1}, des_pipe_to::Array{Int64,1},
pump_fr::Array{Int64,1}, pump_to::Array{Int64,1}, regulator_fr::Array{Int64,1},
regulator_to::Array{Int64,1}, short_pipe_fr::Array{Int64,1},
short_pipe_to::Array{Int64,1}, valve_fr::Array{Int64,1}, valve_to::Array{Int64,1},
reservoirs::Array{Int64,1}, tanks::Array{Int64,1}, dispatchable_demands::Array{Int64,1},
fixed_demand::Float64)
# Collect flow variable references per component.
q_check_valve = var(wm, n, :q_check_valve)
q_pipe = var(wm, n, :q_pipe)
q_des_pipe = var(wm, n, :q_des_pipe_sum)
q_pump = var(wm, n, :q_pump)
q_pressure_reducing_valve = var(wm, n, :q_pressure_reducing_valve)
q_shutoff_valve = var(wm, n, :q_shutoff_valve)
q_pipe, q_des_pipe = var(wm, n, :q_pipe), var(wm, n, :q_des_pipe_sum)
q_pump, q_regulator = var(wm, n, :q_pump), var(wm, n, :q_regulator)
q_short_pipe, q_valve = var(wm, n, :q_short_pipe), var(wm, n, :q_valve)
q_reservoir, q_tank = var(wm, n, :q_reservoir), var(wm, n, :q_tank)
q_demand = var(wm, n, :q_demand)

# Add the flow conservation constraint.
c = JuMP.@constraint(wm.model, -
sum(q_check_valve[a] for a in check_valve_fr) +
sum(q_check_valve[a] for a in check_valve_to) -
con(wm, n, :flow_conservation)[i] = JuMP.@constraint(wm.model, -
sum(q_pipe[a] for a in pipe_fr) + sum(q_pipe[a] for a in pipe_to) -
sum(q_des_pipe[a] for a in des_pipe_fr) + sum(q_des_pipe[a] for a in des_pipe_to) -
sum(q_pump[a] for a in pump_fr) + sum(q_pump[a] for a in pump_to) -
sum(q_pressure_reducing_valve[a] for a in pressure_reducing_valve_fr) +
sum(q_pressure_reducing_valve[a] for a in pressure_reducing_valve_to) -
sum(q_shutoff_valve[a] for a in shutoff_valve_fr) +
sum(q_shutoff_valve[a] for a in shutoff_valve_to) == -
sum(var(wm, n, :qr, k) for k in reservoirs) -
sum(var(wm, n, :qt, k) for k in tanks) +
sum(var(wm, n, :demand, k) for k in dispatchable_junctions) + fixed_demand)

con(wm, n, :flow_conservation)[i] = c
sum(q_regulator[a] for a in regulator_fr) +
sum(q_regulator[a] for a in regulator_to) -
sum(q_short_pipe[a] for a in short_pipe_fr) +
sum(q_short_pipe[a] for a in short_pipe_to) -
sum(q_valve[a] for a in valve_fr) + sum(q_valve[a] for a in valve_to) == -
sum(q_reservoir[k] for k in reservoirs) - sum(q_tank[k] for k in tanks) +
sum(q_demand[k] for k in dispatchable_demands) + fixed_demand)
end


function constraint_tank_state_initial(wm::AbstractWaterModel, n::Int, i::Int, V_0::Float64)
"""
constraint_tank_volume_fixed(wm, n, i, V_0)

Adds a constraint that ensures the volume of a tank at some time step is fixed. Here, `wm`
is the WaterModels object, `n` is the index of a subnetwork within a multinetwork, `i` is
the index of the tank, and `V_0` is the fixed volume of the tank that is desired.
"""
function constraint_tank_volume_fixed(wm::AbstractWaterModel, n::Int, i::Int, V_0::Float64)
V = var(wm, n, :V, i)
c = JuMP.@constraint(wm.model, V == V_0)
con(wm, n, :tank_state)[i] = c
con(wm, n, :tank_volume)[i] = c
end


"""
constraint_tank_state(wm, n_1, n_2, i, time_step)
constraint_tank_volume(wm, n_1, n_2, i, time_step)

Adds a constraint that integrates the volume of a tank forward in time. Here, `wm` is the
WaterModels object, `n_1` is the index of a subnetwork within a multinetwork, `n_2` is the
index of another subnetwork forward in time, relative to `n_1`, i is the index of the tank,
and time_step is the time step (in seconds) between networks `n_1` and `n_2`.
and time_step is the time step (in seconds) of the interval from network `n_1` to `n_2`.
"""
function constraint_tank_state(wm::AbstractWaterModel, n_1::Int, n_2::Int, i::Int, time_step::Float64)
qt = var(wm, n_1, :qt, i) # Tank outflow.
function constraint_tank_volume(wm::AbstractWaterModel, n_1::Int, n_2::Int, i::Int, time_step::Float64)
qt = var(wm, n_1, :q_tank, i) # Tank outflow.
V_1, V_2 = var(wm, n_1, :V, i), var(wm, n_2, :V, i)
c = JuMP.@constraint(wm.model, V_1 - time_step * qt == V_2)
con(wm, n_2, :tank_state)[i] = c
con(wm, n_2, :tank_volume)[i] = c
end


function constraint_recover_volume(wm::AbstractWaterModel, i::Int, n_1::Int, n_f::Int)
_initialize_con_dict(wm, :recover_volume, nw=n_f)
V_1, V_f = var(wm, n_1, :V, i), var(wm, n_f, :V, i)
c = JuMP.@constraint(wm.model, V_f >= V_1)
con(wm, n_f, :recover_volume)[i] = c
"""
constraint_tank_volume_recovery(wm, i, n_1, n_f)

Adds a constraint that ensures the volume of a tank at the end of the time horizon is
greater than or equal to the volume of the tank at the beginning of the time horizon. Here,
`wm` is the WaterModels object, `n_1` is the index of the first subnetwork within a
multinetwork, `n_f` is the index of the final subnetwork, and i is the index of the tank.
"""
function constraint_tank_volume_recovery(wm::AbstractWaterModel, i::Int, n_1::Int, n_f::Int)
if !ref(wm, n_f, :tank, i)["dispatchable"]
_initialize_con_dict(wm, :tank_volume_recovery, nw=n_f)
V_1, V_f = var(wm, n_1, :V, i), var(wm, n_f, :V, i)
c = JuMP.@constraint(wm.model, V_f >= V_1)
con(wm, n_f, :tank_volume_recovery)[i] = c
end
end
Loading