epanet-plus 0.0.1__cp313-cp313-macosx_10_13_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-313-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/leakage.c
ADDED
|
@@ -0,0 +1,527 @@
|
|
|
1
|
+
/*
|
|
2
|
+
******************************************************************************
|
|
3
|
+
Project: OWA EPANET
|
|
4
|
+
Version: 2.3
|
|
5
|
+
Module: leakage.c
|
|
6
|
+
Description: models additional nodal demands due to pipe leaks.
|
|
7
|
+
Authors: see AUTHORS
|
|
8
|
+
Copyright: see AUTHORS
|
|
9
|
+
License: see LICENSE
|
|
10
|
+
Last Updated: 06/14/2024
|
|
11
|
+
******************************************************************************
|
|
12
|
+
*/
|
|
13
|
+
/*
|
|
14
|
+
This module uses the FAVAD (Fixed and Variable Discharge) equation to model
|
|
15
|
+
leaky pipes:
|
|
16
|
+
|
|
17
|
+
Q = Co * L * (Ao + m * H) * sqrt(H)
|
|
18
|
+
|
|
19
|
+
where Q = pipe leak flow rate, Co = an orifice coefficient (= 0.6*sqrt(2g)),
|
|
20
|
+
L = pipe length, Ao = initial area of leak per unit of pipe length,
|
|
21
|
+
m = change in leak area per unit of pressure head, and H = pressure head.
|
|
22
|
+
|
|
23
|
+
The inverted form of this equation is used to model the leakage demand from
|
|
24
|
+
a pipe's end node using a pair of equivalent emitters as follows:
|
|
25
|
+
|
|
26
|
+
H = Cfa * Qfa^2
|
|
27
|
+
H = Cva * Qva^(2/3)
|
|
28
|
+
|
|
29
|
+
where Qfa = fixed area node leakage rate, Qva = variable area node leakage rate,
|
|
30
|
+
Cfa = 1 / SUM(Co*(L/2)*Ao)^2, Cva = 1 / SUM(Co*(L/2)*m)^2/3, and
|
|
31
|
+
SUM(x) is the summation of x over all pipes connected to the node.
|
|
32
|
+
|
|
33
|
+
In implementing this model, the pipe property "LeakArea" represents Ao in
|
|
34
|
+
sq. mm per 100 units of pipe length and "LeakExpan" represents m in sq. mm
|
|
35
|
+
per unit of pressure head.
|
|
36
|
+
|
|
37
|
+
*/
|
|
38
|
+
#include <stdlib.h>
|
|
39
|
+
#include <math.h>
|
|
40
|
+
|
|
41
|
+
#include "types.h"
|
|
42
|
+
#include "funcs.h"
|
|
43
|
+
|
|
44
|
+
// Exported functions (declared in funcs.h)
|
|
45
|
+
//int openleakage(Project *);
|
|
46
|
+
//void closeleakage(Project *);
|
|
47
|
+
//double findlinkleakage(Project *, int);
|
|
48
|
+
//void leakagecoeffs(Project *);
|
|
49
|
+
//double leakageflowchange(Project *, int);
|
|
50
|
+
//int leakagehasconverged(Project *);
|
|
51
|
+
|
|
52
|
+
// Local functions
|
|
53
|
+
static int check_for_leakage(Project *pr);
|
|
54
|
+
static int create_leakage_objects(Project *pr);
|
|
55
|
+
static void convert_pipe_to_node_leakage(Project *pr);
|
|
56
|
+
static void init_node_leakage(Project *pr);
|
|
57
|
+
static int leakage_headloss(Project* pr, int i, double *hfa,
|
|
58
|
+
double *gfa, double *hva, double *gva);
|
|
59
|
+
static void eval_leak_headloss(double q, double c,
|
|
60
|
+
double n, double *hloss, double *hgrad);
|
|
61
|
+
static void add_lower_barrier(double q, double *hloss, double *hgrad);
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
int openleakage(Project *pr)
|
|
65
|
+
/*-------------------------------------------------------------
|
|
66
|
+
** Input: none
|
|
67
|
+
** Output: returns an error code
|
|
68
|
+
** Purpose: opens the pipe leakage modeling system
|
|
69
|
+
**-------------------------------------------------------------
|
|
70
|
+
*/
|
|
71
|
+
{
|
|
72
|
+
Hydraul *hyd = &pr->hydraul;
|
|
73
|
+
|
|
74
|
+
int err;
|
|
75
|
+
|
|
76
|
+
// Check if project includes leakage
|
|
77
|
+
closeleakage(pr);
|
|
78
|
+
hyd->HasLeakage = check_for_leakage(pr);
|
|
79
|
+
if (!hyd->HasLeakage) return 0;
|
|
80
|
+
|
|
81
|
+
// Allocate memory for leakage data objects
|
|
82
|
+
err = create_leakage_objects(pr);
|
|
83
|
+
if (err > 0) return err;
|
|
84
|
+
|
|
85
|
+
// Convert pipe leakage coeffs. to node coeffs.
|
|
86
|
+
convert_pipe_to_node_leakage(pr);
|
|
87
|
+
init_node_leakage(pr);
|
|
88
|
+
return 0;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
int check_for_leakage(Project *pr)
|
|
93
|
+
/*-------------------------------------------------------------
|
|
94
|
+
** Input: none
|
|
95
|
+
** Output: returns TRUE if any pipes can leak, FALSE otherwise
|
|
96
|
+
** Purpose: checks if any pipes can leak.
|
|
97
|
+
**-------------------------------------------------------------
|
|
98
|
+
*/
|
|
99
|
+
{
|
|
100
|
+
Network *net = &pr->network;
|
|
101
|
+
int i;
|
|
102
|
+
Slink *link;
|
|
103
|
+
|
|
104
|
+
for (i = 1; i <= net->Nlinks; i++)
|
|
105
|
+
{
|
|
106
|
+
// Only pipes have leakage
|
|
107
|
+
link = &net->Link[i];
|
|
108
|
+
if (link->Type > PIPE) continue;
|
|
109
|
+
if (link->LeakArea > 0.0 || link->LeakExpan > 0.0) return TRUE;
|
|
110
|
+
}
|
|
111
|
+
return FALSE;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
int create_leakage_objects(Project *pr)
|
|
116
|
+
/*-------------------------------------------------------------
|
|
117
|
+
** Input: none
|
|
118
|
+
** Output: returns an error code
|
|
119
|
+
** Purpose: allocates an array of node leakage objects.
|
|
120
|
+
**-------------------------------------------------------------
|
|
121
|
+
*/
|
|
122
|
+
{
|
|
123
|
+
Network *net = &pr->network;
|
|
124
|
+
Hydraul *hyd = &pr->hydraul;
|
|
125
|
+
int i;
|
|
126
|
+
|
|
127
|
+
hyd->Leakage = (Sleakage *)calloc(net->Njuncs + 1, sizeof(Sleakage));
|
|
128
|
+
if (hyd->Leakage == NULL) return 101;
|
|
129
|
+
for (i = 1; i <= net->Njuncs; i++)
|
|
130
|
+
{
|
|
131
|
+
hyd->Leakage[i].cfa = 0.0;
|
|
132
|
+
hyd->Leakage[i].cva = 0.0;
|
|
133
|
+
hyd->Leakage[i].qfa = 0.0;
|
|
134
|
+
hyd->Leakage[i].qva = 0.0;
|
|
135
|
+
}
|
|
136
|
+
return 0;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
void convert_pipe_to_node_leakage(Project *pr)
|
|
140
|
+
/*-------------------------------------------------------------
|
|
141
|
+
** Input: none
|
|
142
|
+
** Output: none
|
|
143
|
+
** Purpose: converts pipe leakage parameters into node leakage
|
|
144
|
+
** coefficients.
|
|
145
|
+
**-------------------------------------------------------------
|
|
146
|
+
*/
|
|
147
|
+
{
|
|
148
|
+
Network *net = &pr->network;
|
|
149
|
+
Hydraul *hyd = &pr->hydraul;
|
|
150
|
+
|
|
151
|
+
int i;
|
|
152
|
+
double c_area, c_expan, c_orif, len;
|
|
153
|
+
Slink *link;
|
|
154
|
+
Snode *node1;
|
|
155
|
+
Snode *node2;
|
|
156
|
+
|
|
157
|
+
// Orifice coeff. with conversion from sq. mm to sq. m
|
|
158
|
+
c_orif = 4.8149866 * 1.e-6;
|
|
159
|
+
|
|
160
|
+
// Examine each link
|
|
161
|
+
for (i = 1; i <= net->Nlinks; i++)
|
|
162
|
+
{
|
|
163
|
+
// Only pipes have leakage
|
|
164
|
+
link = &net->Link[i];
|
|
165
|
+
if (link->Type > PIPE) continue;
|
|
166
|
+
|
|
167
|
+
// Ignore leakage in a pipe connecting two tanks or
|
|
168
|
+
// reservoirs (since those nodes don't have demands)
|
|
169
|
+
node1 = &net->Node[link->N1];
|
|
170
|
+
node2 = &net->Node[link->N2];
|
|
171
|
+
if (node1->Type != JUNCTION && node2->Type != JUNCTION) continue;
|
|
172
|
+
|
|
173
|
+
// Get pipe's fixed and variable area leak coeffs.
|
|
174
|
+
if (link->LeakArea == 0.0 && link->LeakExpan == 0.0) continue;
|
|
175
|
+
c_area = c_orif * link->LeakArea / SQR(MperFT);
|
|
176
|
+
c_expan = c_orif * link->LeakExpan;
|
|
177
|
+
|
|
178
|
+
// Adjust for number of 100-ft pipe sections
|
|
179
|
+
len = link->Len * pr->Ucf[LENGTH] / 100.;
|
|
180
|
+
if (node1->Type == JUNCTION && node2->Type == JUNCTION)
|
|
181
|
+
{
|
|
182
|
+
len *= 0.5;
|
|
183
|
+
}
|
|
184
|
+
c_area *= len;
|
|
185
|
+
c_expan *= len;
|
|
186
|
+
|
|
187
|
+
// Add these coeffs. to pipe's end nodes
|
|
188
|
+
if (node1->Type == JUNCTION)
|
|
189
|
+
{
|
|
190
|
+
hyd->Leakage[link->N1].cfa += c_area;
|
|
191
|
+
hyd->Leakage[link->N1].cva += c_expan;
|
|
192
|
+
}
|
|
193
|
+
if (node2->Type == JUNCTION)
|
|
194
|
+
{
|
|
195
|
+
hyd->Leakage[link->N2].cfa += c_area;
|
|
196
|
+
hyd->Leakage[link->N2].cva += c_expan;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
void init_node_leakage(Project *pr)
|
|
202
|
+
/*-------------------------------------------------------------
|
|
203
|
+
** Input: none
|
|
204
|
+
** Output: none
|
|
205
|
+
** Purpose: initializes node leakage coeffs. and flows.
|
|
206
|
+
**-------------------------------------------------------------
|
|
207
|
+
*/
|
|
208
|
+
{
|
|
209
|
+
Network *net = &pr->network;
|
|
210
|
+
Hydraul *hyd = &pr->hydraul;
|
|
211
|
+
|
|
212
|
+
int i;
|
|
213
|
+
double c_area, c_expan;
|
|
214
|
+
|
|
215
|
+
for (i = 1; i <= net->Njuncs; i++)
|
|
216
|
+
{
|
|
217
|
+
// Coeff. for fixed area leakage
|
|
218
|
+
c_area = hyd->Leakage[i].cfa;
|
|
219
|
+
if (c_area > 0.0)
|
|
220
|
+
hyd->Leakage[i].cfa = 1.0 / (c_area * c_area);
|
|
221
|
+
else
|
|
222
|
+
hyd->Leakage[i].cfa = 0.0;
|
|
223
|
+
|
|
224
|
+
// Coeff. for variable area leakage
|
|
225
|
+
c_expan = hyd->Leakage[i].cva;
|
|
226
|
+
if (c_expan > 0.0)
|
|
227
|
+
hyd->Leakage[i].cva = 1.0 / pow(c_expan, 2./3.);
|
|
228
|
+
else
|
|
229
|
+
hyd->Leakage[i].cva = 0.0;
|
|
230
|
+
|
|
231
|
+
// Initialize leakage flow to a non-zero value (as required by
|
|
232
|
+
// the hydraulic solver)
|
|
233
|
+
if (hyd->Leakage[i].cfa > 0.0)
|
|
234
|
+
hyd->Leakage[i].qfa = 0.001;
|
|
235
|
+
if (hyd->Leakage[i].cva > 0.0)
|
|
236
|
+
hyd->Leakage[i].qva = 0.001;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
void closeleakage(Project *pr)
|
|
241
|
+
/*-------------------------------------------------------------
|
|
242
|
+
** Input: none
|
|
243
|
+
** Output: none
|
|
244
|
+
** Purpose: frees memory for nodal leakage objects.
|
|
245
|
+
**-------------------------------------------------------------
|
|
246
|
+
*/
|
|
247
|
+
{
|
|
248
|
+
Hydraul *hyd = &pr->hydraul;
|
|
249
|
+
if (hyd->Leakage) free(hyd->Leakage);
|
|
250
|
+
hyd->Leakage = NULL;
|
|
251
|
+
hyd->HasLeakage = FALSE;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
double findlinkleakage(Project *pr, int i)
|
|
255
|
+
/*-------------------------------------------------------------
|
|
256
|
+
** Input: i = link index
|
|
257
|
+
** Output: returns link leakage flow (cfs)
|
|
258
|
+
** Purpose: computes leakage flow from link i at current
|
|
259
|
+
** hydraulic solution.
|
|
260
|
+
**-------------------------------------------------------------
|
|
261
|
+
*/
|
|
262
|
+
{
|
|
263
|
+
Network *net = &pr->network;
|
|
264
|
+
Hydraul *hyd = &pr->hydraul;
|
|
265
|
+
Smatrix *sm = &hyd->smatrix;
|
|
266
|
+
Slink *link = &net->Link[i];
|
|
267
|
+
|
|
268
|
+
int n1, n2;
|
|
269
|
+
double h1, h2, hsqrt, a, m, c, len, q1, q2;
|
|
270
|
+
|
|
271
|
+
// Only pipes can leak
|
|
272
|
+
link = &net->Link[i];
|
|
273
|
+
if (link->Type > PIPE) return 0.0;
|
|
274
|
+
|
|
275
|
+
// No leakage if area & expansion are 0
|
|
276
|
+
if (link->LeakArea == 0.0 && link->LeakExpan == 0.0) return 0.0;
|
|
277
|
+
|
|
278
|
+
// No leakage if link's end nodes are both fixed grade
|
|
279
|
+
n1 = link->N1;
|
|
280
|
+
n2 = link->N2;
|
|
281
|
+
if (n1 > net->Njuncs && n2 > net->Njuncs) return 0.0;
|
|
282
|
+
|
|
283
|
+
// Pressure head of end nodes
|
|
284
|
+
h1 = hyd->NodeHead[n1] - net->Node[n1].El;
|
|
285
|
+
h1 = MAX(h1, 0.0);
|
|
286
|
+
h2 = hyd->NodeHead[n2] - net->Node[n2].El;
|
|
287
|
+
h2 = MAX(h2, 0.0);
|
|
288
|
+
|
|
289
|
+
// Pipe leak parameters converted to feet
|
|
290
|
+
a = link->LeakArea / SQR(MperFT);
|
|
291
|
+
m = link->LeakExpan;
|
|
292
|
+
len = link->Len * pr->Ucf[LENGTH] / 100.; // # 100 ft pipe lengths
|
|
293
|
+
c = 4.8149866 * len / 2.0 * 1.e-6;
|
|
294
|
+
|
|
295
|
+
// Leakage from 1st half of pipe connected to node n1
|
|
296
|
+
q1 = 0.0;
|
|
297
|
+
if (n1 <= net->Njuncs)
|
|
298
|
+
{
|
|
299
|
+
hsqrt = sqrt(h1);
|
|
300
|
+
q1 = c * (a + m * h1) * hsqrt;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// Leakage from 2nd half of pipe connected to node n2
|
|
304
|
+
q2 = 0.0;
|
|
305
|
+
if (n2 <= net->Njuncs)
|
|
306
|
+
{
|
|
307
|
+
hsqrt = sqrt(h2);
|
|
308
|
+
q2 = c * (a + m * h2) * hsqrt;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
// Adjust leakage flows to account for one node being fixed grade
|
|
312
|
+
if (n2 > net->Njuncs) q1 *= 2.0;
|
|
313
|
+
if (n1 > net->Njuncs) q2 *= 2.0;
|
|
314
|
+
return q1 + q2;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
void leakagecoeffs(Project *pr)
|
|
318
|
+
/*
|
|
319
|
+
**--------------------------------------------------------------
|
|
320
|
+
** Input: none
|
|
321
|
+
** Output: none
|
|
322
|
+
** Purpose: computes coeffs. of the linearized hydraulic eqns.
|
|
323
|
+
** contributed by node leakages.
|
|
324
|
+
**--------------------------------------------------------------
|
|
325
|
+
*/
|
|
326
|
+
{
|
|
327
|
+
Network *net = &pr->network;
|
|
328
|
+
Hydraul *hyd = &pr->hydraul;
|
|
329
|
+
Smatrix *sm = &hyd->smatrix;
|
|
330
|
+
|
|
331
|
+
int i, row;
|
|
332
|
+
double hfa, // head loss producing current fixed area leakage
|
|
333
|
+
gfa, // gradient of fixed area head loss
|
|
334
|
+
hva, // head loss producing current variable area leakage
|
|
335
|
+
gva; // gradient of variable area head loss
|
|
336
|
+
|
|
337
|
+
Snode* node;
|
|
338
|
+
|
|
339
|
+
for (i = 1; i <= net->Njuncs; i++)
|
|
340
|
+
{
|
|
341
|
+
// Skip junctions that don't leak
|
|
342
|
+
node = &net->Node[i];
|
|
343
|
+
if (!leakage_headloss(pr, i, &hfa, &gfa, &hva, &gva)) continue;
|
|
344
|
+
|
|
345
|
+
// Addition to matrix diagonal & r.h.s
|
|
346
|
+
row = sm->Row[i];
|
|
347
|
+
if (gfa > 0.0)
|
|
348
|
+
{
|
|
349
|
+
sm->Aii[row] += 1.0 / gfa;
|
|
350
|
+
sm->F[row] += (hfa + node->El) / gfa;
|
|
351
|
+
}
|
|
352
|
+
if (gva > 0.0)
|
|
353
|
+
{
|
|
354
|
+
sm->Aii[row] += 1.0 / gva;
|
|
355
|
+
sm->F[row] += (hva + node->El) / gva;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
// Update node's flow excess (inflow - outflow)
|
|
359
|
+
hyd->Xflow[i] -= (hyd->Leakage[i].qfa + hyd->Leakage[i].qva);
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
double leakageflowchange(Project *pr, int i)
|
|
364
|
+
/*
|
|
365
|
+
**--------------------------------------------------------------
|
|
366
|
+
** Input: i = node index
|
|
367
|
+
** Output: returns change in leakage flow rate
|
|
368
|
+
** Purpose: finds new leakage flow rate at a node after new
|
|
369
|
+
** heads are computed by the hydraulic solver.
|
|
370
|
+
**--------------------------------------------------------------
|
|
371
|
+
*/
|
|
372
|
+
{
|
|
373
|
+
Network *net = &pr->network;
|
|
374
|
+
Hydraul *hyd = &pr->hydraul;
|
|
375
|
+
|
|
376
|
+
double hfa, gfa, hva, gva; // same as defined in leakage_solvercoeffs()
|
|
377
|
+
double h, dqfa, dqva; // pressure head, change in leakage flows
|
|
378
|
+
|
|
379
|
+
// Find the head loss and gradient of the inverted leakage
|
|
380
|
+
// equation for both fixed and variable area leakage at the
|
|
381
|
+
// current leakage flow rates
|
|
382
|
+
if (!leakage_headloss(pr, i, &hfa, &gfa, &hva, &gva)) return 0.0;
|
|
383
|
+
|
|
384
|
+
// Pressure head using latest head solution
|
|
385
|
+
h = hyd->NodeHead[i] - net->Node[i].El;
|
|
386
|
+
|
|
387
|
+
// GGA flow update formula for fixed area leakage
|
|
388
|
+
dqfa = 0.0;
|
|
389
|
+
if (gfa > 0.0)
|
|
390
|
+
{
|
|
391
|
+
dqfa = (hfa - h) / gfa * hyd->RelaxFactor;
|
|
392
|
+
hyd->Leakage[i].qfa -= dqfa;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
// GGA flow update formula for variable area leakage
|
|
396
|
+
dqva = 0.0;
|
|
397
|
+
if (gva > 0.0)
|
|
398
|
+
{
|
|
399
|
+
dqva = (hva - h) / gva * hyd->RelaxFactor;
|
|
400
|
+
hyd->Leakage[i].qva -= dqva;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
// New leakage flow at the node
|
|
404
|
+
hyd->LeakageFlow[i] = hyd->Leakage[i].qfa + hyd->Leakage[i].qva;
|
|
405
|
+
return dqfa + dqva;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
int leakagehasconverged(Project *pr)
|
|
409
|
+
/*
|
|
410
|
+
**--------------------------------------------------------------
|
|
411
|
+
** Input: none
|
|
412
|
+
** Output: returns TRUE if leakage calculations converged,
|
|
413
|
+
** FALSE if not
|
|
414
|
+
** Purpose: checks if leakage calculations have converged.
|
|
415
|
+
**--------------------------------------------------------------
|
|
416
|
+
*/
|
|
417
|
+
{
|
|
418
|
+
Network *net = &pr->network;
|
|
419
|
+
Hydraul *hyd = &pr->hydraul;
|
|
420
|
+
|
|
421
|
+
int i;
|
|
422
|
+
double h, qref, qtest;
|
|
423
|
+
const double QTOL = 0.0001; // 0.0001 cfs ~= 0.005 gpm ~= 0.2 lpm)
|
|
424
|
+
|
|
425
|
+
for (i = 1; i <= net->Njuncs; i++)
|
|
426
|
+
{
|
|
427
|
+
// Skip junctions that don't leak
|
|
428
|
+
if (hyd->Leakage[i].cfa == 0 && hyd->Leakage[i].cva == 0) continue;
|
|
429
|
+
|
|
430
|
+
// Evaluate node's pressure head
|
|
431
|
+
h = hyd->NodeHead[i] - net->Node[i].El;
|
|
432
|
+
|
|
433
|
+
// Directly compute a reference leakage at this pressure head
|
|
434
|
+
qref = 0.0;
|
|
435
|
+
// Contribution from pipes with fixed area leaks
|
|
436
|
+
if (hyd->Leakage[i].cfa > 0.0)
|
|
437
|
+
qref = sqrt(h / hyd->Leakage[i].cfa);
|
|
438
|
+
// Contribution from pipes with variable area leaks
|
|
439
|
+
if (hyd->Leakage[i].cva > 0.0)
|
|
440
|
+
qref += pow((h / hyd->Leakage[i].cva), 1.5);
|
|
441
|
+
|
|
442
|
+
// Compare reference leakage to solution leakage
|
|
443
|
+
qtest = hyd->Leakage[i].qfa + hyd->Leakage[i].qva;
|
|
444
|
+
if (fabs(qref - qtest) > QTOL) return FALSE;
|
|
445
|
+
}
|
|
446
|
+
return TRUE;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
int leakage_headloss(Project* pr, int i, double *hfa, double *gfa,
|
|
450
|
+
double *hva, double *gva)
|
|
451
|
+
/*
|
|
452
|
+
**--------------------------------------------------------------
|
|
453
|
+
** Input: i = node index
|
|
454
|
+
** Output: hfa = fixed area leak head loss (ft)
|
|
455
|
+
** gfa = gradient of fixed area head loss (ft/cfs)
|
|
456
|
+
** hva = variable area leak head loss (ft)
|
|
457
|
+
** gva = gradient of variable area head loss (ft/cfs)
|
|
458
|
+
** returns TRUE if node has leakage, FALSE otherwise
|
|
459
|
+
** Purpose: finds head loss and its gradient for a node's
|
|
460
|
+
** leakage as a function of leakage flow.
|
|
461
|
+
**--------------------------------------------------------------
|
|
462
|
+
*/
|
|
463
|
+
{
|
|
464
|
+
Hydraul *hyd = &pr->hydraul;
|
|
465
|
+
|
|
466
|
+
if (hyd->Leakage[i].cfa == 0.0 && hyd->Leakage[i].cva == 0.0) return FALSE;
|
|
467
|
+
if (hyd->Leakage[i].cfa == 0.0)
|
|
468
|
+
{
|
|
469
|
+
*hfa = 0.0;
|
|
470
|
+
*gfa = 0.0;
|
|
471
|
+
}
|
|
472
|
+
else
|
|
473
|
+
eval_leak_headloss(hyd->Leakage[i].qfa, hyd->Leakage[i].cfa,
|
|
474
|
+
0.5, hfa, gfa);
|
|
475
|
+
if (hyd->Leakage[i].cva == 0.0)
|
|
476
|
+
{
|
|
477
|
+
*hva = 0.0;
|
|
478
|
+
*gva = 0.0;
|
|
479
|
+
}
|
|
480
|
+
else
|
|
481
|
+
eval_leak_headloss(hyd->Leakage[i].qva, hyd->Leakage[i].cva,
|
|
482
|
+
1.5, hva, gva);
|
|
483
|
+
return TRUE;
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
void eval_leak_headloss(double q, double c, double n,
|
|
487
|
+
double *hloss, double *hgrad)
|
|
488
|
+
/*
|
|
489
|
+
**--------------------------------------------------------------
|
|
490
|
+
** Input: q = leakage flow rate (cfs)
|
|
491
|
+
** c = leakage head loss coefficient
|
|
492
|
+
** n = leakage head loss exponent
|
|
493
|
+
** Output: hloss = leakage head loss (ft)
|
|
494
|
+
** hgrad = gradient of leakage head loss (ft/cfs)
|
|
495
|
+
** Purpose: evaluates inverted form of leakage equation to
|
|
496
|
+
** compute head loss and its gradient as a function
|
|
497
|
+
** flow.
|
|
498
|
+
**
|
|
499
|
+
** Note: Inverted leakage equation is:
|
|
500
|
+
** hloss = c * q ^ (1/n)
|
|
501
|
+
**--------------------------------------------------------------
|
|
502
|
+
*/
|
|
503
|
+
{
|
|
504
|
+
n = 1.0 / n;
|
|
505
|
+
*hgrad = n * c * pow(fabs(q), n - 1.0);
|
|
506
|
+
*hloss = (*hgrad) * q / n;
|
|
507
|
+
|
|
508
|
+
// Prevent leakage from going negative
|
|
509
|
+
add_lower_barrier(q, hloss, hgrad);
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
void add_lower_barrier(double q, double* hloss, double* hgrad)
|
|
513
|
+
/*
|
|
514
|
+
**--------------------------------------------------------------------
|
|
515
|
+
** Input: q = current flow rate
|
|
516
|
+
** Output: hloss = head loss value
|
|
517
|
+
** hgrad = head loss gradient value
|
|
518
|
+
** Purpose: adds a head loss barrier to prevent flow from falling
|
|
519
|
+
** below 0.
|
|
520
|
+
**--------------------------------------------------------------------
|
|
521
|
+
*/
|
|
522
|
+
{
|
|
523
|
+
double a = 1.e9 * q;
|
|
524
|
+
double b = sqrt(a*a + 1.e-6);
|
|
525
|
+
*hloss += (a - b) / 2.;
|
|
526
|
+
*hgrad += (1.e9 / 2.) * ( 1.0 - a / b);
|
|
527
|
+
}
|
epanet-src/mempool.c
ADDED
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/*
|
|
2
|
+
******************************************************************************
|
|
3
|
+
Project: OWA EPANET
|
|
4
|
+
Version: 2.3
|
|
5
|
+
Module: mempool.c
|
|
6
|
+
Description: a simple fast poooled memory allocation package
|
|
7
|
+
Authors: see AUTHORS
|
|
8
|
+
Copyright: see AUTHORS
|
|
9
|
+
License: see LICENSE
|
|
10
|
+
Last Updated: 08/02/2023
|
|
11
|
+
|
|
12
|
+
This module is based on code by Steve Hill in Graphics Gems III,
|
|
13
|
+
David Kirk (ed.), Academic Press, Boston, MA, 1992
|
|
14
|
+
******************************************************************************
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
#include <stdlib.h>
|
|
18
|
+
|
|
19
|
+
#include "mempool.h"
|
|
20
|
+
|
|
21
|
+
/*
|
|
22
|
+
** ALLOC_BLOCK_SIZE - adjust this size to suit your installation - it
|
|
23
|
+
** should be reasonably large otherwise you will be mallocing a lot.
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
#define ALLOC_BLOCK_SIZE 64000 /*(62*1024)*/
|
|
27
|
+
|
|
28
|
+
struct MemBlock
|
|
29
|
+
{
|
|
30
|
+
struct MemBlock *next; // Next block
|
|
31
|
+
char *block, // Start of block
|
|
32
|
+
*free, // Next free position in block
|
|
33
|
+
*end; // block + block size
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
struct Mempool
|
|
37
|
+
{
|
|
38
|
+
struct MemBlock *first;
|
|
39
|
+
struct MemBlock *current;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
static struct MemBlock* createMemBlock()
|
|
43
|
+
{
|
|
44
|
+
struct MemBlock* memBlock = malloc(sizeof(struct MemBlock));
|
|
45
|
+
if (memBlock)
|
|
46
|
+
{
|
|
47
|
+
memBlock->block = malloc(ALLOC_BLOCK_SIZE * sizeof(char));
|
|
48
|
+
if (memBlock->block == NULL)
|
|
49
|
+
{
|
|
50
|
+
free(memBlock);
|
|
51
|
+
return NULL;
|
|
52
|
+
}
|
|
53
|
+
memBlock->free = memBlock->block;
|
|
54
|
+
memBlock->next = NULL;
|
|
55
|
+
memBlock->end = memBlock->block + ALLOC_BLOCK_SIZE;
|
|
56
|
+
}
|
|
57
|
+
return memBlock;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
static void deleteMemBlock(struct MemBlock* memBlock)
|
|
62
|
+
{
|
|
63
|
+
free(memBlock->block);
|
|
64
|
+
free(memBlock);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
struct Mempool * mempool_create()
|
|
69
|
+
{
|
|
70
|
+
struct Mempool *mempool;
|
|
71
|
+
mempool = (struct Mempool *)malloc(sizeof(struct Mempool));
|
|
72
|
+
if (mempool == NULL) return NULL;
|
|
73
|
+
mempool->first = createMemBlock();
|
|
74
|
+
mempool->current = mempool->first;
|
|
75
|
+
if (mempool->first == NULL)
|
|
76
|
+
{
|
|
77
|
+
free(mempool);
|
|
78
|
+
return NULL;
|
|
79
|
+
}
|
|
80
|
+
return mempool;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
void mempool_delete(struct Mempool *mempool)
|
|
84
|
+
{
|
|
85
|
+
if (mempool == NULL) return;
|
|
86
|
+
while (mempool->first)
|
|
87
|
+
{
|
|
88
|
+
mempool->current = mempool->first->next;
|
|
89
|
+
deleteMemBlock(mempool->first);
|
|
90
|
+
mempool->first = mempool->current;
|
|
91
|
+
}
|
|
92
|
+
free(mempool);
|
|
93
|
+
mempool = NULL;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
void mempool_reset(struct Mempool *mempool)
|
|
97
|
+
{
|
|
98
|
+
mempool->current = mempool->first;
|
|
99
|
+
mempool->current->free = mempool->current->block;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
char * mempool_alloc(struct Mempool *mempool, size_t size)
|
|
104
|
+
{
|
|
105
|
+
char* ptr;
|
|
106
|
+
|
|
107
|
+
/*
|
|
108
|
+
** Align to 4 byte boundary - should be ok for most machines.
|
|
109
|
+
** Change this if your machine has weird alignment requirements.
|
|
110
|
+
*/
|
|
111
|
+
size = (size + 3) & 0xfffffffc;
|
|
112
|
+
|
|
113
|
+
if (!mempool->current) return NULL;
|
|
114
|
+
ptr = mempool->current->free;
|
|
115
|
+
mempool->current->free += size;
|
|
116
|
+
|
|
117
|
+
// Check if the current block is exhausted
|
|
118
|
+
|
|
119
|
+
if (mempool->current->free >= mempool->current->end)
|
|
120
|
+
{
|
|
121
|
+
// Is the next block already allocated?
|
|
122
|
+
|
|
123
|
+
if (mempool->current->next)
|
|
124
|
+
{
|
|
125
|
+
// re-use block
|
|
126
|
+
mempool->current->next->free = mempool->current->next->block;
|
|
127
|
+
mempool->current = mempool->current->next;
|
|
128
|
+
}
|
|
129
|
+
else
|
|
130
|
+
{
|
|
131
|
+
// extend the pool with a new block
|
|
132
|
+
mempool->current->next = createMemBlock();
|
|
133
|
+
if (!mempool->current->next) return NULL;
|
|
134
|
+
mempool->current = mempool->current->next;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// set ptr to the first location in the next block
|
|
138
|
+
|
|
139
|
+
ptr = mempool->current->free;
|
|
140
|
+
mempool->current->free += size;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Return pointer to allocated memory
|
|
144
|
+
|
|
145
|
+
return ptr;
|
|
146
|
+
}
|
epanet-src/mempool.h
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/*
|
|
2
|
+
******************************************************************************
|
|
3
|
+
Project: OWA EPANET
|
|
4
|
+
Version: 2.3
|
|
5
|
+
Module: mempool.h
|
|
6
|
+
Description: header for a simple pooled memory allocator
|
|
7
|
+
Authors: see AUTHORS
|
|
8
|
+
Copyright: see AUTHORS
|
|
9
|
+
License: see LICENSE
|
|
10
|
+
Last Updated: 11/27/2018
|
|
11
|
+
******************************************************************************
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
#ifndef MEMPOOL_H
|
|
15
|
+
#define MEMPOOL_H
|
|
16
|
+
|
|
17
|
+
struct Mempool;
|
|
18
|
+
|
|
19
|
+
struct Mempool * mempool_create();
|
|
20
|
+
void mempool_delete(struct Mempool *mempool);
|
|
21
|
+
void mempool_reset(struct Mempool *mempool);
|
|
22
|
+
char * mempool_alloc(struct Mempool *mempool, size_t size);
|
|
23
|
+
|
|
24
|
+
#endif
|