Skip to content

Commit

Permalink
Merge pull request #118 from lanl-ansi/v0.5-updates
Browse files Browse the repository at this point in the history
UPD: Various refactors and updates for v0.5.0
  • Loading branch information
tasseff authored Nov 5, 2020
2 parents 8f03157 + 04eefc9 commit 91fa0b2
Show file tree
Hide file tree
Showing 61 changed files with 1,991 additions and 1,086 deletions.
8 changes: 4 additions & 4 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
WaterModels.jl Change Log
=========================

### v0.6.0
### v0.5.0
- Rename formulation types.
- 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.
- Refactored bound-tightening utility.
- Rename `junction` to `demand`.

### v0.4.0
- Removed unnecessary dependencies.
Expand Down
3 changes: 2 additions & 1 deletion Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ julia = "^1"
Cbc = "9961bab8-2fa3-5c5a-9d89-47fab24efd76"
Ipopt = "b6b21f68-93f8-5de0-b562-5493be1d77c9"
Juniper = "2ddba703-00a4-53a7-87a5-e8b9971dde84"
Logging = "56ddb016-857b-54e1-b83d-db4d58db5568"
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"

[targets]
test = ["Test", "Cbc", "Ipopt", "Juniper"]
test = ["Cbc", "Ipopt", "Juniper", "Logging", "Test"]
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ The primary developer is [Byron Tasseff](https://github.com/tasseff) with suppor
- [Russell Bent](https://github.com/rb004f), Los Alamos National Laboratory
- [Carleton Coffrin](https://github.com/ccoffrin), Los Alamos National Laboratory
- Donatella Pasqualini, Los Alamos National Laboratory
- [Devon Sigler](https://github.com/dsigler1234), National Renewable Energy Laboratory
- [Jonathan Stickel](https://github.com/jjstickel), National Renewable Energy Laboratory

## License
Expand Down
4 changes: 2 additions & 2 deletions docs/src/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,9 @@ result["termination_status"]
# The flow along pipe 4, in cubic meters per second.
result["solution"]["pipe"]["4"]["q"]

# The total hydraulic head at junction (node) 2, in meters.
# The total hydraulic head at node 2, in meters.
result["solution"]["node"]["2"]["h"]

# The pressure head at junction (node) 2, in meters.
# The pressure head at node 2, in meters.
result["solution"]["node"]["2"]["p"]
```
96 changes: 10 additions & 86 deletions docs/src/math-model.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,121 +9,45 @@ In summary, the following sets are commonly used when defining a WaterModels pro
| :-------------------------------------- | :----------------------------- | :------------------------- |
| $\mathcal{N}$ | `wm.ref[:nw][n][:node]` | nodes (to which nodal-type components are attached) |
| $\mathcal{K}$ | `nw_ids(wm)` | time indices (multinetwork indices labeled by `n`) |
| $\mathcal{J}$ | `wm.ref[:nw][n][:junction]` | [junctions](http://wateranalytics.org/EPANET/_juncs_page.html) |
| $\mathcal{D}$ | `wm.ref[:nw][n][:demand]` | [demands](http://wateranalytics.org/EPANET/_juncs_page.html) |
| $\mathcal{R}$ | `wm.ref[:nw][n][:reservoir]` | [reservoirs](http://wateranalytics.org/EPANET/_resv_page.html) |
| $\mathcal{T}$ | `wm.ref[:nw][n][:tank]` | [tanks](http://wateranalytics.org/EPANET/_tanks_page.html) |
| $\mathcal{A} \subset \mathcal{L}$ | `wm.ref[:nw][n][:pipe]` | [pipes](http://wateranalytics.org/EPANET/_pipes_page.html) |
| $\mathcal{C} \subset \mathcal{L}$ | `wm.ref[:nw][n][:check_valve]` | [pipes with check valves](http://wateranalytics.org/EPANET/_pipes_page.html) |
| $\mathcal{P} \subset \mathcal{L}$ | `wm.ref[:nw][n][:pump]` | [pumps](http://wateranalytics.org/EPANET/_pumps_page.html) |
| $\mathcal{W} \subset \mathcal{L}$ | `wm.ref[:nw][n][:regulator]` | [regulators](http://wateranalytics.org/EPANET/_valves_page.html) |
| $\mathcal{S} \subset \mathcal{L}$ | `wm.ref[:nw][n][:short_pipe]` | [short pipes](http://wateranalytics.org/EPANET/_pipes_page.html) |
| $\mathcal{V} \subset \mathcal{L}$ | `wm.ref[:nw][n][:valve]` | [valves](http://wateranalytics.org/EPANET/_pipes_page.html) |

## Physical Feasibility
### Nodes

#### Junctions
#### Demands

#### Reservoirs

#### Tanks

### Links

#### Pipes Without Check Valves
#### Pipes

#### Pipes with Check Valves
#### Pumps

#### Pipes with Shutoff Valves
#### Regulators

#### Pressure Reducing Valves
#### Short Pipes

#### Pumps
#### Valves

### Satisfaction of Flow Bounds
For each arc $(i, j) \in \mathcal{L}$, a variable $q_{ij}$ is used to represent the volumetric flow of water across the arc (in $\textrm{m}^{3}/\textrm{s}$).
When $q_{ij}$ is positive, flow on arc $(i, j)$ travels from node $i$ to node $j$.
When $q_{ij}$ is negative, flow travels from node $j$ to node $i$.
The absolute value of flow along the arc can be bounded by physical capacity, engineering judgment, or network analysis.
Having tight bounds is crucial for optimization applications.
For example, maximum flow speed and the diameter of the pipe can be used to bound $q_{ij}$ as per
```math
-\frac{\pi}{4} v_{ij}^{\max} D_{ij}^{2} \leq q_{ij} \leq \frac{\pi}{4} v_{ij}^{\max} D_{ij}^{2},
```
where $D_{ij}$ is the diameter of pipe $(i, j)$ and $v^{\max}_{ij}$ is the maximum flow speed along the pipe.

### Satisfaction of Head Bounds
Each node potential is denoted by $h_{i}$, $i \in \mathcal{N}$, and represents the hydraulic head in units of length ($\textrm{m}$).
The hydraulic head assimilates the elevation and pressure heads at each node, while the velocity head can typically be neglected.
For each reservoir $i \in \mathcal{R}$, the hydraulic head is assumed to be fixed at a value $h_{i}^{\textrm{src}}$, i.e.,
```math
h_{i} = h_{i}^{\textrm{src}}, \; \forall i \in \mathcal{R}.
```
For each junction $i \in \mathcal{J}$, a minimum hydraulic head $\underline{h}_{i}$, determined a priori, must first be satisfied.
In the interest of tightening the optimization formulation, upper bounds on hydraulic heads can also typically be implied from other network data, e.g.,
```math
\underline{h}_{i} \leq h_{i} \leq \overline{h}_{i} = \max_{i \in \mathcal{R}}\{h_{i}^{\textrm{src}}\}.
```

### Conservation of Flow

### Head Loss Relationships
In water distribution networks, flow along a pipe is induced by the difference in potential (head) between the two nodes that connect that pipe.
The relationships that link flow and hydraulic head are commonly referred to as the "head loss equations" or "potential-flow constraints," and are generally of the form
```math
h_{i} - h_{j} = \Phi_{ij}(q_{ij}),
```
where $\Phi_{ij} : \mathbb{R} \to \mathbb{R}$ is a strictly increasing function with rotational symmetry about the origin.
Embedding the above equation in a mathematical program clearly introduces nonconvexity.
(That is, the function $\Phi_{ij}(q_{ij})$ is nonconvex _and_ the relationship must be satisfied with equality.)
As such, different formulations primarily aim to effectively deal with these types of nonconvex constraints in an optimization setting.

Explicit forms of the head loss equation include the [Darcy-Weisbach](https://en.wikipedia.org/wiki/Darcy-Weisbach_equation) equation, i.e.,
```math
h_{i} - h_{j} = \frac{8 L_{ij} \lambda_{ij} q_{ij} \lvert q_{ij} \rvert}{\pi^{2} g D_{ij}^{5}}
```
and the [Hazen-Williams](https://en.wikipedia.org/wiki/Hazen-Williams_equation) equation, i.e.,
```math
h_{i} - h_{j} = \frac{10.67 L_{ij} q_{ij} \lvert q_{ij} \rvert^{0.852}}{\kappa_{ij}^{1.852} D_{ij}^{4.87}}.
```
In these equations, $L_{ij}$ represents the length of pipe $(i, j) \in \mathcal{A}$, $\lambda_{ij}$ represents the friction factor, $g$ is the acceleration due to gravity, and $\kappa_{ij}$ is the roughness coefficient, which depends on the material of the pipe.
In the Darcy-Weisbach formulation, $\lambda_{ij}$ depends on the Reynolds number (and thus the flow $q_{ij}$) in a nonlinear manner.
In WaterModels.jl, the [Swamee-Jain equation](https://en.wikipedia.org/wiki/Darcy_friction_factor_formulae#Swamee%E2%80%93Jain_equation) is used, which serves as an explicit approximation of the implicit [Colebrook-White](https://en.wikipedia.org/wiki/Darcy_friction_factor_formulae#Colebrook%E2%80%93White_equation) equation.
The equation computes the friction factor $\lambda_{ij}$ for $(i, j) \in \mathcal{A}$ as
```math
\lambda_{ij} = \frac{0.25}{\left[\log \left(\frac{\epsilon_{ij} / D_{ij}}{3.7} + \frac{5.74}{\textrm{Re}_{ij}^{0.9}}\right)\right]^{2}}.
```
where $\epsilon_{ij}$ is the pipe's effective roughness and the Reynold's number $\textrm{Re}_{ij}$ is defined as
```math
\textrm{Re}_{ij} = \frac{D_{ij} v_{ij} \rho}{\mu},
```
where $v_{ij}$ is the mean flow speed, $\rho$ is the density, and $\mu$ is the viscosity.
Herein, to remove the source of nonlinearity in the Swamee-Jain equation, $v_{ij}$ is estimated a priori, making the overall resistance term fixed.

When all variables in a head loss equation _except_ $q_{ij}$ are fixed (as in the relations described above), both the Darcy-Weisbach and Hazen-Williams formulations for head loss reduce to a convenient form, namely
```math
h_{i} - h_{j} = L_{ij} r_{ij} q_{ij} \lvert q_{ij} \rvert^{\alpha}.
```
Here, $r_{ij}$ represents the resistance per unit length, and $\alpha$ is the exponent required by the head loss relationship (i.e., one for Darcy-Weisbach and $0.852$ for Hazen-Williams).
Thus, the Darcy-Weisbach resistance per unit length is
```math
r_{ij} = \frac{8 \lambda_{ij}}{\pi^{2} g D_{ij}^{5}},
```
and the Hazen-Williams resistance per unit length is
```math
r_{ij} = \frac{10.67}{\kappa_{ij}^{1.852} D_{ij}^{4.87}}.
```

## Nonconvex Nonlinear Program
The full nonconvex formulation of the physical feasibility problem (NC), which incorporates all requirements from [Physical Feasibility](#Physical-Feasibility-1), may be written as a system that satisfies the following constraints:
```math
\begin{align}
h_{i} - h_{j} &= L_{ij} r_{ij} q_{ij} \lvert q_{ij} \rvert^{\alpha}, ~ \forall (i, j) \in \mathcal{A} \label{eqn:ncnlp-head-loss} \\
h_{i} &= h_{i}^{\textrm{src}}, ~ \forall i \in \mathcal{S} \label{eqn:ncnlp-head-source} \\
\sum_{(j, i) \in \mathcal{A}^{-}(i)} q_{ji} - \sum_{(i, j) \in \mathcal{A}^{+}(i)} q_{ij} &= q_{i}^{\textrm{dem}}, ~ \forall i \in \mathcal{J} \label{eqn:ncnlp-flow-conservation} \\
\underline{h}_{i} \leq h_{i} &\leq \overline{h}_{i}, ~ \forall i \in \mathcal{J} \label{eqn:ncnlp-head-bounds} \\
\underline{q}_{ij} \leq q_{ij} &\leq \overline{q}_{ij}, ~ \forall (i, j) \in \mathcal{A} \label{eqn:ncnlp-flow-bounds}.
\end{align}
```
Here, Constraints $\eqref{eqn:ncnlp-head-loss}$ are [head loss relationships](#Head-Loss-Relationships-1), Constraints $\eqref{eqn:ncnlp-head-source}$ are [head bounds](#Satisfaction-of-Head-Bounds-1) at source nodes, Constraints $\eqref{eqn:ncnlp-flow-conservation}$ are [flow conservation constraints](#Conservation-of-Flow), Constraints $\eqref{eqn:ncnlp-head-bounds}$ [head bounds](#Satisfaction-of-Head-Bounds-1) at junctions, and Constraints $\eqref{eqn:ncnlp-flow-bounds}$ are [flow bounds](#Satisfaction-of-Flow-Bounds-1).
Note that the sources of nonconvexity and nonlinearity are Constraints $\eqref{eqn:ncnlp-head-loss}$.

## Mixed-integer Convex Program

Expand Down
8 changes: 4 additions & 4 deletions docs/src/quickguide.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ Finally, test that the package works as expected by executing

## Solving a Network Design Problem
Once the above dependencies have been installed, obtain the files [`shamir.inp`](https://raw.githubusercontent.com/lanl-ansi/WaterModels.jl/master/examples/data/epanet/shamir.inp) and [`shamir.json`](https://raw.githubusercontent.com/lanl-ansi/WaterModels.jl/master/examples/data/json/shamir.json).
Here, `shamir.inp` is an EPANET file describing a simple seven-node, eight-link water distribution network with one reservoir, six junctions, and eight pipes.
Here, `shamir.inp` is an EPANET file describing a simple seven-node, eight-link water distribution network with one reservoir, six demands, and eight pipes.
In accord, `shamir.json` is a JSON file specifying possible pipe diameters and associated costs per unit length, per diameter setting.
The combination of data from these two files provides the required information to set up a corresponding network design problem, where the goal is to select the most cost-efficient pipe diameters while satisfying all demand in the network.

Expand Down Expand Up @@ -101,9 +101,9 @@ The following example demonstrates one way to perform multiple WaterModels solve
```julia
run_des(data, LRDWaterModel, Cbc.Optimizer, ext=Dict(:pipe_breakpoints=>5))

data["junction"]["3"]["demand"] *= 2.0
data["junction"]["4"]["demand"] *= 2.0
data["junction"]["5"]["demand"] *= 2.0
data["demand"]["3"]["flow_rate"] *= 2.0
data["demand"]["4"]["flow_rate"] *= 2.0
data["demand"]["5"]["flow_rate"] *= 2.0

run_des(data, LRDWaterModel, Cbc.Optimizer, ext=Dict(:pipe_breakpoints=>5))
```
Expand Down
25 changes: 17 additions & 8 deletions src/WaterModels.jl
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,9 @@ __init__() = Memento.register(_LOGGER)
"Suppresses information and warning messages output by WaterModels. For
more fine-grained control, use the Memento package."
function silence()
Memento.info(
_LOGGER,
"Suppressing information and warning messages for " *
"the rest of this session. Use the Memento package for more " *
"fine-grained control of logging.",
)
msg = "Suppressing information and warning messages for the rest of this session. " *
"Use the Memento package for more fine-grained control of logging."
Memento.info(_LOGGER, msg)
Memento.setlevel!(Memento.getlogger(InfrastructureModels), "error")
Memento.setlevel!(Memento.getlogger(WaterModels), "error")
end
Expand All @@ -45,6 +42,16 @@ include("io/common.jl")
include("io/epanet.jl")

include("core/base.jl")
include("core/constants.jl")

include("core/node.jl")
include("core/demand.jl")
include("core/reservoir.jl")
include("core/tank.jl")

include("core/pipe.jl")
include("core/pump.jl")

include("core/data.jl")
include("core/ref.jl")
include("core/types.jl")
Expand All @@ -68,9 +75,11 @@ include("prob/wf.jl")
include("prob/owf.jl")
include("prob/des.jl")

include("util/unbinarize.jl")
include("util/relax.jl")
include("util/variable_index.jl")
include("util/pairwise_cuts.jl")
include("util/pump_volume_cuts.jl")
include("util/obbt.jl")
include("util/compute_cuts.jl")

include("core/export.jl")
end
25 changes: 2 additions & 23 deletions src/core/base.jl
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,8 @@ function run_model(
if multinetwork != _IM.ismultinetwork(data)
model_requirement = multinetwork ? "multi-network" : "single-network"
data_type = _IM.ismultinetwork(data) ? "multi-network" : "single-network"

Memento.error(
_LOGGER,
"Attempted to build a $(model_requirement) model with $(data_type) data.",
)
message = "Attempted to build a $(model_requirement) model with $(data_type) data."
Memento.error(_LOGGER, message)
end

wm = instantiate_model(
Expand Down Expand Up @@ -112,24 +109,6 @@ function instantiate_model(
end


"""
Builds the ref dictionary from the data dictionary. Additionally the ref dictionary would
contain fields populated by the optional vector of ref_extensions provided as a keyword
argument.
"""
function build_ref(
data::Dict{String,<:Any};
ref_extensions::Vector{<:Function} = Vector{Function}([]),
)
return _IM.build_ref(
data,
ref_add_core!,
_wm_global_keys,
ref_extensions = ref_extensions,
)
end


"""
Returns a dict that stores commonly-used, precomputed data from of the data dictionary,
primarily for converting data types, filtering out deactivated components, and storing
Expand Down
11 changes: 11 additions & 0 deletions src/core/constants.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
"Enumerated type specifying the direction of flow along an edge."
@enum FLOW_DIRECTION POSITIVE=1 NEGATIVE=-1 UNKNOWN=0

"Defines a constant for determining whether flow along a pump is considered appreciable."
const _FLOW_MIN = 6.31465679e-6

"Defines the constant for gravitational acceleration in meters per second squared"
const _GRAVITY = 9.80665

"Defines the constant for the density of water in kilograms per cubic meter"
const _DENSITY = 1000.0
Loading

0 comments on commit 91fa0b2

Please sign in to comment.