epanet-plus 0.0.1__cp311-cp311-macosx_10_9_x86_64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of epanet-plus might be problematic. Click here for more details.
- docs/conf.py +67 -0
- epanet-msx-src/dispersion.h +27 -0
- epanet-msx-src/hash.c +107 -0
- epanet-msx-src/hash.h +28 -0
- epanet-msx-src/include/epanetmsx.h +104 -0
- epanet-msx-src/include/epanetmsx_export.h +42 -0
- epanet-msx-src/mathexpr.c +937 -0
- epanet-msx-src/mathexpr.h +39 -0
- epanet-msx-src/mempool.c +204 -0
- epanet-msx-src/mempool.h +24 -0
- epanet-msx-src/msxchem.c +1285 -0
- epanet-msx-src/msxcompiler.c +368 -0
- epanet-msx-src/msxdict.h +42 -0
- epanet-msx-src/msxdispersion.c +586 -0
- epanet-msx-src/msxerr.c +116 -0
- epanet-msx-src/msxfile.c +260 -0
- epanet-msx-src/msxfuncs.c +175 -0
- epanet-msx-src/msxfuncs.h +35 -0
- epanet-msx-src/msxinp.c +1504 -0
- epanet-msx-src/msxout.c +398 -0
- epanet-msx-src/msxproj.c +791 -0
- epanet-msx-src/msxqual.c +2011 -0
- epanet-msx-src/msxrpt.c +400 -0
- epanet-msx-src/msxtank.c +422 -0
- epanet-msx-src/msxtoolkit.c +1164 -0
- epanet-msx-src/msxtypes.h +551 -0
- epanet-msx-src/msxutils.c +524 -0
- epanet-msx-src/msxutils.h +56 -0
- epanet-msx-src/newton.c +158 -0
- epanet-msx-src/newton.h +34 -0
- epanet-msx-src/rk5.c +287 -0
- epanet-msx-src/rk5.h +39 -0
- epanet-msx-src/ros2.c +293 -0
- epanet-msx-src/ros2.h +35 -0
- epanet-msx-src/smatrix.c +816 -0
- epanet-msx-src/smatrix.h +29 -0
- epanet-src/AUTHORS +60 -0
- epanet-src/LICENSE +21 -0
- epanet-src/enumstxt.h +151 -0
- epanet-src/epanet.c +5937 -0
- epanet-src/epanet2.c +961 -0
- epanet-src/epanet2.def +131 -0
- epanet-src/errors.dat +79 -0
- epanet-src/flowbalance.c +186 -0
- epanet-src/funcs.h +219 -0
- epanet-src/genmmd.c +1000 -0
- epanet-src/hash.c +177 -0
- epanet-src/hash.h +28 -0
- epanet-src/hydcoeffs.c +1303 -0
- epanet-src/hydraul.c +1164 -0
- epanet-src/hydsolver.c +781 -0
- epanet-src/hydstatus.c +442 -0
- epanet-src/include/epanet2.h +466 -0
- epanet-src/include/epanet2_2.h +1962 -0
- epanet-src/include/epanet2_enums.h +518 -0
- epanet-src/inpfile.c +884 -0
- epanet-src/input1.c +672 -0
- epanet-src/input2.c +970 -0
- epanet-src/input3.c +2265 -0
- epanet-src/leakage.c +527 -0
- epanet-src/mempool.c +146 -0
- epanet-src/mempool.h +24 -0
- epanet-src/output.c +853 -0
- epanet-src/project.c +1691 -0
- epanet-src/quality.c +695 -0
- epanet-src/qualreact.c +800 -0
- epanet-src/qualroute.c +696 -0
- epanet-src/report.c +1559 -0
- epanet-src/rules.c +1500 -0
- epanet-src/smatrix.c +871 -0
- epanet-src/text.h +508 -0
- epanet-src/types.h +928 -0
- epanet-src/util/cstr_helper.c +59 -0
- epanet-src/util/cstr_helper.h +38 -0
- epanet-src/util/errormanager.c +92 -0
- epanet-src/util/errormanager.h +39 -0
- epanet-src/util/filemanager.c +212 -0
- epanet-src/util/filemanager.h +81 -0
- epanet-src/validate.c +408 -0
- epanet.cpython-311-darwin.so +0 -0
- epanet_plus/VERSION +1 -0
- epanet_plus/__init__.py +8 -0
- epanet_plus/epanet_plus.c +118 -0
- epanet_plus/epanet_toolkit.py +2730 -0
- epanet_plus/epanet_wrapper.py +2414 -0
- epanet_plus/include/epanet_plus.h +9 -0
- epanet_plus-0.0.1.dist-info/METADATA +152 -0
- epanet_plus-0.0.1.dist-info/RECORD +105 -0
- epanet_plus-0.0.1.dist-info/WHEEL +6 -0
- epanet_plus-0.0.1.dist-info/licenses/LICENSE +21 -0
- epanet_plus-0.0.1.dist-info/top_level.txt +11 -0
- examples/basic_usage.py +35 -0
- python-extension/ext.c +344 -0
- python-extension/pyepanet.c +2133 -0
- python-extension/pyepanet.h +143 -0
- python-extension/pyepanet2.c +1823 -0
- python-extension/pyepanet2.h +141 -0
- python-extension/pyepanet_plus.c +37 -0
- python-extension/pyepanet_plus.h +4 -0
- python-extension/pyepanetmsx.c +388 -0
- python-extension/pyepanetmsx.h +35 -0
- tests/test_epanet.py +16 -0
- tests/test_epanetmsx.py +36 -0
- tests/test_epyt.py +114 -0
- tests/test_load_inp_from_buffer.py +18 -0
epanet-src/hydsolver.c
ADDED
|
@@ -0,0 +1,781 @@
|
|
|
1
|
+
/*
|
|
2
|
+
******************************************************************************
|
|
3
|
+
Project: OWA EPANET
|
|
4
|
+
Version: 2.3
|
|
5
|
+
Module: hydsolver.c
|
|
6
|
+
Description: computes flows and pressures throughout a pipe network using
|
|
7
|
+
Todini's Global Gradient Algorithm
|
|
8
|
+
Authors: see AUTHORS
|
|
9
|
+
Copyright: see AUTHORS
|
|
10
|
+
License: see LICENSE
|
|
11
|
+
Last Updated: 06/26/2024
|
|
12
|
+
******************************************************************************
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
#include <stdlib.h>
|
|
16
|
+
#include <stdio.h>
|
|
17
|
+
#include <string.h>
|
|
18
|
+
#include <math.h>
|
|
19
|
+
|
|
20
|
+
#include "types.h"
|
|
21
|
+
#include "funcs.h"
|
|
22
|
+
#include "text.h"
|
|
23
|
+
|
|
24
|
+
// Hydraulic balance error for network being analyzed
|
|
25
|
+
typedef struct {
|
|
26
|
+
double maxheaderror;
|
|
27
|
+
double maxflowerror;
|
|
28
|
+
double maxflowchange;
|
|
29
|
+
int maxheadlink;
|
|
30
|
+
int maxflownode;
|
|
31
|
+
int maxflowlink;
|
|
32
|
+
} Hydbalance;
|
|
33
|
+
|
|
34
|
+
// Exported functions
|
|
35
|
+
int hydsolve(Project *, int *, double *);
|
|
36
|
+
|
|
37
|
+
// Imported functions
|
|
38
|
+
extern int linsolve(Smatrix *, int); //(see SMATRIX.C)
|
|
39
|
+
extern int valvestatus(Project *); //(see HYDSTATUS.C)
|
|
40
|
+
extern int linkstatus(Project *); //(see HYDSTATUS.C)
|
|
41
|
+
|
|
42
|
+
// Local functions
|
|
43
|
+
static int badvalve(Project *, int);
|
|
44
|
+
static int pswitch(Project *);
|
|
45
|
+
|
|
46
|
+
static double newflows(Project *, Hydbalance *);
|
|
47
|
+
static void newlinkflows(Project *, Hydbalance *, double *, double *);
|
|
48
|
+
static void newemitterflows(Project *, Hydbalance *, double *, double *);
|
|
49
|
+
static void newdemandflows(Project *, Hydbalance *, double *, double *);
|
|
50
|
+
static void newleakageflows(Project *, Hydbalance *, double *, double *);
|
|
51
|
+
|
|
52
|
+
static void checkhydbalance(Project *, Hydbalance *);
|
|
53
|
+
static int hasconverged(Project *, double *, Hydbalance *);
|
|
54
|
+
static int pdaconverged(Project *);
|
|
55
|
+
static void reporthydbal(Project *, Hydbalance *);
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
int hydsolve(Project *pr, int *iter, double *relerr)
|
|
59
|
+
/*
|
|
60
|
+
**-------------------------------------------------------------------
|
|
61
|
+
** Input: none
|
|
62
|
+
** Output: *iter = # of iterations to reach solution
|
|
63
|
+
** *relerr = convergence error in solution
|
|
64
|
+
** returns error code
|
|
65
|
+
** Purpose: solves network nodal equations for heads and flows
|
|
66
|
+
** using Todini's Gradient algorithm
|
|
67
|
+
**
|
|
68
|
+
** Notes: Status checks on CVs, pumps and pipes to tanks are made
|
|
69
|
+
** every CheckFreq iteration, up until MaxCheck iterations
|
|
70
|
+
** are reached. Status checks on control valves are made
|
|
71
|
+
** every iteration if DampLimit = 0 or only when the
|
|
72
|
+
** convergence error is at or below DampLimit. If DampLimit
|
|
73
|
+
** is > 0 then future computed flow changes are only 60% of
|
|
74
|
+
** their full value. A complete status check on all links
|
|
75
|
+
** is made when convergence is achieved. If convergence is
|
|
76
|
+
** not achieved in MaxIter trials and ExtraIter > 0 then
|
|
77
|
+
** another ExtraIter trials are made with no status changes
|
|
78
|
+
** made to any links and a warning message is generated.
|
|
79
|
+
**
|
|
80
|
+
** This procedure calls linsolve() which appears in SMATRIX.C.
|
|
81
|
+
**-------------------------------------------------------------------
|
|
82
|
+
*/
|
|
83
|
+
{
|
|
84
|
+
Network *net = &pr->network;
|
|
85
|
+
Hydraul *hyd = &pr->hydraul;
|
|
86
|
+
Smatrix *sm = &hyd->smatrix;
|
|
87
|
+
Report *rpt = &pr->report;
|
|
88
|
+
|
|
89
|
+
int i; // Node index
|
|
90
|
+
int errcode = 0; // Node causing solution error
|
|
91
|
+
int nextcheck; // Next status check trial
|
|
92
|
+
int maxtrials; // Max. trials for convergence
|
|
93
|
+
double newerr; // New convergence error
|
|
94
|
+
int valveChange; // Valve status change flag
|
|
95
|
+
int statChange; // Non-valve status change flag
|
|
96
|
+
Hydbalance hydbal; // Hydraulic balance errors
|
|
97
|
+
|
|
98
|
+
// Initialize status checking & relaxation factor
|
|
99
|
+
nextcheck = hyd->CheckFreq;
|
|
100
|
+
hyd->RelaxFactor = 1.0;
|
|
101
|
+
|
|
102
|
+
// Initialize convergence criteria and PDA results
|
|
103
|
+
hydbal.maxheaderror = 0.0;
|
|
104
|
+
hydbal.maxflowchange = 0.0;
|
|
105
|
+
hyd->DeficientNodes = 0;
|
|
106
|
+
hyd->DemandReduction = 0.0;
|
|
107
|
+
|
|
108
|
+
// Repeat iterations until convergence or trial limit is exceeded.
|
|
109
|
+
// (ExtraIter used to increase trials in case of status cycling.)
|
|
110
|
+
if (rpt->Statflag == FULL) writerelerr(pr, 0, 0);
|
|
111
|
+
maxtrials = hyd->MaxIter;
|
|
112
|
+
if (hyd->ExtraIter > 0) maxtrials += hyd->ExtraIter;
|
|
113
|
+
*iter = 1;
|
|
114
|
+
while (*iter <= maxtrials)
|
|
115
|
+
{
|
|
116
|
+
// Compute coefficient matrices A & F and solve A*H = F
|
|
117
|
+
// where H = heads, A = Jacobian coeffs. derived from
|
|
118
|
+
// head loss gradients, & F = flow correction terms.
|
|
119
|
+
// Solution for H is returned in F from call to linsolve().
|
|
120
|
+
|
|
121
|
+
headlosscoeffs(pr);
|
|
122
|
+
matrixcoeffs(pr);
|
|
123
|
+
errcode = linsolve(sm, net->Njuncs);
|
|
124
|
+
|
|
125
|
+
// Matrix ill-conditioning problem - if control valve causing problem,
|
|
126
|
+
// fix its status & continue, otherwise quit with no solution.
|
|
127
|
+
if (errcode > 0)
|
|
128
|
+
{
|
|
129
|
+
if (badvalve(pr, sm->Order[errcode])) continue;
|
|
130
|
+
else break;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Update current solution.
|
|
134
|
+
// (Row[i] = row of solution matrix corresponding to node i)
|
|
135
|
+
for (i = 1; i <= net->Njuncs; i++)
|
|
136
|
+
{
|
|
137
|
+
hyd->NodeHead[i] = sm->F[sm->Row[i]]; // Update heads
|
|
138
|
+
}
|
|
139
|
+
newerr = newflows(pr, &hydbal); // Update flows
|
|
140
|
+
*relerr = newerr;
|
|
141
|
+
|
|
142
|
+
// Write convergence error to status report if called for
|
|
143
|
+
if (rpt->Statflag == FULL)
|
|
144
|
+
{
|
|
145
|
+
writerelerr(pr, *iter, *relerr);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Apply solution damping & check for change in valve status
|
|
149
|
+
hyd->RelaxFactor = 1.0;
|
|
150
|
+
valveChange = FALSE;
|
|
151
|
+
if (hyd->DampLimit > 0.0)
|
|
152
|
+
{
|
|
153
|
+
if (*relerr <= hyd->DampLimit)
|
|
154
|
+
{
|
|
155
|
+
hyd->RelaxFactor = 0.6;
|
|
156
|
+
valveChange = valvestatus(pr);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
else
|
|
160
|
+
{
|
|
161
|
+
valveChange = valvestatus(pr);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Check for convergence
|
|
165
|
+
if (hasconverged(pr, relerr, &hydbal))
|
|
166
|
+
{
|
|
167
|
+
// We have convergence - quit if we are into extra iterations
|
|
168
|
+
if (*iter > hyd->MaxIter) break;
|
|
169
|
+
|
|
170
|
+
// Quit if no status changes occur
|
|
171
|
+
statChange = FALSE;
|
|
172
|
+
if (valveChange) statChange = TRUE;
|
|
173
|
+
if (linkstatus(pr)) statChange = TRUE;
|
|
174
|
+
if (pswitch(pr)) statChange = TRUE;
|
|
175
|
+
if (!statChange) break;
|
|
176
|
+
|
|
177
|
+
// We have a status change so continue the iterations
|
|
178
|
+
nextcheck = *iter + hyd->CheckFreq;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// No convergence yet - see if it's time for a periodic status
|
|
182
|
+
// check on pumps, CV's, and pipes connected to tank
|
|
183
|
+
else if (*iter <= hyd->MaxCheck && *iter == nextcheck)
|
|
184
|
+
{
|
|
185
|
+
linkstatus(pr);
|
|
186
|
+
nextcheck += hyd->CheckFreq;
|
|
187
|
+
}
|
|
188
|
+
(*iter)++;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// Iterations ended - report any errors.
|
|
192
|
+
if (errcode > 0)
|
|
193
|
+
{
|
|
194
|
+
writehyderr(pr, sm->Order[errcode]); // Ill-conditioned matrix error
|
|
195
|
+
errcode = 110;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// Save total outflow (NodeDemand) at each junction
|
|
199
|
+
for (i = 1; i <= net->Njuncs; i++)
|
|
200
|
+
{
|
|
201
|
+
hyd->NodeDemand[i] = hyd->DemandFlow[i] +
|
|
202
|
+
hyd->EmitterFlow[i] +
|
|
203
|
+
hyd->LeakageFlow[i];
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// Save convergence info
|
|
207
|
+
hyd->RelativeError = *relerr;
|
|
208
|
+
hyd->MaxHeadError = hydbal.maxheaderror;
|
|
209
|
+
hyd->MaxFlowChange = hydbal.maxflowchange;
|
|
210
|
+
hyd->Iterations = *iter;
|
|
211
|
+
return errcode;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
int badvalve(Project *pr, int n)
|
|
216
|
+
/*
|
|
217
|
+
**-----------------------------------------------------------------
|
|
218
|
+
** Input: n = node index
|
|
219
|
+
** Output: returns 1 if node n belongs to an active control valve,
|
|
220
|
+
** 0 otherwise
|
|
221
|
+
** Purpose: determines if a node belongs to an active control valve
|
|
222
|
+
** whose setting causes an inconsistent set of eqns. If so,
|
|
223
|
+
** the valve status is fixed open and a warning condition
|
|
224
|
+
** is generated.
|
|
225
|
+
**-----------------------------------------------------------------
|
|
226
|
+
*/
|
|
227
|
+
{
|
|
228
|
+
Network *net = &pr->network;
|
|
229
|
+
Hydraul *hyd = &pr->hydraul;
|
|
230
|
+
Report *rpt = &pr->report;
|
|
231
|
+
Times *time = &pr->times;
|
|
232
|
+
|
|
233
|
+
int i, k, n1, n2;
|
|
234
|
+
Slink *link;
|
|
235
|
+
LinkType t;
|
|
236
|
+
|
|
237
|
+
for (i = 1; i <= net->Nvalves; i++)
|
|
238
|
+
{
|
|
239
|
+
k = net->Valve[i].Link;
|
|
240
|
+
link = &net->Link[k];
|
|
241
|
+
n1 = link->N1;
|
|
242
|
+
n2 = link->N2;
|
|
243
|
+
if (n == n1 || n == n2)
|
|
244
|
+
{
|
|
245
|
+
t = link->Type;
|
|
246
|
+
if (t == PRV || t == PSV || t == FCV)
|
|
247
|
+
{
|
|
248
|
+
if (hyd->LinkStatus[k] == ACTIVE)
|
|
249
|
+
{
|
|
250
|
+
if (rpt->Statflag == FULL)
|
|
251
|
+
{
|
|
252
|
+
sprintf(pr->Msg, FMT61,
|
|
253
|
+
clocktime(rpt->Atime, time->Htime), link->ID);
|
|
254
|
+
writeline(pr, pr->Msg);
|
|
255
|
+
}
|
|
256
|
+
if (link->Type == FCV) hyd->LinkStatus[k] = XFCV;
|
|
257
|
+
else hyd->LinkStatus[k] = XPRESSURE;
|
|
258
|
+
return 1;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
return 0;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
return 0;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
int pswitch(Project *pr)
|
|
269
|
+
/*
|
|
270
|
+
**--------------------------------------------------------------
|
|
271
|
+
** Input: none
|
|
272
|
+
** Output: returns 1 if status of any link changes, 0 if not
|
|
273
|
+
** Purpose: adjusts settings of links controlled by junction
|
|
274
|
+
** pressures after a hydraulic solution is found
|
|
275
|
+
**--------------------------------------------------------------
|
|
276
|
+
*/
|
|
277
|
+
{
|
|
278
|
+
Network *net = &pr->network;
|
|
279
|
+
Hydraul *hyd = &pr->hydraul;
|
|
280
|
+
Report *rpt = &pr->report;
|
|
281
|
+
|
|
282
|
+
int i, // Control statement index
|
|
283
|
+
k, // Index of link being controlled
|
|
284
|
+
n, // Node controlling link k
|
|
285
|
+
reset, // Flag on control conditions
|
|
286
|
+
change, // Flag for status or setting change
|
|
287
|
+
anychange = 0; // Flag for 1 or more control actions
|
|
288
|
+
char s; // Current link status
|
|
289
|
+
Slink *link;
|
|
290
|
+
|
|
291
|
+
// Check each control statement
|
|
292
|
+
for (i = 1; i <= net->Ncontrols; i++)
|
|
293
|
+
{
|
|
294
|
+
reset = 0;
|
|
295
|
+
k = net->Control[i].Link;
|
|
296
|
+
if (k <= 0) continue;
|
|
297
|
+
|
|
298
|
+
// Determine if control based on a junction, not a tank
|
|
299
|
+
n = net->Control[i].Node;
|
|
300
|
+
if (n > 0 && n <= net->Njuncs)
|
|
301
|
+
{
|
|
302
|
+
// Determine if control conditions are satisfied
|
|
303
|
+
if (net->Control[i].Type == LOWLEVEL &&
|
|
304
|
+
hyd->NodeHead[n] <= net->Control[i].Grade + hyd->Htol)
|
|
305
|
+
{
|
|
306
|
+
reset = 1;
|
|
307
|
+
}
|
|
308
|
+
if (net->Control[i].Type == HILEVEL &&
|
|
309
|
+
hyd->NodeHead[n] >= net->Control[i].Grade - hyd->Htol)
|
|
310
|
+
{
|
|
311
|
+
reset = 1;
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
// Determine if control forces a status or setting change
|
|
316
|
+
if (reset == 1)
|
|
317
|
+
{
|
|
318
|
+
link = &net->Link[k];
|
|
319
|
+
change = 0;
|
|
320
|
+
s = hyd->LinkStatus[k];
|
|
321
|
+
if (link->Type == PIPE)
|
|
322
|
+
{
|
|
323
|
+
if (s != net->Control[i].Status) change = 1;
|
|
324
|
+
}
|
|
325
|
+
if (link->Type == PUMP)
|
|
326
|
+
{
|
|
327
|
+
if (hyd->LinkSetting[k] != net->Control[i].Setting) change = 1;
|
|
328
|
+
}
|
|
329
|
+
if (link->Type >= PRV)
|
|
330
|
+
{
|
|
331
|
+
if (hyd->LinkSetting[k] != net->Control[i].Setting) change = 1;
|
|
332
|
+
else if (hyd->LinkSetting[k] == MISSING && s != net->Control[i].Status)
|
|
333
|
+
{
|
|
334
|
+
change = 1;
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
// If a change occurs, update status & setting
|
|
339
|
+
if (change)
|
|
340
|
+
{
|
|
341
|
+
hyd->LinkStatus[k] = net->Control[i].Status;
|
|
342
|
+
if (link->Type > PIPE)
|
|
343
|
+
{
|
|
344
|
+
hyd->LinkSetting[k] = net->Control[i].Setting;
|
|
345
|
+
}
|
|
346
|
+
if (rpt->Statflag == FULL)
|
|
347
|
+
{
|
|
348
|
+
writestatchange(pr, k, s, hyd->LinkStatus[k]);
|
|
349
|
+
}
|
|
350
|
+
anychange = 1;
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
return anychange;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
|
|
358
|
+
double newflows(Project *pr, Hydbalance *hbal)
|
|
359
|
+
/*
|
|
360
|
+
**----------------------------------------------------------------
|
|
361
|
+
** Input: hbal = ptr. to hydraulic balance information
|
|
362
|
+
** Output: returns solution convergence error
|
|
363
|
+
** Purpose: updates link, emitter & demand flows after new
|
|
364
|
+
** nodal heads are computed.
|
|
365
|
+
**----------------------------------------------------------------
|
|
366
|
+
*/
|
|
367
|
+
{
|
|
368
|
+
Hydraul *hyd = &pr->hydraul;
|
|
369
|
+
|
|
370
|
+
double dqsum, // Network flow change
|
|
371
|
+
qsum; // Network total flow
|
|
372
|
+
|
|
373
|
+
// Initialize sum of flows & corrections
|
|
374
|
+
qsum = 0.0;
|
|
375
|
+
dqsum = 0.0;
|
|
376
|
+
hbal->maxflowchange = 0.0;
|
|
377
|
+
hbal->maxflowlink = 1;
|
|
378
|
+
hbal->maxflownode = -1;
|
|
379
|
+
|
|
380
|
+
// Update flows in all real and virtual links
|
|
381
|
+
newlinkflows(pr, hbal, &qsum, &dqsum);
|
|
382
|
+
newemitterflows(pr, hbal, &qsum, &dqsum);
|
|
383
|
+
newdemandflows(pr, hbal, &qsum, &dqsum);
|
|
384
|
+
if (hyd->HasLeakage) newleakageflows(pr, hbal, &qsum, &dqsum);
|
|
385
|
+
|
|
386
|
+
// Return ratio of total flow corrections to total flow
|
|
387
|
+
if (qsum > hyd->Hacc) return (dqsum / qsum);
|
|
388
|
+
else return dqsum;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
|
|
392
|
+
void newlinkflows(Project *pr, Hydbalance *hbal, double *qsum, double *dqsum)
|
|
393
|
+
/*
|
|
394
|
+
**----------------------------------------------------------------
|
|
395
|
+
** Input: hbal = ptr. to hydraulic balance information
|
|
396
|
+
** qsum = sum of current system flows
|
|
397
|
+
** dqsum = sum of system flow changes
|
|
398
|
+
** Output: updates hbal, qsum and dqsum
|
|
399
|
+
** Purpose: updates link flows after new nodal heads computed
|
|
400
|
+
**----------------------------------------------------------------
|
|
401
|
+
*/
|
|
402
|
+
{
|
|
403
|
+
Network *net = &pr->network;
|
|
404
|
+
Hydraul *hyd = &pr->hydraul;
|
|
405
|
+
|
|
406
|
+
double dh, /* Link head loss */
|
|
407
|
+
dq; /* Link flow change */
|
|
408
|
+
int k, n, n1, n2;
|
|
409
|
+
Slink *link;
|
|
410
|
+
|
|
411
|
+
// Initialize net inflows (i.e., demands) at fixed grade nodes
|
|
412
|
+
for (n = net->Njuncs + 1; n <= net->Nnodes; n++)
|
|
413
|
+
{
|
|
414
|
+
hyd->NodeDemand[n] = 0.0;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
// Examine each link
|
|
418
|
+
for (k = 1; k <= net->Nlinks; k++)
|
|
419
|
+
{
|
|
420
|
+
// Get link and its end nodes
|
|
421
|
+
link = &net->Link[k];
|
|
422
|
+
n1 = link->N1;
|
|
423
|
+
n2 = link->N2;
|
|
424
|
+
|
|
425
|
+
// Apply flow update formula:
|
|
426
|
+
// dq = Y - P * (new head loss)
|
|
427
|
+
// P = 1 / (previous head loss gradient)
|
|
428
|
+
// Y = P * (previous head loss)
|
|
429
|
+
// where P & Y were computed in hlosscoeff() in hydcoeffs.c
|
|
430
|
+
|
|
431
|
+
dh = hyd->NodeHead[n1] - hyd->NodeHead[n2];
|
|
432
|
+
dq = hyd->Y[k] - hyd->P[k] * dh;
|
|
433
|
+
|
|
434
|
+
// Adjust flow change by the relaxation factor
|
|
435
|
+
dq *= hyd->RelaxFactor;
|
|
436
|
+
|
|
437
|
+
// Prevent flow in constant HP pumps from going negative
|
|
438
|
+
if (link->Type == PUMP)
|
|
439
|
+
{
|
|
440
|
+
n = findpump(net, k);
|
|
441
|
+
if (net->Pump[n].Ptype == CONST_HP && dq > hyd->LinkFlow[k])
|
|
442
|
+
{
|
|
443
|
+
dq = hyd->LinkFlow[k] / 2.0;
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
// Update link flow and system flow summation
|
|
448
|
+
hyd->LinkFlow[k] -= dq;
|
|
449
|
+
*qsum += ABS(hyd->LinkFlow[k]);
|
|
450
|
+
*dqsum += ABS(dq);
|
|
451
|
+
|
|
452
|
+
// Update identity of element with max. flow change
|
|
453
|
+
if (ABS(dq) > hbal->maxflowchange)
|
|
454
|
+
{
|
|
455
|
+
hbal->maxflowchange = ABS(dq);
|
|
456
|
+
hbal->maxflowlink = k;
|
|
457
|
+
hbal->maxflownode = -1;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
// Update net flows to fixed grade nodes
|
|
461
|
+
if (hyd->LinkStatus[k] > CLOSED)
|
|
462
|
+
{
|
|
463
|
+
if (n1 > net->Njuncs) hyd->NodeDemand[n1] -= hyd->LinkFlow[k];
|
|
464
|
+
if (n2 > net->Njuncs) hyd->NodeDemand[n2] += hyd->LinkFlow[k];
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
|
|
470
|
+
void newemitterflows(Project *pr, Hydbalance *hbal, double *qsum,
|
|
471
|
+
double *dqsum)
|
|
472
|
+
/*
|
|
473
|
+
**----------------------------------------------------------------
|
|
474
|
+
** Input: hbal = ptr. to hydraulic balance information
|
|
475
|
+
** qsum = sum of current system flows
|
|
476
|
+
** dqsum = sum of system flow changes
|
|
477
|
+
** Output: updates hbal, qsum and dqsum
|
|
478
|
+
** Purpose: updates nodal emitter flows after new nodal heads computed
|
|
479
|
+
**----------------------------------------------------------------
|
|
480
|
+
*/
|
|
481
|
+
{
|
|
482
|
+
Network *net = &pr->network;
|
|
483
|
+
Hydraul *hyd = &pr->hydraul;
|
|
484
|
+
|
|
485
|
+
int i;
|
|
486
|
+
double hloss, hgrad, dh, dq;
|
|
487
|
+
|
|
488
|
+
// Examine each network junction
|
|
489
|
+
for (i = 1; i <= net->Njuncs; i++)
|
|
490
|
+
{
|
|
491
|
+
// Skip junction if it does not have an emitter
|
|
492
|
+
if (net->Node[i].Ke == 0.0) continue;
|
|
493
|
+
|
|
494
|
+
// Find emitter head loss and gradient
|
|
495
|
+
emitterheadloss(pr, i, &hloss, &hgrad);
|
|
496
|
+
|
|
497
|
+
// Find emitter flow change
|
|
498
|
+
dh = hyd->NodeHead[i] - net->Node[i].El;
|
|
499
|
+
dq = (hloss - dh) / hgrad;
|
|
500
|
+
dq *= hyd->RelaxFactor;
|
|
501
|
+
hyd->EmitterFlow[i] -= dq;
|
|
502
|
+
|
|
503
|
+
// Update system flow summation
|
|
504
|
+
*qsum += ABS(hyd->EmitterFlow[i]);
|
|
505
|
+
*dqsum += ABS(dq);
|
|
506
|
+
|
|
507
|
+
// Update identity of element with max. flow change
|
|
508
|
+
if (ABS(dq) > hbal->maxflowchange)
|
|
509
|
+
{
|
|
510
|
+
hbal->maxflowchange = ABS(dq);
|
|
511
|
+
hbal->maxflownode = i;
|
|
512
|
+
hbal->maxflowlink = -1;
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
|
|
518
|
+
void newleakageflows(Project *pr, Hydbalance *hbal, double *qsum,
|
|
519
|
+
double *dqsum)
|
|
520
|
+
/*
|
|
521
|
+
**----------------------------------------------------------------
|
|
522
|
+
** Input: hbal = ptr. to hydraulic balance information
|
|
523
|
+
** qsum = sum of current system flows
|
|
524
|
+
** dqsum = sum of system flow changes
|
|
525
|
+
** Output: updates hbal, qsum and dqsum
|
|
526
|
+
** Purpose: updates nodal leakage flows after new nodal heads computed
|
|
527
|
+
**----------------------------------------------------------------
|
|
528
|
+
*/
|
|
529
|
+
{
|
|
530
|
+
Network *net = &pr->network;
|
|
531
|
+
Hydraul *hyd = &pr->hydraul;
|
|
532
|
+
|
|
533
|
+
int i;
|
|
534
|
+
double dq;
|
|
535
|
+
|
|
536
|
+
for (i = 1; i <= net->Njuncs; i++)
|
|
537
|
+
{
|
|
538
|
+
// Update leakage flow at node i
|
|
539
|
+
dq = leakageflowchange(pr, i);
|
|
540
|
+
if (dq == 0.0) continue;
|
|
541
|
+
|
|
542
|
+
// Update system flow summation
|
|
543
|
+
*qsum += ABS(hyd->LeakageFlow[i]);
|
|
544
|
+
*dqsum += ABS(dq);
|
|
545
|
+
|
|
546
|
+
// Update identity of element with max. flow change
|
|
547
|
+
if (ABS(dq) > hbal->maxflowchange)
|
|
548
|
+
{
|
|
549
|
+
hbal->maxflowchange = ABS(dq);
|
|
550
|
+
hbal->maxflownode = i;
|
|
551
|
+
hbal->maxflowlink = -1;
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
|
|
557
|
+
void newdemandflows(Project *pr, Hydbalance *hbal, double *qsum, double *dqsum)
|
|
558
|
+
/*
|
|
559
|
+
**----------------------------------------------------------------
|
|
560
|
+
** Input: hbal = ptr. to hydraulic balance information
|
|
561
|
+
** qsum = sum of current system flows
|
|
562
|
+
** dqsum = sum of system flow changes
|
|
563
|
+
** Output: updates hbal, qsum and dqsum
|
|
564
|
+
** Purpose: updates nodal pressure dependent demand flows after
|
|
565
|
+
** new nodal heads computed
|
|
566
|
+
**----------------------------------------------------------------
|
|
567
|
+
*/
|
|
568
|
+
{
|
|
569
|
+
Network *net = &pr->network;
|
|
570
|
+
Hydraul *hyd = &pr->hydraul;
|
|
571
|
+
|
|
572
|
+
double dp, // pressure range over which demand can vary (ft)
|
|
573
|
+
dq, // change in demand flow (cfs)
|
|
574
|
+
n, // exponent in head loss v. demand function
|
|
575
|
+
hloss, // current head loss through outflow junction (ft)
|
|
576
|
+
hgrad, // head loss gradient with respect to flow (ft/cfs)
|
|
577
|
+
dh; // new head loss through outflow junction (ft)
|
|
578
|
+
int i;
|
|
579
|
+
|
|
580
|
+
// Get demand function parameters
|
|
581
|
+
if (hyd->DemandModel == DDA) return;
|
|
582
|
+
dp = MAX((hyd->Preq - hyd->Pmin), MINPDIFF);
|
|
583
|
+
n = 1.0 / hyd->Pexp;
|
|
584
|
+
|
|
585
|
+
// Examine each junction
|
|
586
|
+
for (i = 1; i <= net->Njuncs; i++)
|
|
587
|
+
{
|
|
588
|
+
// Skip junctions with no positive demand
|
|
589
|
+
if (hyd->FullDemand[i] <= 0.0) continue;
|
|
590
|
+
|
|
591
|
+
// Find change in demand flow (see hydcoeffs.c)
|
|
592
|
+
demandheadloss(pr, i, dp, n, &hloss, &hgrad);
|
|
593
|
+
dh = hyd->NodeHead[i] - net->Node[i].El - hyd->Pmin;
|
|
594
|
+
dq = (hloss - dh) / hgrad;
|
|
595
|
+
dq *= hyd->RelaxFactor;
|
|
596
|
+
|
|
597
|
+
// Prevent a flow change greater than full demand
|
|
598
|
+
if (fabs(dq) > 0.4 * hyd->FullDemand[i])
|
|
599
|
+
dq = 0.4 * SGN(dq) * hyd->FullDemand[i];
|
|
600
|
+
hyd->DemandFlow[i] -= dq;
|
|
601
|
+
|
|
602
|
+
// Update system flow summation
|
|
603
|
+
*qsum += ABS(hyd->DemandFlow[i]);
|
|
604
|
+
*dqsum += ABS(dq);
|
|
605
|
+
|
|
606
|
+
// Update identity of element with max. flow change
|
|
607
|
+
if (ABS(dq) > hbal->maxflowchange)
|
|
608
|
+
{
|
|
609
|
+
hbal->maxflowchange = ABS(dq);
|
|
610
|
+
hbal->maxflownode = i;
|
|
611
|
+
hbal->maxflowlink = -1;
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
|
|
617
|
+
void checkhydbalance(Project *pr, Hydbalance *hbal)
|
|
618
|
+
/*
|
|
619
|
+
**--------------------------------------------------------------
|
|
620
|
+
** Input: hbal = hydraulic balance errors
|
|
621
|
+
** Output: none
|
|
622
|
+
** Purpose: finds the link with the largest head imbalance
|
|
623
|
+
**--------------------------------------------------------------
|
|
624
|
+
*/
|
|
625
|
+
{
|
|
626
|
+
Network *net = &pr->network;
|
|
627
|
+
Hydraul *hyd = &pr->hydraul;
|
|
628
|
+
|
|
629
|
+
int k, n1, n2;
|
|
630
|
+
double dh, headerror, headloss;
|
|
631
|
+
Slink *link;
|
|
632
|
+
|
|
633
|
+
hbal->maxheaderror = 0.0;
|
|
634
|
+
hbal->maxheadlink = 1;
|
|
635
|
+
headlosscoeffs(pr);
|
|
636
|
+
for (k = 1; k <= net->Nlinks; k++)
|
|
637
|
+
{
|
|
638
|
+
if (hyd->LinkStatus[k] <= CLOSED) continue;
|
|
639
|
+
if (hyd->P[k] == 0.0) continue;
|
|
640
|
+
link = &net->Link[k];
|
|
641
|
+
n1 = link->N1;
|
|
642
|
+
n2 = link->N2;
|
|
643
|
+
dh = hyd->NodeHead[n1] - hyd->NodeHead[n2];
|
|
644
|
+
headloss = hyd->Y[k] / hyd->P[k];
|
|
645
|
+
headerror = ABS(dh - headloss);
|
|
646
|
+
if (headerror > hbal->maxheaderror)
|
|
647
|
+
{
|
|
648
|
+
hbal->maxheaderror = headerror;
|
|
649
|
+
hbal->maxheadlink = k;
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
|
|
655
|
+
int hasconverged(Project *pr, double *relerr, Hydbalance *hbal)
|
|
656
|
+
/*
|
|
657
|
+
**--------------------------------------------------------------
|
|
658
|
+
** Input: relerr = current total relative flow change
|
|
659
|
+
** hbal = current hydraulic balance errors
|
|
660
|
+
** Output: returns 1 if system has converged or 0 if not
|
|
661
|
+
** Purpose: checks various criteria to see if system has
|
|
662
|
+
** become hydraulically balanced
|
|
663
|
+
**--------------------------------------------------------------
|
|
664
|
+
*/
|
|
665
|
+
{
|
|
666
|
+
Hydraul *hyd = &pr->hydraul;
|
|
667
|
+
|
|
668
|
+
// Check that total relative flow change is small enough
|
|
669
|
+
if (*relerr > hyd->Hacc) return 0;
|
|
670
|
+
|
|
671
|
+
// Find largest head loss error and absolute flow change
|
|
672
|
+
checkhydbalance(pr, hbal);
|
|
673
|
+
if (pr->report.Statflag == FULL)
|
|
674
|
+
{
|
|
675
|
+
reporthydbal(pr, hbal);
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
// Check that head loss error and flow change criteria are met
|
|
679
|
+
if (hyd->HeadErrorLimit > 0.0 &&
|
|
680
|
+
hbal->maxheaderror > hyd->HeadErrorLimit) return 0;
|
|
681
|
+
if (hyd->FlowChangeLimit > 0.0 &&
|
|
682
|
+
hbal->maxflowchange > hyd->FlowChangeLimit) return 0;
|
|
683
|
+
|
|
684
|
+
// Check for node leakage convergence
|
|
685
|
+
if (hyd->HasLeakage && !leakagehasconverged(pr)) return 0;
|
|
686
|
+
|
|
687
|
+
// Check for pressure driven analysis convergence
|
|
688
|
+
if (hyd->DemandModel == PDA) return pdaconverged(pr);
|
|
689
|
+
return 1;
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
|
|
693
|
+
int pdaconverged(Project *pr)
|
|
694
|
+
/*
|
|
695
|
+
**--------------------------------------------------------------
|
|
696
|
+
** Input: none
|
|
697
|
+
** Output: returns 1 if PDA converged, 0 if not
|
|
698
|
+
** Purpose: checks if pressure driven analysis has converged
|
|
699
|
+
** and updates total demand deficit
|
|
700
|
+
**--------------------------------------------------------------
|
|
701
|
+
*/
|
|
702
|
+
{
|
|
703
|
+
Hydraul *hyd = &pr->hydraul;
|
|
704
|
+
|
|
705
|
+
const double QTOL = 0.0001; // 0.0001 cfs ~= 0.05 gpm ~= 0.2 lpm)
|
|
706
|
+
int i, converged = 1;
|
|
707
|
+
|
|
708
|
+
double totalDemand = 0.0, totalReduction = 0.0;
|
|
709
|
+
double dp = hyd->Preq - hyd->Pmin;
|
|
710
|
+
double p, q, r;
|
|
711
|
+
|
|
712
|
+
hyd->DeficientNodes = 0;
|
|
713
|
+
hyd->DemandReduction = 0.0;
|
|
714
|
+
|
|
715
|
+
// Examine each network junction
|
|
716
|
+
for (i = 1; i <= pr->network.Njuncs; i++)
|
|
717
|
+
{
|
|
718
|
+
// Skip nodes whose required demand is non-positive
|
|
719
|
+
if (hyd->FullDemand[i] <= 0.0) continue;
|
|
720
|
+
|
|
721
|
+
// Evaluate demand equation at current pressure solution
|
|
722
|
+
p = hyd->NodeHead[i] - pr->network.Node[i].El;
|
|
723
|
+
if (p <= hyd->Pmin)
|
|
724
|
+
q = 0.0;
|
|
725
|
+
else if (p >= hyd->Preq)
|
|
726
|
+
q = hyd->FullDemand[i];
|
|
727
|
+
else
|
|
728
|
+
{
|
|
729
|
+
r = (p - hyd->Pmin) / dp;
|
|
730
|
+
q = hyd->FullDemand[i] * pow(r, hyd->Pexp);
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
// Check if demand has not converged
|
|
734
|
+
if (fabs(q - hyd->DemandFlow[i]) > QTOL)
|
|
735
|
+
converged = 0;
|
|
736
|
+
|
|
737
|
+
// Accumulate demand deficient node count and demand deficit
|
|
738
|
+
if (hyd->DemandFlow[i] + QTOL < hyd->FullDemand[i])
|
|
739
|
+
{
|
|
740
|
+
hyd->DeficientNodes++;
|
|
741
|
+
totalDemand += hyd->FullDemand[i];
|
|
742
|
+
totalReduction += hyd->FullDemand[i] - hyd->DemandFlow[i];
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
if (totalDemand > 0.0)
|
|
746
|
+
hyd->DemandReduction = totalReduction / totalDemand * 100.0;
|
|
747
|
+
return converged;
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
|
|
751
|
+
void reporthydbal(Project *pr, Hydbalance *hbal)
|
|
752
|
+
/*
|
|
753
|
+
**--------------------------------------------------------------
|
|
754
|
+
** Input: hbal = current hydraulic balance errors
|
|
755
|
+
** Output: none
|
|
756
|
+
** Purpose: identifies links with largest flow change and
|
|
757
|
+
** largest head loss error.
|
|
758
|
+
**--------------------------------------------------------------
|
|
759
|
+
*/
|
|
760
|
+
{
|
|
761
|
+
double qchange = hbal->maxflowchange * pr->Ucf[FLOW];
|
|
762
|
+
double herror = hbal->maxheaderror * pr->Ucf[HEAD];
|
|
763
|
+
int qlink = hbal->maxflowlink;
|
|
764
|
+
int qnode = hbal->maxflownode;
|
|
765
|
+
int hlink = hbal->maxheadlink;
|
|
766
|
+
if (qlink >= 1)
|
|
767
|
+
{
|
|
768
|
+
sprintf(pr->Msg, FMT66, qchange, pr->network.Link[qlink].ID);
|
|
769
|
+
writeline(pr, pr->Msg);
|
|
770
|
+
}
|
|
771
|
+
else if (qnode >= 1)
|
|
772
|
+
{
|
|
773
|
+
sprintf(pr->Msg, FMT67, qchange, pr->network.Node[qnode].ID);
|
|
774
|
+
writeline(pr, pr->Msg);
|
|
775
|
+
}
|
|
776
|
+
if (hlink >= 1)
|
|
777
|
+
{
|
|
778
|
+
sprintf(pr->Msg, FMT68, herror, pr->network.Link[hlink].ID);
|
|
779
|
+
writeline(pr, pr->Msg);
|
|
780
|
+
}
|
|
781
|
+
}
|