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.

Files changed (105) hide show
  1. docs/conf.py +67 -0
  2. epanet-msx-src/dispersion.h +27 -0
  3. epanet-msx-src/hash.c +107 -0
  4. epanet-msx-src/hash.h +28 -0
  5. epanet-msx-src/include/epanetmsx.h +104 -0
  6. epanet-msx-src/include/epanetmsx_export.h +42 -0
  7. epanet-msx-src/mathexpr.c +937 -0
  8. epanet-msx-src/mathexpr.h +39 -0
  9. epanet-msx-src/mempool.c +204 -0
  10. epanet-msx-src/mempool.h +24 -0
  11. epanet-msx-src/msxchem.c +1285 -0
  12. epanet-msx-src/msxcompiler.c +368 -0
  13. epanet-msx-src/msxdict.h +42 -0
  14. epanet-msx-src/msxdispersion.c +586 -0
  15. epanet-msx-src/msxerr.c +116 -0
  16. epanet-msx-src/msxfile.c +260 -0
  17. epanet-msx-src/msxfuncs.c +175 -0
  18. epanet-msx-src/msxfuncs.h +35 -0
  19. epanet-msx-src/msxinp.c +1504 -0
  20. epanet-msx-src/msxout.c +398 -0
  21. epanet-msx-src/msxproj.c +791 -0
  22. epanet-msx-src/msxqual.c +2011 -0
  23. epanet-msx-src/msxrpt.c +400 -0
  24. epanet-msx-src/msxtank.c +422 -0
  25. epanet-msx-src/msxtoolkit.c +1164 -0
  26. epanet-msx-src/msxtypes.h +551 -0
  27. epanet-msx-src/msxutils.c +524 -0
  28. epanet-msx-src/msxutils.h +56 -0
  29. epanet-msx-src/newton.c +158 -0
  30. epanet-msx-src/newton.h +34 -0
  31. epanet-msx-src/rk5.c +287 -0
  32. epanet-msx-src/rk5.h +39 -0
  33. epanet-msx-src/ros2.c +293 -0
  34. epanet-msx-src/ros2.h +35 -0
  35. epanet-msx-src/smatrix.c +816 -0
  36. epanet-msx-src/smatrix.h +29 -0
  37. epanet-src/AUTHORS +60 -0
  38. epanet-src/LICENSE +21 -0
  39. epanet-src/enumstxt.h +151 -0
  40. epanet-src/epanet.c +5937 -0
  41. epanet-src/epanet2.c +961 -0
  42. epanet-src/epanet2.def +131 -0
  43. epanet-src/errors.dat +79 -0
  44. epanet-src/flowbalance.c +186 -0
  45. epanet-src/funcs.h +219 -0
  46. epanet-src/genmmd.c +1000 -0
  47. epanet-src/hash.c +177 -0
  48. epanet-src/hash.h +28 -0
  49. epanet-src/hydcoeffs.c +1303 -0
  50. epanet-src/hydraul.c +1164 -0
  51. epanet-src/hydsolver.c +781 -0
  52. epanet-src/hydstatus.c +442 -0
  53. epanet-src/include/epanet2.h +466 -0
  54. epanet-src/include/epanet2_2.h +1962 -0
  55. epanet-src/include/epanet2_enums.h +518 -0
  56. epanet-src/inpfile.c +884 -0
  57. epanet-src/input1.c +672 -0
  58. epanet-src/input2.c +970 -0
  59. epanet-src/input3.c +2265 -0
  60. epanet-src/leakage.c +527 -0
  61. epanet-src/mempool.c +146 -0
  62. epanet-src/mempool.h +24 -0
  63. epanet-src/output.c +853 -0
  64. epanet-src/project.c +1691 -0
  65. epanet-src/quality.c +695 -0
  66. epanet-src/qualreact.c +800 -0
  67. epanet-src/qualroute.c +696 -0
  68. epanet-src/report.c +1559 -0
  69. epanet-src/rules.c +1500 -0
  70. epanet-src/smatrix.c +871 -0
  71. epanet-src/text.h +508 -0
  72. epanet-src/types.h +928 -0
  73. epanet-src/util/cstr_helper.c +59 -0
  74. epanet-src/util/cstr_helper.h +38 -0
  75. epanet-src/util/errormanager.c +92 -0
  76. epanet-src/util/errormanager.h +39 -0
  77. epanet-src/util/filemanager.c +212 -0
  78. epanet-src/util/filemanager.h +81 -0
  79. epanet-src/validate.c +408 -0
  80. epanet.cpython-311-darwin.so +0 -0
  81. epanet_plus/VERSION +1 -0
  82. epanet_plus/__init__.py +8 -0
  83. epanet_plus/epanet_plus.c +118 -0
  84. epanet_plus/epanet_toolkit.py +2730 -0
  85. epanet_plus/epanet_wrapper.py +2414 -0
  86. epanet_plus/include/epanet_plus.h +9 -0
  87. epanet_plus-0.0.1.dist-info/METADATA +152 -0
  88. epanet_plus-0.0.1.dist-info/RECORD +105 -0
  89. epanet_plus-0.0.1.dist-info/WHEEL +6 -0
  90. epanet_plus-0.0.1.dist-info/licenses/LICENSE +21 -0
  91. epanet_plus-0.0.1.dist-info/top_level.txt +11 -0
  92. examples/basic_usage.py +35 -0
  93. python-extension/ext.c +344 -0
  94. python-extension/pyepanet.c +2133 -0
  95. python-extension/pyepanet.h +143 -0
  96. python-extension/pyepanet2.c +1823 -0
  97. python-extension/pyepanet2.h +141 -0
  98. python-extension/pyepanet_plus.c +37 -0
  99. python-extension/pyepanet_plus.h +4 -0
  100. python-extension/pyepanetmsx.c +388 -0
  101. python-extension/pyepanetmsx.h +35 -0
  102. tests/test_epanet.py +16 -0
  103. tests/test_epanetmsx.py +36 -0
  104. tests/test_epyt.py +114 -0
  105. tests/test_load_inp_from_buffer.py +18 -0
epanet-src/hydcoeffs.c ADDED
@@ -0,0 +1,1303 @@
1
+ /*
2
+ ******************************************************************************
3
+ Project: OWA EPANET
4
+ Version: 2.3
5
+ Module: hydcoeffs.c
6
+ Description: computes coefficients for a hydraulic solution matrix
7
+ Authors: see AUTHORS
8
+ Copyright: see AUTHORS
9
+ License: see LICENSE
10
+ Last Updated: 06/15/2024
11
+ ******************************************************************************
12
+ */
13
+
14
+ #include <stdlib.h>
15
+ #include <stdio.h>
16
+ #include <string.h>
17
+ #include <math.h>
18
+
19
+ #include "types.h"
20
+ #include "funcs.h"
21
+
22
+ // Constants used for computing Darcy-Weisbach friction factor
23
+ const double A1 = 3.14159265358979323850e+03; // 1000*PI
24
+ const double A2 = 1.57079632679489661930e+03; // 500*PI
25
+ const double A3 = 5.02654824574366918160e+01; // 16*PI
26
+ const double A4 = 6.28318530717958647700e+00; // 2*PI
27
+ const double A8 = 4.61841319859066668690e+00; // 5.74*(PI/4)^.9
28
+ const double A9 = -8.68588963806503655300e-01; // -2/ln(10)
29
+ const double AA = -1.5634601348517065795e+00; // -2*.9*2/ln(10)
30
+ const double AB = 3.28895476345399058690e-03; // 5.74/(4000^.9)
31
+ const double AC = -5.14214965799093883760e-03; // AA*AB
32
+
33
+ // Definitions of very small and very big coefficients
34
+ const double CSMALL = 1.e-6;
35
+ const double CBIG = 1.e8;
36
+
37
+ // Exported functions
38
+ //void resistcoeff(Project *, int );
39
+ //double pcvlosscoeff(Project *, int, double);
40
+ //void headlosscoeffs(Project *);
41
+ //void matrixcoeffs(Project *);
42
+ //void emitterheadloss(Project *, int, double *, double *);
43
+ //void demandheadloss(Project *, int, double, double, double *, double *);
44
+
45
+ // Local functions
46
+ static void linkcoeffs(Project *pr);
47
+ static void nodecoeffs(Project *pr);
48
+ static void valvecoeffs(Project *pr);
49
+ static void emittercoeffs(Project *pr);
50
+ static void demandcoeffs(Project *pr);
51
+
52
+ static void pipecoeff(Project *pr, int k);
53
+ static void DWpipecoeff(Project *pr, int k);
54
+ static double frictionFactor(double q, double e, double s, double *dfdq);
55
+
56
+ static void pumpcoeff(Project *pr, int k);
57
+ static void curvecoeff(Project *pr, int i, double q, double *h0, double *r);
58
+
59
+ static void valvecoeff(Project *pr, int k);
60
+ static void gpvcoeff(Project *pr, int k);
61
+ static void pbvcoeff(Project *pr, int k);
62
+ static void tcvcoeff(Project *pr, int k);
63
+ static void pcvcoeff(Project *pr, int k);
64
+ static void prvcoeff(Project *pr, int k, int n1, int n2);
65
+ static void psvcoeff(Project *pr, int k, int n1, int n2);
66
+ static void fcvcoeff(Project *pr, int k, int n1, int n2);
67
+
68
+
69
+ void addlowerbarrier(double dq, double* hloss, double* hgrad)
70
+ /*
71
+ **--------------------------------------------------------------------
72
+ ** Input: dq = difference between current flow and lower flow limit
73
+ ** Output: hloss = updated head loss value
74
+ ** hgrad = updated head loss gradient value
75
+ ** Purpose: adds a head loss barrier to prevent flow from falling
76
+ ** below a given lower limit.
77
+ **--------------------------------------------------------------------
78
+ */
79
+ {
80
+ double a = 1.e9 * dq;
81
+ double b = sqrt(a*a + 1.e-6);
82
+ *hloss += (a - b) / 2.;
83
+ *hgrad += (1.e9 / 2.) * ( 1.0 - a / b);
84
+ }
85
+
86
+ void addupperbarrier(double dq, double* hloss, double* hgrad)
87
+ /*
88
+ **--------------------------------------------------------------------
89
+ ** Input: dq = difference between current flow and upper flow limit
90
+ ** Output: hloss = updated head loss value
91
+ ** hgrad = updated head loss gradient value
92
+ ** Purpose: adds a head loss barrier to prevent flow from exceeding
93
+ ** a given upper limit.
94
+ **--------------------------------------------------------------------
95
+ */
96
+ {
97
+ double a = 1.e9 * dq;
98
+ double b = sqrt(a*a + 1.e-6);
99
+ *hloss += (a + b) / 2.;
100
+ *hgrad += (1.e9 / 2.) * ( 1.0 + a / b);
101
+ }
102
+
103
+
104
+ void resistcoeff(Project *pr, int k)
105
+ /*
106
+ **--------------------------------------------------------------------
107
+ ** Input: k = link index
108
+ ** Output: none
109
+ ** Purpose: computes link flow resistance coefficient
110
+ **--------------------------------------------------------------------
111
+ */
112
+ {
113
+ Network *net = &pr->network;
114
+ Hydraul *hyd = &pr->hydraul;
115
+
116
+ double e, d, L;
117
+ Slink *link = &net->Link[k];
118
+
119
+ switch (link->Type) {
120
+
121
+ // ... Link is a pipe. Compute resistance based on headloss formula.
122
+ // Friction factor for D-W formula gets included during head loss
123
+ // calculation.
124
+ case CVPIPE:
125
+ case PIPE:
126
+ e = link->Kc; // Roughness coeff.
127
+ d = link->Diam; // Diameter
128
+ L = link->Len; // Length
129
+ switch (hyd->Formflag)
130
+ {
131
+ case HW:
132
+ link->R = 4.727 * L / pow(e, hyd->Hexp) / pow(d, 4.871);
133
+ break;
134
+ case DW:
135
+ link->R = L / 2.0 / 32.2 / d / SQR(PI * SQR(d) / 4.0);
136
+ break;
137
+ case CM:
138
+ link->R = SQR(4.0 * e / (1.49 * PI * SQR(d))) *
139
+ pow((d / 4.0), -1.333) * L;
140
+ }
141
+ break;
142
+
143
+ // ... Link is a pump. Use huge resistance.
144
+ case PUMP:
145
+ link->R = CBIG;
146
+ break;
147
+
148
+ case PCV:
149
+ link->R = pcvlosscoeff(pr, k, link->Kc);
150
+ break;
151
+
152
+ // ... For all other links (e.g. valves) use a small resistance
153
+ default:
154
+ link->R = CSMALL;
155
+ break;
156
+ }
157
+ }
158
+
159
+
160
+ double pcvlosscoeff(Project* pr, int k, double s)
161
+ /*
162
+ **--------------------------------------------------------------
163
+ ** Input: k = link index
164
+ ** s = valve percent open setting
165
+ ** Output: returns a valve loss coefficient
166
+ ** Purpose: finds a Positional Control Valve's loss
167
+ ** coefficient from its percent open setting.
168
+ **--------------------------------------------------------------
169
+ */
170
+ {
171
+ Network* net = &pr->network;
172
+
173
+ int v = findvalve(net, k); // valve index
174
+ int c = net->Valve[v].Curve; // Kv curve index
175
+ double d; // valve diameter
176
+ double kmo; // fully open loss coeff.
177
+ double km; // partly open loss coeff.
178
+ double kvr; // Kv / Kvo (Kvo = Kv at fully open)
179
+ double *x, *y; // points on kvr v. percent open curve
180
+ int k1, k2, npts;
181
+ Scurve *curve;
182
+
183
+ // Valve has no setting so return 0
184
+ if (s == MISSING) return 0.0;
185
+
186
+ // Valve is completely open so return its Km value
187
+ d = net->Link[k].Diam;
188
+ kmo = net->Link[k].Km;
189
+ if (s >= 100.0) return kmo;
190
+
191
+ // Valve is completely closed so return a large coeff.
192
+ if (s <= 0.0) return CBIG;
193
+
194
+ // Valve has no assigned curve so assume a linear one
195
+ if (c == 0) kvr = s;
196
+
197
+ else
198
+ {
199
+ // Valve curve data
200
+ curve = &net->Curve[c];
201
+ npts = curve->Npts;
202
+ x = curve->X; // x = % open
203
+ y = curve->Y; // y = Kv / Kvo as a %
204
+
205
+ // s lies below first point of curve
206
+ if (s < x[0])
207
+ kvr = s / x[0] * y[0];
208
+
209
+ // s lies above last point of curve
210
+ else if (s > x[npts-1])
211
+ {
212
+ k2 = npts - 1;
213
+ kvr = (s - x[k2]) / (1. - x[k2]) * (1. - y[k2]) + y[k2];
214
+ }
215
+
216
+ // Otherwise interpolate over curve segment that brackets s
217
+ else
218
+ {
219
+ k2 = 0;
220
+ while (k2 < npts && x[k2] < s) k2++;
221
+ if (k2 == 0) k2++;
222
+ else if (k2 == npts) k2--;
223
+ k1 = k2 - 1;
224
+ kvr = (y[k2] - y[k1]) / (x[k2] - x[k1]);
225
+ kvr = y[k1] + kvr * (s - x[k1]);
226
+ }
227
+ }
228
+
229
+ // Convert kvr from % to fraction
230
+ kvr /= 100.;
231
+ kvr = MIN(kvr, 1.0);
232
+ kvr = MAX(kvr, CSMALL);
233
+
234
+ // Convert from Kv ratio to minor loss coeff.
235
+ km = kmo / (kvr * kvr);
236
+ km = MIN(km, CBIG);
237
+ return km;
238
+ }
239
+
240
+
241
+ void headlosscoeffs(Project *pr)
242
+ /*
243
+ **--------------------------------------------------------------
244
+ ** Input: none
245
+ ** Output: none
246
+ ** Purpose: computes coefficients P (1 / head loss gradient)
247
+ ** and Y (head loss / gradient) for all links.
248
+ **--------------------------------------------------------------
249
+ */
250
+ {
251
+ Network *net = &pr->network;
252
+ Hydraul *hyd = &pr->hydraul;
253
+
254
+ int k;
255
+
256
+ for (k = 1; k <= net->Nlinks; k++)
257
+ {
258
+ switch (net->Link[k].Type)
259
+ {
260
+ case CVPIPE:
261
+ case PIPE:
262
+ pipecoeff(pr, k);
263
+ break;
264
+ case PUMP:
265
+ pumpcoeff(pr, k);
266
+ break;
267
+ case PBV:
268
+ pbvcoeff(pr, k);
269
+ break;
270
+ case TCV:
271
+ tcvcoeff(pr, k);
272
+ break;
273
+ case PCV:
274
+ pcvcoeff(pr, k);
275
+ break;
276
+ case GPV:
277
+ gpvcoeff(pr, k);
278
+ break;
279
+ case FCV:
280
+ case PRV:
281
+ case PSV:
282
+ if (hyd->LinkSetting[k] == MISSING) valvecoeff(pr, k);
283
+ else hyd->P[k] = 0.0;
284
+ }
285
+ }
286
+ }
287
+
288
+
289
+ void matrixcoeffs(Project *pr)
290
+ /*
291
+ **--------------------------------------------------------------
292
+ ** Input: none
293
+ ** Output: none
294
+ ** Purpose: computes coefficients of linearized network eqns.
295
+ **--------------------------------------------------------------
296
+ */
297
+ {
298
+ Network *net = &pr->network;
299
+ Hydraul *hyd = &pr->hydraul;
300
+ Smatrix *sm = &hyd->smatrix;
301
+
302
+ // Reset values of all diagonal coeffs. (Aii), off-diagonal
303
+ // coeffs. (Aij), r.h.s. coeffs. (F) and node excess flow (Xflow)
304
+ memset(sm->Aii, 0, (net->Nnodes + 1) * sizeof(double));
305
+ memset(sm->Aij, 0, (sm->Ncoeffs + 1) * sizeof(double));
306
+ memset(sm->F, 0, (net->Nnodes + 1) * sizeof(double));
307
+ memset(hyd->Xflow, 0, (net->Nnodes + 1) * sizeof(double));
308
+
309
+ // Compute matrix coeffs. from links, emitters, and nodal demands
310
+ linkcoeffs(pr);
311
+ emittercoeffs(pr);
312
+ demandcoeffs(pr);
313
+ if (hyd->HasLeakage) leakagecoeffs(pr);
314
+
315
+ // Update nodal flow balances with demands and add onto r.h.s. coeffs.
316
+ nodecoeffs(pr);
317
+
318
+ // Finally, find coeffs. for PRV/PSV/FCV control valves whose
319
+ // status is not fixed to OPEN/CLOSED
320
+ valvecoeffs(pr);
321
+ }
322
+
323
+
324
+ void linkcoeffs(Project *pr)
325
+ /*
326
+ **--------------------------------------------------------------
327
+ ** Input: none
328
+ ** Output: none
329
+ ** Purpose: computes coefficients contributed by links to the
330
+ ** linearized system of hydraulic equations.
331
+ **--------------------------------------------------------------
332
+ */
333
+ {
334
+ Network *net = &pr->network;
335
+ Hydraul *hyd = &pr->hydraul;
336
+ Smatrix *sm = &hyd->smatrix;
337
+
338
+ int k, n1, n2;
339
+ Slink *link;
340
+
341
+ // Examine each link of network
342
+ for (k = 1; k <= net->Nlinks; k++)
343
+ {
344
+ if (hyd->P[k] == 0.0) continue;
345
+ link = &net->Link[k];
346
+ n1 = link->N1; // Start node of link
347
+ n2 = link->N2; // End node of link
348
+
349
+ // Update nodal flow excess (Xflow)
350
+ // (Flow out of node is (-), flow into node is (+))
351
+ hyd->Xflow[n1] -= hyd->LinkFlow[k];
352
+ hyd->Xflow[n2] += hyd->LinkFlow[k];
353
+
354
+ // Add to off-diagonal coeff. of linear system matrix
355
+ sm->Aij[sm->Ndx[k]] -= hyd->P[k];
356
+
357
+ // Update linear system coeffs. associated with start node n1
358
+ // ... node n1 is junction
359
+ if (n1 <= net->Njuncs)
360
+ {
361
+ sm->Aii[sm->Row[n1]] += hyd->P[k]; // Diagonal coeff.
362
+ sm->F[sm->Row[n1]] += hyd->Y[k]; // RHS coeff.
363
+ }
364
+
365
+ // ... node n1 is a tank/reservoir
366
+ else sm->F[sm->Row[n2]] += (hyd->P[k] * hyd->NodeHead[n1]);
367
+
368
+ // Update linear system coeffs. associated with end node n2
369
+ // ... node n2 is junction
370
+ if (n2 <= net->Njuncs)
371
+ {
372
+ sm->Aii[sm->Row[n2]] += hyd->P[k]; // Diagonal coeff.
373
+ sm->F[sm->Row[n2]] -= hyd->Y[k]; // RHS coeff.
374
+ }
375
+
376
+ // ... node n2 is a tank/reservoir
377
+ else sm->F[sm->Row[n1]] += (hyd->P[k] * hyd->NodeHead[n2]);
378
+ }
379
+ }
380
+
381
+
382
+ void nodecoeffs(Project *pr)
383
+ /*
384
+ **----------------------------------------------------------------
385
+ ** Input: none
386
+ ** Output: none
387
+ ** Purpose: completes calculation of nodal flow balance array
388
+ ** (Xflow) & r.h.s. (F) of linearized hydraulic eqns.
389
+ **----------------------------------------------------------------
390
+ */
391
+ {
392
+ Network *net = &pr->network;
393
+ Hydraul *hyd = &pr->hydraul;
394
+ Smatrix *sm = &hyd->smatrix;
395
+
396
+ int i;
397
+
398
+ // For junction nodes, subtract demand flow from net
399
+ // flow excess & add flow excess to RHS array F
400
+ for (i = 1; i <= net->Njuncs; i++)
401
+ {
402
+ hyd->Xflow[i] -= hyd->DemandFlow[i];
403
+ sm->F[sm->Row[i]] += hyd->Xflow[i];
404
+ }
405
+ }
406
+
407
+
408
+ void valvecoeffs(Project *pr)
409
+ /*
410
+ **--------------------------------------------------------------
411
+ ** Input: none
412
+ ** Output: none
413
+ ** Purpose: computes coeffs. of the linearized hydraulic eqns.
414
+ ** contributed by PRVs, PSVs & FCVs whose status is
415
+ ** not fixed to OPEN/CLOSED
416
+ **--------------------------------------------------------------
417
+ */
418
+ {
419
+ Network *net = &pr->network;
420
+ Hydraul *hyd = &pr->hydraul;
421
+
422
+ int i, k, n1, n2;
423
+ Slink *link;
424
+ Svalve *valve;
425
+
426
+ // Examine each valve
427
+ for (i = 1; i <= net->Nvalves; i++)
428
+ {
429
+ // Find valve's link index
430
+ valve = &net->Valve[i];
431
+ k = valve->Link;
432
+
433
+ // Coeffs. for fixed status valves have already been computed
434
+ if (hyd->LinkSetting[k] == MISSING) continue;
435
+
436
+ // Start & end nodes of valve's link
437
+ link = &net->Link[k];
438
+ n1 = link->N1;
439
+ n2 = link->N2;
440
+
441
+ // Call valve-specific function
442
+ switch (link->Type)
443
+ {
444
+ case PRV:
445
+ prvcoeff(pr, k, n1, n2);
446
+ break;
447
+ case PSV:
448
+ psvcoeff(pr, k, n1, n2);
449
+ break;
450
+ case FCV:
451
+ fcvcoeff(pr, k, n1, n2);
452
+ break;
453
+ default: continue;
454
+ }
455
+ }
456
+ }
457
+
458
+
459
+ void emittercoeffs(Project *pr)
460
+ /*
461
+ **--------------------------------------------------------------
462
+ ** Input: none
463
+ ** Output: none
464
+ ** Purpose: computes coeffs. of the linearized hydraulic eqns.
465
+ ** contributed by emitters.
466
+ **
467
+ ** Note: Emitters consist of a fictitious pipe connected to
468
+ ** a fictitious reservoir whose elevation equals that
469
+ ** of the junction. The headloss through this pipe is
470
+ ** Ke*(Flow)^hyd->Qexp, where Ke = emitter headloss coeff.
471
+ **--------------------------------------------------------------
472
+ */
473
+ {
474
+ Network *net = &pr->network;
475
+ Hydraul *hyd = &pr->hydraul;
476
+ Smatrix *sm = &hyd->smatrix;
477
+
478
+ int i, row;
479
+ double hloss, hgrad;
480
+ Snode *node;
481
+
482
+ for (i = 1; i <= net->Njuncs; i++)
483
+ {
484
+ // Skip junctions without emitters
485
+ node = &net->Node[i];
486
+ if (node->Ke == 0.0) continue;
487
+
488
+ // Find emitter head loss and gradient
489
+ emitterheadloss(pr, i, &hloss, &hgrad);
490
+
491
+ // Row of solution matrix
492
+ row = sm->Row[i];
493
+
494
+ // Addition to matrix diagonal & r.h.s
495
+ sm->Aii[row] += 1.0 / hgrad;
496
+ sm->F[row] += (hloss + node->El) / hgrad;
497
+
498
+ // Update to node flow excess
499
+ hyd->Xflow[i] -= hyd->EmitterFlow[i];
500
+ }
501
+ }
502
+
503
+
504
+ void emitterheadloss(Project *pr, int i, double *hloss, double *hgrad)
505
+ /*
506
+ **-------------------------------------------------------------
507
+ ** Input: i = node index
508
+ ** Output: hloss = head loss across node's emitter
509
+ ** hgrad = head loss gradient
510
+ ** Purpose: computes an emitter's head loss and gradient.
511
+ **-------------------------------------------------------------
512
+ */
513
+ {
514
+ Hydraul *hyd = &pr->hydraul;
515
+
516
+ double ke;
517
+ double q;
518
+
519
+ // Set adjusted emitter coeff.
520
+ ke = MAX(CSMALL, pr->network.Node[i].Ke);
521
+
522
+ // Compute gradient of head loss through emitter
523
+ q = hyd->EmitterFlow[i];
524
+ *hgrad = hyd->Qexp * ke * pow(fabs(q), hyd->Qexp - 1.0);
525
+
526
+ // Use linear head loss function for small gradient
527
+ if (*hgrad < hyd->RQtol)
528
+ {
529
+ *hgrad = hyd->RQtol / hyd->Qexp;
530
+ *hloss = (*hgrad) * q;
531
+ }
532
+
533
+ // Otherwise use normal emitter head loss function
534
+ else *hloss = (*hgrad) * q / hyd->Qexp;
535
+
536
+ // Prevent negative flow if backflow not allowed
537
+ if (hyd->EmitBackFlag == 0)
538
+ {
539
+ addlowerbarrier(q, hloss, hgrad);
540
+ }
541
+ }
542
+
543
+
544
+ void demandcoeffs(Project *pr)
545
+ /*
546
+ **--------------------------------------------------------------
547
+ ** Input: none
548
+ ** Output: none
549
+ ** Purpose: computes coeffs. of the linearized hydraulic eqns.
550
+ ** contributed by pressure dependent demands.
551
+ **
552
+ ** Note: Pressure dependent demands are modelled like emitters
553
+ ** with Hloss = Preq * (D / Dfull)^(1/Pexp)
554
+ ** where D (actual demand) is zero for negative pressure
555
+ ** and is Dfull above pressure Preq.
556
+ **--------------------------------------------------------------
557
+ */
558
+ {
559
+ Network *net = &pr->network;
560
+ Hydraul *hyd = &pr->hydraul;
561
+ Smatrix *sm = &hyd->smatrix;
562
+
563
+ int i, row;
564
+ double dp, // pressure range over which demand can vary (ft)
565
+ n, // exponent in head loss v. demand function
566
+ hloss, // head loss in supplying demand (ft)
567
+ hgrad; // gradient of demand head loss (ft/cfs)
568
+
569
+ // Get demand function parameters
570
+ if (hyd->DemandModel == DDA) return;
571
+ dp = hyd->Preq - hyd->Pmin;
572
+ n = 1.0 / hyd->Pexp;
573
+
574
+ // Examine each junction node
575
+ for (i = 1; i <= net->Njuncs; i++)
576
+ {
577
+ // Skip junctions with non-positive demands
578
+ if (hyd->FullDemand[i] <= 0.0) continue;
579
+
580
+ // Find head loss for demand outflow at node's elevation
581
+ demandheadloss(pr, i, dp, n, &hloss, &hgrad);
582
+
583
+ // Update row of solution matrix A & its r.h.s. F
584
+ if (hgrad > 0.0)
585
+ {
586
+ row = sm->Row[i];
587
+ sm->Aii[row] += 1.0 / hgrad;
588
+ sm->F[row] += (hloss + net->Node[i].El + hyd->Pmin) / hgrad;
589
+ }
590
+ }
591
+ }
592
+
593
+ void demandheadloss(Project *pr, int i, double dp, double n,
594
+ double *hloss, double *hgrad)
595
+ /*
596
+ **--------------------------------------------------------------
597
+ ** Input: i = junction index
598
+ ** dp = pressure range for demand function (ft)
599
+ ** n = exponent in head v. demand function
600
+ ** Output: hloss = pressure dependent demand head loss (ft)
601
+ ** hgrad = gradient of head loss (ft/cfs)
602
+ ** Purpose: computes head loss and its gradient for delivering
603
+ ** a pressure dependent demand flow.
604
+ **--------------------------------------------------------------
605
+ */
606
+ {
607
+ Hydraul *hyd = &pr->hydraul;
608
+
609
+ double d = hyd->DemandFlow[i];
610
+ double dfull = hyd->FullDemand[i];
611
+ double r = d / dfull;
612
+
613
+ // Evaluate inverted demand function
614
+ r = fabs(d) / dfull;
615
+ *hgrad = n * dp * pow(r, n - 1.0) / dfull;
616
+ *hloss = (*hgrad) * d / n;
617
+
618
+ // Add barrier functions
619
+ addlowerbarrier(d, hloss, hgrad);
620
+ addupperbarrier(d-dfull, hloss, hgrad);
621
+ }
622
+
623
+
624
+ void pipecoeff(Project *pr, int k)
625
+ /*
626
+ **--------------------------------------------------------------
627
+ ** Input: k = link index
628
+ ** Output: none
629
+ ** Purpose: computes P & Y coefficients for pipe k.
630
+ **
631
+ ** P = inverse head loss gradient = 1/hgrad
632
+ ** Y = flow correction term = hloss / hgrad
633
+ **--------------------------------------------------------------
634
+ */
635
+ {
636
+ Hydraul *hyd = &pr->hydraul;
637
+
638
+ double hloss, // Head loss
639
+ hgrad, // Head loss gradient
640
+ ml, // Minor loss coeff.
641
+ q, // Abs. value of flow
642
+ r; // Resistance coeff.
643
+
644
+ // For closed pipe use headloss formula: hloss = CBIG*q
645
+ if (hyd->LinkStatus[k] <= CLOSED)
646
+ {
647
+ hyd->P[k] = 1.0 / CBIG;
648
+ hyd->Y[k] = hyd->LinkFlow[k];
649
+ return;
650
+ }
651
+
652
+ // Use custom function for Darcy-Weisbach formula
653
+ if (hyd->Formflag == DW)
654
+ {
655
+ DWpipecoeff(pr, k);
656
+ return;
657
+ }
658
+
659
+ q = ABS(hyd->LinkFlow[k]);
660
+ ml = pr->network.Link[k].Km;
661
+ r = pr->network.Link[k].R;
662
+
663
+ // Friction head loss gradient
664
+ hgrad = hyd->Hexp * r * pow(q, hyd->Hexp - 1.0);
665
+
666
+ // Friction head loss:
667
+ // ... use linear function for very small gradient
668
+ if (hgrad < hyd->RQtol)
669
+ {
670
+ hgrad = hyd->RQtol / hyd->Hexp;
671
+ hloss = hgrad * q;
672
+ }
673
+ // ... otherwise use original formula
674
+ else hloss = hgrad * q / hyd->Hexp;
675
+
676
+ // Contribution of minor head loss
677
+ if (ml > 0.0)
678
+ {
679
+ hloss += ml * q * q;
680
+ hgrad += 2.0 * ml * q;
681
+ }
682
+
683
+ // Adjust head loss sign for flow direction
684
+ hloss *= SGN(hyd->LinkFlow[k]);
685
+
686
+ // P and Y coeffs.
687
+ hyd->P[k] = 1.0 / hgrad;
688
+ hyd->Y[k] = hloss / hgrad;
689
+ }
690
+
691
+
692
+ void DWpipecoeff(Project *pr, int k)
693
+ /*
694
+ **--------------------------------------------------------------
695
+ ** Input: k = link index
696
+ ** Output: none
697
+ ** Purpose: computes pipe head loss coeffs. for Darcy-Weisbach
698
+ ** formula.
699
+ **--------------------------------------------------------------
700
+ */
701
+ {
702
+ Hydraul *hyd = &pr->hydraul;
703
+ Slink *link = &pr->network.Link[k];
704
+
705
+ double q = ABS(hyd->LinkFlow[k]);
706
+ double r = link->R; // Resistance coeff.
707
+ double ml = link->Km; // Minor loss coeff.
708
+ double e = link->Kc / link->Diam; // Relative roughness
709
+ double s = hyd->Viscos * link->Diam; // Viscosity / diameter
710
+ double hloss, hgrad, f, dfdq, r1;
711
+
712
+ // Compute head loss and its derivative
713
+ // ... use Hagen-Poiseuille formula for laminar flow (Re <= 2000)
714
+ if (q <= A2 * s)
715
+ {
716
+ r = 16.0 * PI * s * r;
717
+ hloss = hyd->LinkFlow[k] * (r + ml * q);
718
+ hgrad = r + 2.0 * ml * q;
719
+ }
720
+
721
+ // ... otherwise use Darcy-Weisbach formula with friction factor
722
+ else
723
+ {
724
+ dfdq = 0.0;
725
+ f = frictionFactor(q, e, s, &dfdq);
726
+ r1 = f * r + ml;
727
+ hloss = r1 * q * hyd->LinkFlow[k];
728
+ hgrad = (2.0 * r1 * q) + (dfdq * r * q * q);
729
+ }
730
+
731
+ // Compute P and Y coefficients
732
+ hyd->P[k] = 1.0 / hgrad;
733
+ hyd->Y[k] = hloss / hgrad;
734
+ }
735
+
736
+
737
+ double frictionFactor(double q, double e, double s, double *dfdq)
738
+ /*
739
+ **--------------------------------------------------------------
740
+ ** Input: q = |pipe flow|
741
+ ** e = pipe roughness / diameter
742
+ ** s = viscosity * pipe diameter
743
+ ** Output: dfdq = derivative of friction factor w.r.t. flow
744
+ ** Returns: pipe's friction factor
745
+ ** Purpose: computes Darcy-Weisbach friction factor and its
746
+ ** derivative as a function of Reynolds Number (Re).
747
+ **--------------------------------------------------------------
748
+ */
749
+ {
750
+ double f; // friction factor
751
+ double x1, x2, x3, x4,
752
+ y1, y2, y3,
753
+ fa, fb, r;
754
+ double w = q / s; // Re*Pi/4
755
+
756
+ // For Re >= 4000 use Swamee & Jain approximation
757
+ // of the Colebrook-White Formula
758
+ if ( w >= A1 )
759
+ {
760
+ y1 = A8 / pow(w, 0.9);
761
+ y2 = e / 3.7 + y1;
762
+ y3 = A9 * log(y2);
763
+ f = 1.0 / (y3*y3);
764
+ *dfdq = 1.8 * f * y1 * A9 / y2 / y3 / q;
765
+ }
766
+
767
+ // Use interpolating polynomials developed by
768
+ // E. Dunlop for transition flow from 2000 < Re < 4000.
769
+ else
770
+ {
771
+ y2 = e / 3.7 + AB;
772
+ y3 = A9 * log(y2);
773
+ fa = 1.0 / (y3*y3);
774
+ fb = (2.0 + AC / (y2*y3)) * fa;
775
+ r = w / A2;
776
+ x1 = 7.0 * fa - fb;
777
+ x2 = 0.128 - 17.0 * fa + 2.5 * fb;
778
+ x3 = -0.128 + 13.0 * fa - (fb + fb);
779
+ x4 = 0.032 - 3.0 * fa + 0.5 *fb;
780
+ f = x1 + r * (x2 + r * (x3 + r * x4));
781
+ *dfdq = (x2 + r * (2.0 * x3 + r * 3.0 * x4)) / s / A2;
782
+ }
783
+ return f;
784
+ }
785
+
786
+
787
+ void pumpcoeff(Project *pr, int k)
788
+ /*
789
+ **--------------------------------------------------------------
790
+ ** Input: k = link index
791
+ ** Output: none
792
+ ** Purpose: computes P & Y coeffs. for pump in link k
793
+ **--------------------------------------------------------------
794
+ */
795
+ {
796
+ Hydraul *hyd = &pr->hydraul;
797
+
798
+ int p; // Pump index
799
+ double h0, // Shutoff head
800
+ q, // Abs. value of flow
801
+ r, // Flow resistance coeff.
802
+ n, // Flow exponent coeff.
803
+ setting, // Pump speed setting
804
+ hloss, // Head loss across pump
805
+ hgrad; // Head loss gradient
806
+ Spump *pump;
807
+
808
+ // Use high resistance pipe if pump closed or cannot deliver head
809
+ setting = hyd->LinkSetting[k];
810
+ if (hyd->LinkStatus[k] <= CLOSED || setting == 0.0)
811
+ {
812
+ hyd->P[k] = 1.0 / CBIG;
813
+ hyd->Y[k] = hyd->LinkFlow[k];
814
+ return;
815
+ }
816
+
817
+ // Obtain reference to pump object
818
+ q = ABS(hyd->LinkFlow[k]);
819
+ p = findpump(&pr->network, k);
820
+ pump = &pr->network.Pump[p];
821
+
822
+ // If no pump curve treat pump as an open valve
823
+ if (pump->Ptype == NOCURVE)
824
+ {
825
+ hyd->P[k] = 1.0 / CSMALL;
826
+ hyd->Y[k] = hyd->LinkFlow[k];
827
+ return;
828
+ }
829
+
830
+ // Get pump curve coefficients for custom pump curve
831
+ // (Other pump types have pre-determined coeffs.)
832
+ if (pump->Ptype == CUSTOM)
833
+ {
834
+ // Find intercept (h0) & slope (r) of pump curve
835
+ // line segment which contains speed-adjusted flow.
836
+ curvecoeff(pr, pump->Hcurve, q / setting, &h0, &r);
837
+
838
+ // Determine head loss coefficients (negative sign
839
+ // converts from pump curve's head gain to head loss)
840
+ pump->H0 = -h0;
841
+ pump->R = -r;
842
+ pump->N = 1.0;
843
+
844
+ // Compute head loss and its gradient (with speed adjustment)
845
+ hgrad = pump->R * setting ;
846
+ hloss = pump->H0 * SQR(setting) + hgrad * hyd->LinkFlow[k];
847
+ }
848
+ else
849
+ {
850
+ // Adjust head loss coefficients for pump speed
851
+ h0 = SQR(setting) * pump->H0;
852
+ n = pump->N;
853
+ if (ABS(n - 1.0) < TINY) n = 1.0;
854
+ r = pump->R * pow(setting, 2.0 - n);
855
+
856
+ // Constant HP pump
857
+ if (pump->Ptype == CONST_HP)
858
+ {
859
+ // ... compute pump curve's gradient
860
+ hgrad = -r / q / q;
861
+
862
+ // ... treat as closed link if gradient too large
863
+ if (hgrad > CBIG)
864
+ {
865
+ hyd->P[k] = 1.0 / CBIG;
866
+ hyd->Y[k] = hyd->LinkFlow[k];
867
+ return;
868
+ }
869
+
870
+ // ... treat as open valve if gradient too small
871
+ else if (hgrad < CSMALL)
872
+ {
873
+ hyd->P[k] = 1.0 / CSMALL;
874
+ hyd->Y[k] = hyd->LinkFlow[k];
875
+ return;
876
+ }
877
+
878
+ // ... otherwise compute head loss from pump curve
879
+ else
880
+ {
881
+ hloss = r / hyd->LinkFlow[k];
882
+ }
883
+ }
884
+
885
+ // Compute head loss and its gradient
886
+ // ... pump curve is nonlinear
887
+ else if (n != 1.0)
888
+ {
889
+ // ... compute pump curve's gradient
890
+ hgrad = n * r * pow(q, n - 1.0);
891
+ // ... use linear pump curve if gradient too small
892
+ if (hgrad < hyd->RQtol)
893
+ {
894
+ hgrad = hyd->RQtol;
895
+ hloss = h0 + hgrad * hyd->LinkFlow[k];
896
+ }
897
+ // ... otherwise compute head loss from pump curve
898
+ else hloss = h0 + hgrad * hyd->LinkFlow[k] / n;
899
+ }
900
+ // ... pump curve is linear
901
+ else
902
+ {
903
+ hgrad = r;
904
+ hloss = h0 + hgrad * hyd->LinkFlow[k];
905
+ }
906
+ }
907
+
908
+ // P and Y coeffs.
909
+ hyd->P[k] = 1.0 / hgrad;
910
+ hyd->Y[k] = hloss / hgrad;
911
+ }
912
+
913
+
914
+ void curvecoeff(Project *pr, int i, double q, double *h0, double *r)
915
+ /*
916
+ **-------------------------------------------------------------------
917
+ ** Input: i = curve index
918
+ ** q = flow rate
919
+ ** Output: *h0 = head at zero flow (y-intercept)
920
+ ** *r = dHead/dFlow (slope)
921
+ ** Purpose: computes intercept and slope of head v. flow curve
922
+ ** at current flow.
923
+ **-------------------------------------------------------------------
924
+ */
925
+ {
926
+ int k1, k2, npts;
927
+ double *x, *y;
928
+ Scurve *curve;
929
+
930
+ // Remember that curve is stored in untransformed units
931
+ q *= pr->Ucf[FLOW];
932
+ curve = &pr->network.Curve[i];
933
+ x = curve->X; // x = flow
934
+ y = curve->Y; // y = head
935
+ npts = curve->Npts;
936
+
937
+ // Find linear segment of curve that brackets flow q
938
+ k2 = 0;
939
+ while (k2 < npts && x[k2] < q) k2++;
940
+ if (k2 == 0) k2++;
941
+ else if (k2 == npts) k2--;
942
+ k1 = k2 - 1;
943
+
944
+ // Compute slope and intercept of this segment
945
+ *r = (y[k2] - y[k1]) / (x[k2] - x[k1]);
946
+ *h0 = y[k1] - (*r)*x[k1];
947
+
948
+ // Convert units
949
+ *h0 = (*h0) / pr->Ucf[HEAD];
950
+ *r = (*r) * pr->Ucf[FLOW] / pr->Ucf[HEAD];
951
+ }
952
+
953
+
954
+ void gpvcoeff(Project *pr, int k)
955
+ /*
956
+ **--------------------------------------------------------------
957
+ ** Input: k = link index
958
+ ** Output: none
959
+ ** Purpose: computes P & Y coeffs. for general purpose valve
960
+ **--------------------------------------------------------------
961
+ */
962
+ {
963
+ int i;
964
+ double h0, // Intercept of head loss curve segment
965
+ r, // Slope of head loss curve segment
966
+ q; // Abs. value of flow
967
+
968
+ Hydraul *hyd = &pr->hydraul;
969
+
970
+ // Treat as a pipe if valve closed
971
+ if (hyd->LinkStatus[k] == CLOSED) valvecoeff(pr, k);
972
+
973
+ // Otherwise utilize segment of head loss curve
974
+ // bracketing current flow (curve index is stored
975
+ // in valve's setting)
976
+ else
977
+ {
978
+ // Index of valve's head loss curve
979
+ i = (int)ROUND(hyd->LinkSetting[k]);
980
+
981
+ // Adjusted flow rate
982
+ q = ABS(hyd->LinkFlow[k]);
983
+ q = MAX(q, TINY);
984
+
985
+ // Intercept and slope of curve segment containing q
986
+ curvecoeff(pr, i, q, &h0, &r);
987
+ r = MAX(r, TINY);
988
+
989
+ // Resulting P and Y coeffs.
990
+ hyd->P[k] = 1.0 / r;
991
+ hyd->Y[k] = (h0 / r + q) * SGN(hyd->LinkFlow[k]);
992
+ }
993
+ }
994
+
995
+
996
+ void pbvcoeff(Project *pr, int k)
997
+ /*
998
+ **--------------------------------------------------------------
999
+ ** Input: k = link index
1000
+ ** Output: none
1001
+ ** Purpose: computes P & Y coeffs. for pressure breaker valve
1002
+ **--------------------------------------------------------------
1003
+ */
1004
+ {
1005
+ Hydraul *hyd = &pr->hydraul;
1006
+ Slink *link = &pr->network.Link[k];
1007
+
1008
+ // If valve fixed OPEN or CLOSED then treat as a pipe
1009
+ if (hyd->LinkSetting[k] == MISSING || hyd->LinkSetting[k] == 0.0)
1010
+ {
1011
+ valvecoeff(pr, k);
1012
+ }
1013
+
1014
+ // If valve is active
1015
+ else
1016
+ {
1017
+ // Treat as a pipe if minor loss > valve setting
1018
+ if (link->Km * SQR(hyd->LinkFlow[k]) > hyd->LinkSetting[k])
1019
+ {
1020
+ valvecoeff(pr, k);
1021
+ }
1022
+ // Otherwise force headloss across valve to be equal to setting
1023
+ else
1024
+ {
1025
+ hyd->P[k] = CBIG;
1026
+ hyd->Y[k] = hyd->LinkSetting[k] * CBIG;
1027
+ }
1028
+ }
1029
+ }
1030
+
1031
+
1032
+ void tcvcoeff(Project *pr, int k)
1033
+ /*
1034
+ **--------------------------------------------------------------
1035
+ ** Input: k = link index
1036
+ ** Output: none
1037
+ ** Purpose: computes P & Y coeffs. for throttle control valve
1038
+ **--------------------------------------------------------------
1039
+ */
1040
+ {
1041
+ double km;
1042
+ Hydraul *hyd = &pr->hydraul;
1043
+ Slink *link = &pr->network.Link[k];
1044
+
1045
+ // Save original loss coeff. for open valve
1046
+ km = link->Km;
1047
+
1048
+ // If valve not fixed OPEN or CLOSED, compute its loss coeff.
1049
+ if (hyd->LinkSetting[k] != MISSING)
1050
+ {
1051
+ link->Km = 0.02517 * hyd->LinkSetting[k] / (SQR(link->Diam)*SQR(link->Diam));
1052
+ }
1053
+
1054
+ // Then apply usual valve formula
1055
+ valvecoeff(pr, k);
1056
+
1057
+ // Restore original loss coeff.
1058
+ link->Km = km;
1059
+ }
1060
+
1061
+
1062
+ void pcvcoeff(Project *pr, int k)
1063
+ /*
1064
+ **--------------------------------------------------------------
1065
+ ** Input: k = link index
1066
+ ** Output: none
1067
+ ** Purpose: computes P & Y coeffs. for positional control valve
1068
+ **--------------------------------------------------------------
1069
+ */
1070
+ {
1071
+ double km;
1072
+ Hydraul *hyd = &pr->hydraul;
1073
+ Slink *link = &pr->network.Link[k];
1074
+
1075
+ // Save original loss coeff. for open valve
1076
+ km = link->Km;
1077
+
1078
+ // If valve not fixed OPEN or CLOSED, compute its loss coeff.
1079
+ if (hyd->LinkSetting[k] != MISSING)
1080
+ {
1081
+ link->Km = link->R;
1082
+ }
1083
+
1084
+ // Then apply usual valve formula
1085
+ valvecoeff(pr, k);
1086
+
1087
+ // Restore original loss coeff.
1088
+ link->Km = km;
1089
+ }
1090
+
1091
+
1092
+ void prvcoeff(Project *pr, int k, int n1, int n2)
1093
+ /*
1094
+ **--------------------------------------------------------------
1095
+ ** Input: k = link index
1096
+ ** n1 = upstream node of valve
1097
+ ** n2 = downstream node of valve
1098
+ ** Output: none
1099
+ ** Purpose: computes solution matrix coeffs. for pressure
1100
+ ** reducing valves
1101
+ **--------------------------------------------------------------
1102
+ */
1103
+ {
1104
+ Hydraul *hyd = &pr->hydraul;
1105
+ Smatrix *sm = &hyd->smatrix;
1106
+
1107
+ int i, j; // Rows of solution matrix
1108
+ double hset; // Valve head setting
1109
+
1110
+ i = sm->Row[n1]; // Matrix rows of nodes
1111
+ j = sm->Row[n2];
1112
+ hset = pr->network.Node[n2].El +
1113
+ hyd->LinkSetting[k]; // Valve setting
1114
+
1115
+ if (hyd->LinkStatus[k] == ACTIVE)
1116
+ {
1117
+
1118
+ // Set coeffs. to force head at downstream
1119
+ // node equal to valve setting & force flow
1120
+ // to equal to flow excess at downstream node.
1121
+
1122
+ hyd->P[k] = 0.0;
1123
+ hyd->Y[k] = hyd->LinkFlow[k] + hyd->Xflow[n2]; // Force flow balance
1124
+ sm->F[j] += (hset * CBIG); // Force head = hset
1125
+ sm->Aii[j] += CBIG; // at downstream node
1126
+ if (hyd->Xflow[n2] < 0.0)
1127
+ {
1128
+ sm->F[i] += hyd->Xflow[n2];
1129
+ }
1130
+ return;
1131
+ }
1132
+
1133
+ // For OPEN, CLOSED, or XPRESSURE valve
1134
+ // compute matrix coeffs. using the valvecoeff() function.
1135
+
1136
+ valvecoeff(pr, k);
1137
+ sm->Aij[sm->Ndx[k]] -= hyd->P[k];
1138
+ sm->Aii[i] += hyd->P[k];
1139
+ sm->Aii[j] += hyd->P[k];
1140
+ sm->F[i] += (hyd->Y[k] - hyd->LinkFlow[k]);
1141
+ sm->F[j] -= (hyd->Y[k] - hyd->LinkFlow[k]);
1142
+ }
1143
+
1144
+
1145
+ void psvcoeff(Project *pr, int k, int n1, int n2)
1146
+ /*
1147
+ **--------------------------------------------------------------
1148
+ ** Input: k = link index
1149
+ ** n1 = upstream node of valve
1150
+ ** n2 = downstream node of valve
1151
+ ** Output: none
1152
+ ** Purpose: computes solution matrix coeffs. for pressure
1153
+ ** sustaining valve
1154
+ **--------------------------------------------------------------
1155
+ */
1156
+ {
1157
+ Hydraul *hyd = &pr->hydraul;
1158
+ Smatrix *sm = &hyd->smatrix;
1159
+
1160
+ int i, j; // Rows of solution matrix
1161
+ double hset; // Valve head setting
1162
+
1163
+ i = sm->Row[n1]; // Matrix rows of nodes
1164
+ j = sm->Row[n2];
1165
+ hset = pr->network.Node[n1].El +
1166
+ hyd->LinkSetting[k]; // Valve setting
1167
+
1168
+ if (hyd->LinkStatus[k] == ACTIVE)
1169
+ {
1170
+ // Set coeffs. to force head at upstream
1171
+ // node equal to valve setting & force flow
1172
+ // equal to flow excess at upstream node.
1173
+
1174
+ hyd->P[k] = 0.0;
1175
+ hyd->Y[k] = hyd->LinkFlow[k] - hyd->Xflow[n1]; // Force flow balance
1176
+ sm->F[i] += (hset * CBIG); // Force head = hset
1177
+ sm->Aii[i] += CBIG; // at upstream node
1178
+ if (hyd->Xflow[n1] > 0.0)
1179
+ {
1180
+ sm->F[j] += hyd->Xflow[n1];
1181
+ }
1182
+ sm->Aij[sm->Ndx[k]] -= 1.0 / CBIG; // Preserve connectivity
1183
+ sm->Aii[j] += 1.0 / CBIG;
1184
+ return;
1185
+ }
1186
+
1187
+ // For OPEN, CLOSED, or XPRESSURE valve
1188
+ // compute matrix coeffs. using the valvecoeff() function.
1189
+
1190
+ valvecoeff(pr, k);
1191
+ sm->Aij[sm->Ndx[k]] -= hyd->P[k];
1192
+ sm->Aii[i] += hyd->P[k];
1193
+ sm->Aii[j] += hyd->P[k];
1194
+ sm->F[i] += (hyd->Y[k] - hyd->LinkFlow[k]);
1195
+ sm->F[j] -= (hyd->Y[k] - hyd->LinkFlow[k]);
1196
+ }
1197
+
1198
+
1199
+ void fcvcoeff(Project *pr, int k, int n1, int n2)
1200
+ /*
1201
+ **--------------------------------------------------------------
1202
+ ** Input: k = link index
1203
+ ** n1 = upstream node of valve
1204
+ ** n2 = downstream node of valve
1205
+ ** Output: none
1206
+ ** Purpose: computes solution matrix coeffs. for flow control
1207
+ ** valve
1208
+ **--------------------------------------------------------------
1209
+ */
1210
+ {
1211
+ Hydraul *hyd = &pr->hydraul;
1212
+ Smatrix *sm = &hyd->smatrix;
1213
+
1214
+ int i, j; // Rows in solution matrix
1215
+ double q; // Valve flow setting
1216
+
1217
+ q = hyd->LinkSetting[k];
1218
+ i = sm->Row[n1];
1219
+ j = sm->Row[n2];
1220
+
1221
+ // If valve active, break network at valve and treat
1222
+ // flow setting as external demand at upstream node
1223
+ // and external supply at downstream node.
1224
+
1225
+ if (hyd->LinkStatus[k] == ACTIVE)
1226
+ {
1227
+ hyd->Xflow[n1] -= q;
1228
+ hyd->Xflow[n2] += q;
1229
+ hyd->Y[k] = hyd->LinkFlow[k] - q;
1230
+ sm->F[i] -= q;
1231
+ sm->F[j] += q;
1232
+ hyd->P[k] = 1.0 / CBIG;
1233
+ sm->Aij[sm->Ndx[k]] -= hyd->P[k];
1234
+ sm->Aii[i] += hyd->P[k];
1235
+ sm->Aii[j] += hyd->P[k];
1236
+ }
1237
+
1238
+ // Otherwise treat valve as an open pipe
1239
+
1240
+ else
1241
+ {
1242
+ valvecoeff(pr, k);
1243
+ sm->Aij[sm->Ndx[k]] -= hyd->P[k];
1244
+ sm->Aii[i] += hyd->P[k];
1245
+ sm->Aii[j] += hyd->P[k];
1246
+ sm->F[i] += (hyd->Y[k] - hyd->LinkFlow[k]);
1247
+ sm->F[j] -= (hyd->Y[k] - hyd->LinkFlow[k]);
1248
+ }
1249
+ }
1250
+
1251
+
1252
+ void valvecoeff(Project *pr, int k)
1253
+ /*
1254
+ **--------------------------------------------------------------
1255
+ ** Input: k = link index
1256
+ ** Output: none
1257
+ ** Purpose: computes solution matrix coeffs. for a completely
1258
+ ** open, closed, or throttled control valve.
1259
+ **--------------------------------------------------------------
1260
+ */
1261
+ {
1262
+ Hydraul *hyd = &pr->hydraul;
1263
+ Slink *link = &pr->network.Link[k];
1264
+
1265
+ double flow, q, hloss, hgrad;
1266
+
1267
+ flow = hyd->LinkFlow[k];
1268
+
1269
+ // Valve is closed. Use a very small matrix coeff.
1270
+ if (hyd->LinkStatus[k] <= CLOSED)
1271
+ {
1272
+ hyd->P[k] = 1.0 / CBIG;
1273
+ hyd->Y[k] = flow;
1274
+ return;
1275
+ }
1276
+
1277
+ // Account for any minor headloss through the valve
1278
+ if (link->Km > 0.0)
1279
+ {
1280
+ q = fabs(flow);
1281
+ hgrad = 2.0 * link->Km * q;
1282
+
1283
+ // Guard against too small a head loss gradient
1284
+ if (hgrad < hyd->RQtol)
1285
+ {
1286
+ hgrad = hyd->RQtol / 2.0;
1287
+ hloss = flow * hgrad;
1288
+ }
1289
+ else hloss = flow * hgrad / 2.0;
1290
+
1291
+ // P and Y coeffs.
1292
+ hyd->P[k] = 1.0 / hgrad;
1293
+ hyd->Y[k] = hloss / hgrad;
1294
+ }
1295
+
1296
+ // If no minor loss coeff. specified use a
1297
+ // low resistance linear head loss relation
1298
+ else
1299
+ {
1300
+ hyd->P[k] = 1.0 / CSMALL;
1301
+ hyd->Y[k] = flow;
1302
+ }
1303
+ }