Skip to content

Commit c4b7c90

Browse files
authored
Merge pull request #210 from LRossman/contributor-lr
Pressure Dependent Demands added to address issue 163
2 parents 761199b + 9306119 commit c4b7c90

25 files changed

+1518
-1022
lines changed

README.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,9 @@ The EPANET Library is a pressurized pipe network hydraulic and water quality ana
1313

1414
Please see the [`version 2.1` Release Notes](https://github.com/OpenWaterAnalytics/EPANET/blob/master/ReleaseNotes2_1.md) for information relevant to users of the previous official version (2.00.12). If you would like to contribute by addressing any of the outstanding [Issues](https://github.com/OpenWaterAnalytics/EPANET/issues), then please comment on the Issue, then Fork this repo to your own account and base your commits on the [`dev` branch](https://github.com/OpenWaterAnalytics/EPANET/tree/dev). Once you are finished, you can open a Pull Request to test the code and discuss merging your changes back into the community respository.
1515

16-
A step-by-step tutorial on how to contribute to EPANET using GitHub is also [available] (http://www.slideshare.net/demetriseliades/contributing-to-epanet-using-github-in-windows).
16+
A step-by-step tutorial on how to contribute to EPANET using GitHub is also [available](http://www.slideshare.net/demetriseliades/contributing-to-epanet-using-github-in-windows).
1717

1818
__Note:__ This repository is not affiliated with, or endorsed by, the USEPA. For the last "official" release of EPANET (2.00.12 UI and Toolkit) please go to the [EPA's GitHub repo](https://github.com/USEPA/Water-Distribution-Network-Model) or [the USEPA website](http://www2.epa.gov/water-research/epanet). It is also not the graphical user interface version. This is the hydraulic and water quality solver engine.
1919

2020
However, if you are interested in extending EPANET for academic, personal, or commercial use, then you've come to the right place. For community discussion, FAQ, and roadmapping of the project, go to the [Community Forum](http://community.wateranalytics.org/category/epanet).
21+

ReleaseNotes2_2.md

+96
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
2+
3+
4+
Release Notes for EPANET 2.2 (Draft)
5+
============================
6+
7+
This document describes the changes and updates that have been made to version 2.2 of EPANET.
8+
9+
## Thread-Safe API Functions
10+
11+
A duplicate set of the version 2.1 API functions has been provided that allow multiple EPANET projects to be analyzed concurrently in a thread-safe manner. These functions maintain the same name as the original but use a `EN_` prefix instead of `EN`. In addition, the first argument to each of these functions is a pointer to an `EN_Project` structure that encapsulates the network data for the particular project being analyzed. For example, instead of writing:
12+
13+
`ENgetnodevalue(nodeIndex, EN_ELEVATION, &elev)`
14+
15+
one would use:
16+
17+
`EN_getnodevalue(pr, nodeIndex, EN_ELEVATION, &elev)`
18+
19+
where `pr` is the pointer to an `EN_Project`.
20+
21+
Two new functions have been added to the API to manage the creation and deletion of project pointers. `EN_createproject` creates a new project along with a pointer to it, while `EN_deleteproject` deletes a project. An example of using the thread-safe version of the API is shown below:
22+
```
23+
#include "epanet2.h"
24+
int runEpanet(char *finp, char *frpt)
25+
{
26+
EN_Project *pr = NULL;
27+
int err;
28+
err = EN_createproject(&pr);
29+
if (err) return err;
30+
err = EN_open(pr, finp, frpt, "");
31+
if (!err) err = EN_solveH(pr);
32+
if (!err) err = EN_report(pr);
33+
EN_close(pr);
34+
EN_deleteproject(pr);
35+
return err;
36+
}
37+
```
38+
39+
## Additional Convergence Parameters
40+
41+
Two new analysis options have been added to provide more rigorous convergence criteria for EPANET's hydraulic solver. In the API they are named `EN_HEADERROR` and `EN_FLOWCHANGE` while in the `[OPTIONS]` section of an EPANET input file they are named `HEADERROR` and `FLOWCHANGE`, respectively.
42+
43+
`EN_HEADERROR` is the maximum head loss error that any network link can have for hydraulic convergence to occur. A link's head loss error is the difference between the head loss found as a function of computed flow in the link (such as by the Hazen-Williams equation for a pipe) and the difference in computed heads for the link's end nodes. The units of this parameter are feet (or meters for SI units). The default value of 0 indicates that no head error limit applies.
44+
45+
`EN_FLOWCHANGE` is the largest change in flow that any network element (link, emitter, or pressure-dependent demand) can have for hydraulic convergence to occur. It is specified in whatever flow units the project is using. The default value of 0 indicates that no flow change limit applies.
46+
47+
These new parameters augment the current `EN_ACCURACY` option which always remains in effect. In addition, both `EN_HEADERROR` and `EN_FLOWCHANGE` can be used as parameters in the `ENgetstatistic` (or `EN_getstatistic`) function to retrieve their computed values (even when their option values are 0) after a hydraulic solution has been completed.
48+
49+
## Improved Linear Solver Routine
50+
EPANET's hydraulic solver requires solving a system of linear equations over a series of iterations until a set of convergence criteria are met. The coefficient matrix of this linear system is square and symmetric. It has a row for each network node and a non-zero off-diagonal coefficient for each link. The numerical effort needed to solve the linear system can be reduced if the nodes are re-ordered so that the non-zero coefficients cluster more tightly around the diagonal.
51+
52+
EPANET's original node re-ordering scheme has been replaced by the more powerful **Multiple Minimum Degree (MMD)** algorithm. On a series of eight networks ranging in size from 7,700 to 100,000 nodes **MMD** reduced the solution time for a single period (steady state) hydraulic analysis by an average of more than 50%.
53+
54+
## Pressure Dependent Demands
55+
EPANET has always employed a Demand Driven Analysis (**DDA**) when modeling network hydraulics. Under this approach nodal demands at a given point in time are fixed values that must be delivered no matter what nodal heads and link flows are produced by a hydraulic solution. This can result in situations where required demands are satisfied at nodes that have negative pressures - a physical impossibility.
56+
57+
To address this issue EPANET has been extended to use a Pressure Driven Analysis (**PDA**) if so desired. Under **PDA**, the demand *D* delivered at a node depends on the node's available pressure *P* according to:
58+
$$D =D_f\left(\frac{P-P_{min}}{P_{req}-P_{min}}\right)^{P_{exp}} for P_{0}<=P<=P_{req}$$where *D<sub>f</sub>* is the full demand required, *P<sub>min</sub>* is the pressure below which demand is zero, *P<sub>req</sub>* is the pressure required to deliver the full required demand and *P<sub>exp</sub>* is an exponent. When *P < P<sub>min</sub>* demand is 0 and when *P > P<sub>req</sub>* demand equals *D<sub>f</sub>*.
59+
60+
To implement pressure dependent analysis four new parameters have been added to the [OPTIONS] section of the EPANET input file:
61+
62+
63+
| Parameter | Description | Default |
64+
|--|--|--|
65+
| DEMAND MODEL | either DDA or PDA | DDA |
66+
| MINIMUM PRESSURE | value for *P<sub>min</sub>* | 0
67+
| REQUIRED PRESSURE | value for *P<sub>req</sub>* | 0
68+
| PRESSURE EXPONENT | value for *P<sub>exp</sub>* | 0.5 |
69+
70+
These parameters can also be set and retrieved in code using the following API functions
71+
```
72+
int ENsetdemandmodel(int modelType, double pMin, double pReq, double pExp);
73+
int ENgetdemandmodel(int *modelType, double *pMin, double *pReq, double *pExp);
74+
```
75+
for the legacy API and
76+
```
77+
int EN_setdemandmodel(EN_Project *pr, int modelType, double pMin, double pReq, double pExp);
78+
int EN_getdemandmodel(EN_Project *pr, int *modelType, double *pMin, double *pReq, double *pExp);
79+
```
80+
for the thread-safe API. Some additional points regarding the new **PDA** option are:
81+
82+
- If no DEMAND MODEL and its parameters are specified then the analysis defaults to being demand driven (**DDA**).
83+
- This implementation of **PDA** assumes that the same parameters apply to all nodes in the network. Extending the framework to allow different parameters for specific nodes is straightforward to do but is left as a future feature to implement.
84+
- *P<sub>0</sub>* is allowed to equal to *P<sub>req</sub>*. This condition can be used to find a solution that results in the smallest amount of demand reductions needed to insure that no node delivers positive demand at a pressure below *P<sub>min</min>*.
85+
86+
## Code Changes
87+
88+
- The header file `vars.h` containing global variables has been eliminated. Instead a number of new structures incorporating these variables has been added to `types.h`. These structures have been incorporated into the new `EN_Project` structure, also defined in `types.h`, which gets passed into each of the thread-safe API functions as a pointer.
89+
- Each of the legacy API functions now simply calls its thread-safe counterpart passing in a pointer to a default global`EN_Project` variable that is declared in `types.h`.
90+
- Throughout all code modules, global variables that were previously accessed through `vars.h` are now accessed using the `EN_Project` pointer that is passed into the functions where the variables appear.
91+
- The exceedingly long `hydraul.c` file has been split into four separate files:
92+
- `hydraul.c` now contains just the code needed to initialize a hydraulic analysis, set demands and control actions at each time step, and determine the length of the next time step to take.
93+
- `hydsolver.c` implements EPANET's hydraulic solver at a single point in time.
94+
- `hydcoeffs.c` computes values of the matrix coefficients (derived from link head losses and their gradients) used by the hydraulic solver.
95+
- `hydstatus.c` checks for status changes in valves and pumps as requested by the hydraulic solver.
96+
- The Multiple Minimum Degree re-ordering algorithm appears in a new file named `genmmd.c`. This is 1990's legacy code that is readily available on the web and can be found in several linear equation solver libraries.

include/epanet2.bas

+8
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,8 @@ Public Const EN_NEXTEVENT = 14
7878

7979
Public Const EN_ITERATIONS = 0
8080
Public Const EN_RELATIVEERROR = 1
81+
Public Const EN_MAXHEADERROR = 2
82+
Public Const EN_MAXFLOWCHANGE = 3
8183

8284
Public Const EN_NODECOUNT = 0 'Component counts
8385
Public Const EN_TANKCOUNT = 1
@@ -126,6 +128,9 @@ Public Const EN_MLD = 7
126128
Public Const EN_CMH = 8
127129
Public Const EN_CMD = 9
128130

131+
Public Const EN_DDA = 0 ' Demand driven analysis
132+
Public Const EN_PDA = 1 ' Pressure driven analysis
133+
129134
Public Const EN_TRIALS = 0 ' Misc. options
130135
Public Const EN_ACCURACY = 1
131136
Public Const EN_TOLERANCE = 2
@@ -233,6 +238,9 @@ Public Const EN_G_CURVE = 4 ' General\default curve
233238

234239
Declare Function ENgetversion Lib "epanet2.dll" (value As Long) As Long
235240

241+
Declare Function ENgetdemandmodel Lib "epanet2.dll" (type as long, pmin as Single, preq as Single, pexp as Single) As Long
242+
Declare Function ENsetdemandmodel Lib "epanet2.dll" (ByVal type as long, ByVal pmin as Single, ByVal preq as Single, ByVal pexp as Single) As Long
243+
236244
Declare Function ENsetflowunits Lib "epanet2.dll" (ByVal code As Long) As Long
237245
Declare Function ENsetcontrol Lib "epanet2.dll" (ByVal Cindex As Long, ByVal Ctype As Long, ByVal Lindex As Long, ByVal setting As Single, ByVal Nindex As Long, ByVal Level As Single) As Long
238246
Declare Function ENsetnodevalue Lib "epanet2.dll" (ByVal index As Long, ByVal code As Long, ByVal value As Single) As Long

include/epanet2.h

+39-16
Original file line numberDiff line numberDiff line change
@@ -141,10 +141,11 @@ typedef enum {
141141
EN_NEXTEVENTIDX = 15
142142
} EN_TimeProperty;
143143

144-
145144
typedef enum {
146145
EN_ITERATIONS = 0,
147-
EN_RELATIVEERROR = 1
146+
EN_RELATIVEERROR = 1,
147+
EN_MAXHEADERROR = 2,
148+
EN_MAXFLOWCHANGE = 3
148149
} EN_AnalysisStatistic;
149150

150151
typedef enum {
@@ -153,7 +154,7 @@ typedef enum {
153154
EN_LINKCOUNT = 2, /**< Number of Links (Pipes + Pumps + Valves) */
154155
EN_PATCOUNT = 3, /**< Number of Time Patterns */
155156
EN_CURVECOUNT = 4, /**< Number of Curves */
156-
EN_CONTROLCOUNT = 5, /**< Number of Control Statements */
157+
EN_CONTROLCOUNT = 5, /**< Number of Control Statements */
157158
EN_RULECOUNT = 6 /**< Number of Rule-based Control Statements */
158159
} EN_CountType;
159160

@@ -208,6 +209,10 @@ typedef enum {
208209
EN_CMD = 9
209210
} EN_FlowUnits;
210211

212+
typedef enum { /* Demand model types. */
213+
EN_DDA = 0, /**< Demand driven analysis */
214+
EN_PDA = 1 /**< Pressure driven analysis */
215+
} EN_DemandModel;
211216

212217
/// Simulation Option codes
213218
typedef enum {
@@ -272,8 +277,6 @@ extern "C" {
272277
@brief The EPANET Project wrapper object
273278
*/
274279
typedef struct EN_Project EN_Project;
275-
typedef struct EN_Pattern EN_Pattern;
276-
typedef struct EN_Curve EN_Curve;
277280

278281
/**
279282
@brief runs a complete EPANET simulation
@@ -524,7 +527,29 @@ extern "C" {
524527
@return Error code
525528
*/
526529
int DLLEXPORT ENsetflowunits(int code);
527-
530+
531+
/**
532+
@brief Retrieves the type of demand model in use and its parameters
533+
@param[out] type Type of demand model (EN_DDA or EN_PDA)
534+
@param[out] pmin Pressure below which there is no demand
535+
@param[out] preq Pressure required to deliver full demand
536+
@param[out] pexp Pressure exponent in demand function
537+
@return Error code
538+
*/
539+
int DLLEXPORT ENgetdemandmodel(int *type, EN_API_FLOAT_TYPE *pmin,
540+
EN_API_FLOAT_TYPE *preq, EN_API_FLOAT_TYPE *pexp);
541+
542+
/**
543+
@brief Sets the type of demand model to use and its parameters
544+
@param type Type of demand model (EN_DDA or EN_PDA)
545+
@param pmin Pressure below which there is no demand
546+
@param preq Pressure required to deliver full demand
547+
@param pexp Pressure exponent in demand function
548+
@return Error code
549+
*/
550+
int DLLEXPORT ENsetdemandmodel(int type, EN_API_FLOAT_TYPE pmin,
551+
EN_API_FLOAT_TYPE preq, EN_API_FLOAT_TYPE pexp);
552+
528553
/**
529554
@brief Retrieves the index of the time pattern with specified ID
530555
@param id String ID of the time pattern
@@ -806,7 +831,7 @@ extern "C" {
806831
int DLLEXPORT ENsetnodevalue(int index, int code, EN_API_FLOAT_TYPE v);
807832

808833
/**
809-
@brief Set a proprty value for a link.
834+
@brief Set a property value for a link.
810835
@param index The index of a link. First link is index 1.
811836
@param code The code for the property to set.
812837
@param v The value to set for this link and property.
@@ -1131,15 +1156,14 @@ extern "C" {
11311156
int DLLEXPORT ENdeletelink(int linkIndex);
11321157

11331158

1134-
1135-
11361159
/***************************************************
11371160
11381161
Threadsafe versions of all epanet functions
11391162
11401163
***************************************************/
1141-
int DLLEXPORT EN_alloc(EN_Project **p);
1142-
int DLLEXPORT EN_free(EN_Project *p);
1164+
int DLLEXPORT EN_createproject(EN_Project **p);
1165+
int DLLEXPORT EN_deleteproject(EN_Project *p);
1166+
11431167
int DLLEXPORT EN_epanet(char *inpFile, char *rptFile, char *binOutFile, void (*callback) (char *));
11441168
int DLLEXPORT EN_init(EN_Project *p, char *rptFile, char *binOutFile, EN_FlowUnits UnitsType, EN_FormType HeadlossFormula);
11451169
int DLLEXPORT EN_open(EN_Project *p, char *inpFile, char *rptFile, char *binOutFile);
@@ -1171,6 +1195,10 @@ extern "C" {
11711195
int DLLEXPORT EN_gettimeparam(EN_Project *p, int code, long *value);
11721196
int DLLEXPORT EN_getflowunits(EN_Project *p, int *code);
11731197
int DLLEXPORT EN_setflowunits(EN_Project *p, int code);
1198+
int DLLEXPORT EN_getdemandmodel(EN_Project *p, int *type, EN_API_FLOAT_TYPE *pmin, EN_API_FLOAT_TYPE *preq,
1199+
EN_API_FLOAT_TYPE *pexp);
1200+
int DLLEXPORT EN_setdemandmodel(EN_Project *p, int type, EN_API_FLOAT_TYPE pmin, EN_API_FLOAT_TYPE preq,
1201+
EN_API_FLOAT_TYPE pexp);
11741202
int DLLEXPORT EN_getpatternindex(EN_Project *p, char *id, int *index);
11751203
int DLLEXPORT EN_getpatternid(EN_Project *p, int index, char *id);
11761204
int DLLEXPORT EN_getpatternlen(EN_Project *p, int index, int *len);
@@ -1236,11 +1264,6 @@ extern "C" {
12361264
int DLLEXPORT EN_deletenode(EN_Project *p, int nodeIndex);
12371265
int DLLEXPORT EN_deletelink(EN_Project *p, int linkIndex);
12381266

1239-
1240-
1241-
1242-
1243-
12441267
#if defined(__cplusplus)
12451268
}
12461269
#endif

src/enumstxt.h

+4
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,10 @@ char *PressUnitsTxt[] = {w_PSI,
7474
w_KPA,
7575
w_METERS};
7676

77+
char *DemandModelTxt[] = { w_DDA,
78+
w_PDA,
79+
NULL };
80+
7781
char *QualTxt[] = {w_NONE,
7882
w_CHEM,
7983
w_AGE,

0 commit comments

Comments
 (0)