Code Monkey home page Code Monkey logo

watermodels.jl's Introduction

WaterModels.jl

WaterModels Logo

Latest Documentation Status Development Build Status Code Coverage Status

WaterModels.jl is a Julia/JuMP package for steady state water network optimization. It is designed to enable computational evaluation of historical and emerging water network formulations and algorithms using a common platform. The code is engineered to decouple problem specifications (e.g., network design, optimal water flow) from network optimization formulations (e.g., mixed-integer linear, mixed-integer nonlinear). This decoupling enables the definition of a wide variety of optimization formulations and their comparison on common problem specifications.

Core Problem Specifications

  • Water Flow (wf and mn_wf) - obtain feasible flows and heads for a network
  • Optimal Water Flow (owf and mn_owf) - minimize the cost of network operation
  • Network Design (des) - minimize the cost of network design

Core Network Formulations

  • NC - nonconvex nonlinear formulation
  • NCD - nonconvex nonlinear direction-based formulation
  • CRD - continuous (convex) relaxation- and direction-based formulation
  • LRD - linear relaxation- and direction-based formulation
  • PWLRD - piecewise linear relaxation- and direction-based formulation
  • LA - linear approximation-based formulation

Documentation

The package documentation includes a quick start guide. Be advised that aside from the quick start guide, documentation is under development and may currently be inaccurate.

Development

Community-driven development and enhancement of WaterModels is welcomed and encouraged. Please feel free to fork this repository and share your contributions to the master branch with a pull request. That said, it is important to keep in mind the code quality requirements and scope of WaterModels before preparing a contribution. See CONTRIBUTING.md for code contribution guidelines.

Acknowledgments

This work is currently supported by the Advanced Grid Modeling Program within the U.S. Department of Energy under the project "Coordinated Planning and Operation of Water and Power Infrastructures for Increased Resilience and Reliability." Work at Los Alamos National Laboratory is conducted under the auspices of the National Nuclear Security Administration of the U.S. Department of Energy under Contract No. 89233218CNA000001. Previous work was supported by the Los Alamos National Laboratory Directed Research and Development program under the project "Adaptation Science for Complex Natural-engineered Systems" (20180033DR). It is also supported by the Advanced Network Science Initiative at Los Alamos National Laboratory.

The primary developer is Byron Tasseff with support from the following contributors:

License

This code is provided under a modified BSD license as part of the Multi-Infrastructure Control and Optimization Toolkit (MICOT), C15024.

watermodels.jl's People

Contributors

ccoffrin avatar claytonpbarrows avatar dsigler1234 avatar github-actions[bot] avatar hskkanth avatar juliatagbot avatar odow avatar rb004f avatar tasseff avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

watermodels.jl's Issues

Implement constraints for tanks

Refer to #25 and #26 for discussions on tank data structures and variables, respectively. Similar to junctions, tanks must satisfy flow conservation constraints at each time step. The starting value for the water surface elevation of a tank should be elevation + initlevel and should be constrained with equality to this value at time step n = 0. Otherwise, for n greater than zero, the temporal difference in water surface elevation should be a function of the incoming and outgoing flow, multiplied by the (constant) time step and cross-sectional wetted area of the tank. For cylindrical tanks with constant diameters, these constraints should be easy to derive. For tanks where volcurve is nonempty, this may require the addition of new variables and separate constraints to relate the change in water surface elevation to a change in tank volume. Refer to this paper for a detailed tank model that includes binary variables.

Additionally, we can likely assume that the head at the inlet of a tank is greater than or equal to the head at the outlet of a tank. Separate constraints also may need to be added to ensure minimum volume constraints are satisfied (especially when dealing with tanks with nonempty volcurve values).

Model Hierarchy

We have a software engineering challenge centered around having essentially three different modeling choices

  1. Mathematical Model, i.e. MINLP or MISCOP, etc.

  2. Direction, i.e. whether or not the flow directions are fixed or not

  3. Pipeflow physics, i.e. Hazen-Williams or Darcy-Weichbach

At the moment, the code is controlling these choices via if/then/else statements. I suggest we control these choices based on typing the the model. GasModels has already done this for the equivalents of 1) and 2) using types and union operators - see https://github.com/lanl-ansi/GasModels.jl

Thus, I suggest the following hierarchy

AbstractWaterFormulation - top level formulation object, all common constraints/variables/etc. are defined at this level, equivalent of AbstractGasFormulation in https://github.com/lanl-ansi/GasModels.jl/blob/master/src/core/base.jl

AbstractDirectedWaterFormulation <: AbstractWaterFormulation and AbstractUndirectedWaterFormulation <: AbstractWaterFormulation - defines formulation types for directed vs. undirected, This is like AbstractDirectedGasFormulation and AbstractUndirectedGasFormulation in https://github.com/lanl-ansi/GasModels.jl/blob/master/src/core/base.jl

AbstractDarcyWeisbachWaterFormulation <: AbstractWaterFormulation and AbstractHazenWilliamsWaterFormulation <: AbstractWaterFormulation - defines formulation types for Hazen Williams and Darcy Weisbach

Now, I suggest we use the union operator to combine the formulations into types

i.e. AbstractHazenWilliamsDirectedForm = Union{AbstractHazenWilliamsWaterFormulation, AbstractDirectedWaterFormulation}

This will then allow you defined the mathematical objects on the form of the model, i.e. for MINLPs, you will have things like

""
@compat abstract type AbstractMINLPHazenWilliamsDirectedForm <: AbstractHazenWilliamsDirectedWaterFormulation end

""
@compat abstract type StandardMINLPHazenWilliamsDirectedForm <: AbstractMINLPHazenWilliamsDirectedForm end

See https://github.com/lanl-ansi/GasModels.jl/blob/master/src/form/minlp.jl for an example.

If you run into situations where combinations of the modeling choices share common code, you can use the union operator to combine types.

See for example AbstractMINLPForms = Union{AbstractMINLPDirectedForm, AbstractMINLPForm} in GasModels. - this is used for MINLP features that don't care if the model is directed or undirected.

At the end of the day, the only thing that really differs across all three combinations of modeling choices is the pipeflow equations, and this will allow us to switch between them through the definition of the mathematical form.

Post Function Notes

  • the network id need not be specified explicitly for single network only models
  • conditionals of the form T <: Union{AbstractMICPForm, AbstractNCNLPForm} should be avoided in favor of function dispatch / overloading.

Faster Node ID Linking

The function _get_node_id_by_name has a worst-case quadratic runtime when called inside _add_link_ids! use of a Dict for lookup will be expected linear time.

Tidy up use of Enumerate

We should try to avoid things like, enumerate(ref(wm, n, :resistance, a)) in favor of things like ref(wm, n, :resistance).

Tidy REQUIRE

The REQUIRE should be broken into two files. The package root REQUIRE is for the bare minimum needed for the package. The test REQUIRE adds extra dependencies needed for running Pkg.test.

Retain letter casing when reading in data

Future versions of WaterModels will require the ability to export design and control decisions to an EPANET file. As such, the current input parsing methods that convert characters to lowercase should be replaced to ensure internal data (e.g., source_id) are instead consistently cased. Additionally, data fields that are usually present in EPANET are also converted to lowercase (e.g., [PIPES] becomes "pipes" and then eventually ref[:pipes]). Internally, lowercase labeling of component types seems to be the most readable option and I think should be retained. This may add additional complexity upon the construction of an output templater, but it is probably worth it.

Time Series Block

At the least this block needs an integer specifying how many items each array will have. It would also benefit from a time_elapsed parameter and any other metadata that might come out of the source data.

Implement variables for check valves

Refer to #29 for a discussion on check valve data structures. Obviously, the variables that already exist for pipe objects will remain for pipes with check valves. However, each check valve will require the addition of a binary variable, wm.var[:nw][n][:x_check][a], which defines if there is flow within pipe a (1) or not (0) at time n.

Implement check valve data structures

See the Open Water Analytics definition of check valves and EPANET's description of check valves in Section 3.1. Note that shutoff and check valves are not considered to be [VALVES] but are instead parts of [PIPES]. Pipe objects with status flags equal to cv should be considered check valves. A :check_valve key within ref should be constructed to easily filter links that are pipes with check valves. Since [PIPES] objects are already parsed in src/io/epanet.jl, implementing these data structures should be easy. Furthermore, when check valves are detected, the corresponding flow_direction attribute should be set appropriately (e.g., to POSITIVE).

Implement pump data structures

See the Open Water Analytics definition of pumps and EPANET's description of pumps on page 32. Pumps are links that increase head across the flow direction. Pumps in the initial implementation should contain the following data:

Name Type Units Description
id / source_id String N/A String identifier
index Int N/A Integer identifier
node1 String N/A source_id of start node
node2 String N/A source_id of end node
f_node Int N/A index of start node
t_node Int N/A index of end node
power Union{Float64, nothing} watts Power of a constant energy pump
head Union{Function, nothing} meters versus cubic meters per second Function describing head versus flow for the pump
speed Float64 unitless Relative pump speed setting
pattern Union{Array{n, 2}, nothing} seconds and unitless Time pattern describing how pump speed varies with time

Note that pumps are associated with unidirectional flow.

Create a template processor for the EPANET format

To ensure interoperability with EPANET (e.g., for further testing of design and control decisions), a template processor must be developed that converts solution data from the internal data model to a file that conforms to the EPANET format.

calc_friction_factor

I suggest breaking this out into into two functions one for hazen williams and one darcy weisbach. Then, these functions get called depending on the pipeflow equation that is added. In general, we don't store calculated values in the data unless it is an O(n) operation (or worse).... this prevents inevitable bugs where a calculated value gets out of sync with the data used to compute the calculated value. (see how we compute conductance and suscpetance in PowerModels and friction factors in GasModels)

Compute flow variables using JuMP expressions where appropriate

In many of the formulations, flow variables along an arc are decomposed into two separate directed flow variables (i.e., one in the positive direction and one in the negative direction). Furthermore, for network expansion models, flow variables are repeated per possible resistance value that can be selected. Formulating the problems in this way is mathematically elegant, but it discourages the easy retrieval of final solution data (e.g., flows along an arc). For these formulations, this can be remedied by introducing expressions that perform a reduction on the multiple flow variables (e.g., qp and qn) into a single expression q = qp - qn without having to introduce variables into the JuMP model. All formulations that use nonstandard physical variables should "roll up" into these placeholder JuMP expressions representing the original physical variables of interest, when possible.

Component Keys

These should be ints, not strings. If the source format enforces int names then those are used, otherwise the component ids should be from 1-to-n based on the order they appear in the file and the name field and source_id can be used to save the string id.

Implement variables for tanks

Refer to #25 for a discussion on tank data structures. Since tanks are most generally defined by volume versus height relationships, the water surface elevation within a tank of index i at time n should be defined by the variable wm.var[:nw][n][:h][i], which represents the water surface elevation (or hydraulic head) at time n. The lower bound on this variable should be elevation + minlevel, and the upper bound should be elevation + maxlevel. The starting value should be elevation + initlevel, and the variable should be constrained with equality to this value at time step n = 0. When volcurve is nonempty, the formulation may have to include additional variables and/or approximations that relate the change in height to a change in volume. Otherwise, for a cylindrical tank, the volume will be a simple function of the change in water surface elevation and the (constant) tank diameter. Refer to this paper for a detailed tank model that includes binary variables.

Mathematical forms

It looks like you want 3 mathematical forms

MINLP - the mixed integer non linear programming form
MICP - the mixed integer convex programming form (relaxation)
MILP - the mixed integer linear programing form (the piecewise linear relaxation of the MICP)

Then, in the implementations of the various variable creation functions, the necessary variables and constraints can be added. For example, in GasModels, there is a function variable_flow. If the mathematical form is undirected, the implementation of this function creates direction variables. If the mathematical form is directed, the implementation does not.

ie.

function variable_flow{T <: AbstractMIForms}(gm::GenericGasModel{T}, n::Int=gm.cnw; bounded::Bool = true)
variable_mass_flow(gm,n; bounded=bounded)
variable_connection_direction(gm,n)
end

vs.

function variable_flow{T <: AbstractMIDirectedForms}(gm::GenericGasModel{T}, n::Int=gm.cnw; bounded::Bool = true)
variable_mass_flow(gm,n; bounded=bounded)
end

Eventually we will want to include the NLP form that encodes the sign (abs) without resorting to direction variables, but that will be much later

Memento v0.8

I recommend that you bump the min version of Memento to 0.8, then you can remove this line,

setlevel!(LOGGER, "info")

which has a non-ideal hack.

SI units

It looks like the conversion to SI units is happening in base.jl. Rather than doing the conversion there, I suggest doing the conversion in the io packages, since that input units could vary depending on the source data. So, the epanet parser will do the conversion to SI units depending on how the epanet file defines it units, the foo parser will do the conversion to SI depending on how some arbitrary foo type file defines it, etc.

OWF Tests

We need some basic tests of this formulation.

non dimensionalization

The non-dimensionalization (per unit) conversion should live in data.jl (see how PowerModels.jl and GasModels.jl handles the automation of conversion between per unit and mixed units (PowerModels) or SI units (GasModels)

Dict Type Signautre

Type signatures of Dict{String, Any} should be updated to Dict{String, <:Any} to accommodate strong typed inputs.

Add tests for multinetwork data

Currently, there are no tests to ensure that multinetwork data are handled properly when reading inputs, building models, solving models, and storing solutions. Tests should be implemented to ensure proper coverage of this functionality.

For water flow (wf) problem formulations, networks should be replicated and combined into a single water flow problem. In this case, solutions obtained (e.g., flow and head values) per network replica should be identical. For network expansion (ne) problem formulations, similar replicas of the initial data should be made. In this case, solutions obtained (e.g., selected resistances) per network replica should also be identical, and the optimal objective should be a multiple of the number of replicas.

To assist in building this functionality, see how PowerModels.jl replicates network data and the corresponding multinetwork tests.

Windows CI

I saw the request to Install AppVeyor. Do you have a specific need for windows CI? If not, I would skip it. If so, is Travis and option? There is basic support for windows as far as I know.

Implement piecewise linear relaxation of head loss constraints

The current formulations either use the full nonconvex physics or outer-approximations of the head loss relationship. A piecewise linear relaxation of the head loss relationship should also be implemented to form mixed-integer linear programming (MILP) variants of the existing core problem specifications (i.e., water flow and network expansion). The MILP formulation, with a standard convex combination rewriting of the piecewise linear constraints, seems to be common throughout the literature and is thus important to include in WaterModels. Further note that binary variables representing the active portion of a head loss curve can be shared across shared flow variables along an arc, e.g., in network expansion formulations. (That is, only one binary variable per arc is needed if the same number of breakpoints are used per discrete head loss curve.)

Support non-integer component IDs

Currently, all component IDs are assumed to be integer upon the reading of network data. These IDs are then used identically throughout various model building and solution routines. However, many EPANET input files contain component IDs that are non-integer (i.e., they contain combinations of characters and numbers). For this reason, WaterModels.jl should support network encodings that assume string-like component IDs.

Internally, in the modeling routines and the ref structure, corresponding integer IDs should be used that map to each component. The method for assigning these integer IDs should return deterministic mappings when repeatedly called. When the solution to the model is obtained, solution data should be printed with respect to the original (string-like) component IDs.

This could be most easily accomplished through the addition of an integer index field to the network components when reading in data (e.g., in [src/io/epanet.jl]). A string id field could then be used to provide a mapping from the integer to the string. However, reading in data that modifies the network may be more tricky. In this case, the initial network (e.g., from an EPANET input file) should always be constructed first. Then, modifications can be applied on top of the existing network to ensure changes are always made to the correct components.

OrderedDicts

Discuss if OrderedDicts are required of if we can get by with Array and Dict.

Implement constraints for shutoff valves

Pipes with shutoff valves are bidirectional and must satisfy the same head loss relationships as other pipes. However, when the shutoff valve is closed, the head difference between connecting nodes must become decoupled. (That is, the head at one end of a pipe with a shutoff valve, when closed, should not depend on the head at the other end of the pipe.) To accomplish this, the head loss equation must be relaxed with big-M constraints that depend on the binary variable describing the state of the shutoff valve.

Interestingly, though, consider the mixed-integer convex relaxation (MICP) and linear outer-approximation (MILPR) formulations already present within WaterModels. In these formulations, the head loss relationships are already similarly relaxed (decoupled). However, the relaxations are upper-bounded by a linear constraint that depends on the flow upper bound. Thus, in these formulations, when a check valve is closed, the relaxation upper bounds should instead become nonbinding, i.e., the upper bounds should be replaced with some larger big-M or simply excluded. Right now, I'm not quite sure how such a big-M should be derived, but it should represent the absolute largest head difference that could exist between adjacent nodes.

Implement control valve data structures

See the Open Water Analytics definition of control valves and EPANET's description of control valves on page 176. Note that shutoff and check valves are not considered to be [VALVES] (i.e., control valves) but are instead parts of [PIPES]. Control valves in the initial implementation should include the following data:

Name Type Units Description
id / source_id String N/A String identifier
index Int N/A Integer identifier
node1 String N/A source_id of start node
node2 String N/A source_id of end node
f_node Int N/A index of start node
t_node Int N/A index of end node
diameter Float64 meters Diameter
type String N/A Valve type (see below)
setting Union{Float64, String} variable Valve setting (see below)
minorloss Float64 unitless Minor loss coefficient

Valve types and settings should be modeled as tuples of enumerated types and setting types, respectively. Valve types and settings include the following data:

Type Type Acronym Setting Setting Units
Pressure reducing valve PRV Pressure meters
Pressure sustaining valve PSV Pressure meters
Pressure breaker valve PBV Pressure meters
Flow control valve FCV Flow cubic meters per second
Throttle control valve TCV Loss coefficient unitless
General purpose valve GPV Head loss curve N/A ([CURVES] id value)

Implement variables for shutoff valves

Refer to #28 for a discussion on shutoff valve data structures. Obviously, the variables that already exist for pipe objects will remain for pipes with shutoff valves. However, each shutoff valve will require the addition of a binary variable, wm.var[:nw][n][:x_so][a], which defines if the shutoff valve within pipe a has been opened (1) or closed (0) by the operator at time n.

Pipe Node Names

I am wondering if we can omit the start/end_node_name node names from the pipes as these will be captured in explicit node objects.

Source Organization

Functions in data.jl should only depend on the data dict. Functions that require GenericWaterModel should be moved into a new file base/ref.jl.

Add Constraint Templates

These can help in isolating the key mathematical differences in the formulations by collecting all of the data extraction and processing into a common location.

fix epanet parser

the epanet parser isn't loaded by default when runninginclude("src/WaterModels_linux.jl"). Also, the parser appears to be nonfunctional for anything but a basic '.inp' file.

Implement tank data structures

See the Open Water Analytics definition of tanks and EPANET's description of tanks in Section 3.1. Tanks in the initial implementation should include the following data:

Name Type Units Description
id String N/A String identifier
index Int N/A Integer identifier
elevation Float64 meters Bottom elevation (where water level is zero)
initlevel Float64 meters Initial water level
minlevel Float64 meters Minimum water level
maxlevel Float64 meters Maximum water level
diameter Float64 meters Cylindrical diameter
minvol Float64 cubic meters Minimum volume
volcurve String N/A String identifier of the volume curve

First, note that the total water surface elevation is the bottom elevation (elevation) plus the water level. Also note that, for cylindrical tanks, computing the water level as a function of volume is simple. Otherwise, if a nonempty volcurve is defined, the appropriate [CURVES] data structure should be referenced, which defines a volume versus height relationship.

When reading in network data (e.g., in src/io/epanet.jl), it makes sense to have [TANKS] objects separated from [CURVES] objects. Moreover, an initial implementation should focus only on the functionality of cylindrical tanks. However, when a nonempty volcurve is defined, it is unclear what the data structure held in ref should look like. Should the associated curve be attached to the tank object, or should the two types remain decoupled?

Build Ref Updates

  • Add node-based arc/link lookup lists so that flow balance constraints are not quadratic
  • filter out inactive components (i.e. status values)
  • update to improved build_ref design, lanl-ansi/PowerModels.jl#505

Implement shutoff valve data structures

See the Open Water Analytics definition of shutoff valves and EPANET's description of shutoff valves in Section 3.1. Note that shutoff and check valves are not considered to be [VALVES] but are instead parts of [PIPES]. Although this does not appear to be documented, as far as I can tell, pipe objects with empty status flags should be considered shutoff valves. A :shutoff_valve key within ref should be constructed to easily filter links that are pipes with shutoff valves. Since [PIPES] objects are already parsed in src/io/epanet.jl, implementing these data structures should be easy.

Naming Conventions

PowerModels has standardized around the post-fix values _fr and _to for edge-like components. Following this we might change f_id/t_id to node_fr/node_to.

Implement constraints for check valves

Pipes with check valves are unidirectional and must satisfy the same head loss relationships as other pipes. Right now, I think this could be most cleanly handled by simply ensuring lower bounds of zero are placed on flow variables for pipes that contain check valves (assuming that flow will travel from the node1 to node2 indices specified in the original EPANET file). We should also ensure that flow_direction is properly set (to POSITIVE) upon reading the EPANET file.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.