epyt-flow 0.13.1__py3-none-any.whl → 0.14.1__py3-none-any.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.
- epyt_flow/EPANET/EPANET/SRC_engines/AUTHORS +40 -8
- epyt_flow/EPANET/EPANET/SRC_engines/LICENSE +3 -3
- epyt_flow/EPANET/EPANET/SRC_engines/enumstxt.h +24 -7
- epyt_flow/EPANET/EPANET/SRC_engines/epanet.c +726 -374
- epyt_flow/EPANET/EPANET/SRC_engines/epanet2.c +128 -32
- epyt_flow/EPANET/EPANET/SRC_engines/errors.dat +7 -1
- epyt_flow/EPANET/EPANET/SRC_engines/flowbalance.c +186 -0
- epyt_flow/EPANET/EPANET/SRC_engines/funcs.h +40 -14
- epyt_flow/EPANET/EPANET/SRC_engines/hash.c +177 -177
- epyt_flow/EPANET/EPANET/SRC_engines/hash.h +28 -28
- epyt_flow/EPANET/EPANET/SRC_engines/hydcoeffs.c +192 -40
- epyt_flow/EPANET/EPANET/SRC_engines/hydraul.c +101 -46
- epyt_flow/EPANET/EPANET/SRC_engines/hydsolver.c +85 -24
- epyt_flow/EPANET/EPANET/SRC_engines/hydstatus.c +29 -63
- epyt_flow/EPANET/EPANET/SRC_engines/include/epanet2.h +70 -37
- epyt_flow/EPANET/EPANET/SRC_engines/include/epanet2_2.h +408 -234
- epyt_flow/EPANET/EPANET/SRC_engines/include/epanet2_enums.h +87 -37
- epyt_flow/EPANET/EPANET/SRC_engines/inpfile.c +153 -79
- epyt_flow/EPANET/EPANET/SRC_engines/input1.c +59 -94
- epyt_flow/EPANET/EPANET/SRC_engines/input2.c +73 -202
- epyt_flow/EPANET/EPANET/SRC_engines/input3.c +446 -351
- epyt_flow/EPANET/EPANET/SRC_engines/leakage.c +527 -0
- epyt_flow/EPANET/EPANET/SRC_engines/mempool.c +8 -4
- epyt_flow/EPANET/EPANET/SRC_engines/mempool.h +23 -23
- epyt_flow/EPANET/EPANET/SRC_engines/output.c +5 -4
- epyt_flow/EPANET/EPANET/SRC_engines/project.c +407 -75
- epyt_flow/EPANET/EPANET/SRC_engines/quality.c +12 -2
- epyt_flow/EPANET/EPANET/SRC_engines/qualreact.c +70 -13
- epyt_flow/EPANET/EPANET/SRC_engines/qualroute.c +7 -5
- epyt_flow/EPANET/EPANET/SRC_engines/report.c +88 -20
- epyt_flow/EPANET/EPANET/SRC_engines/rules.c +144 -6
- epyt_flow/EPANET/EPANET/SRC_engines/smatrix.c +19 -19
- epyt_flow/EPANET/EPANET/SRC_engines/text.h +16 -5
- epyt_flow/EPANET/EPANET/SRC_engines/types.h +73 -19
- epyt_flow/EPANET/EPANET/SRC_engines/util/cstr_helper.c +59 -0
- epyt_flow/EPANET/EPANET/SRC_engines/util/cstr_helper.h +38 -0
- epyt_flow/EPANET/EPANET/SRC_engines/util/errormanager.c +92 -0
- epyt_flow/EPANET/EPANET/SRC_engines/util/errormanager.h +39 -0
- epyt_flow/EPANET/EPANET/SRC_engines/util/filemanager.c +212 -0
- epyt_flow/EPANET/EPANET/SRC_engines/util/filemanager.h +81 -0
- epyt_flow/EPANET/EPANET/SRC_engines/validate.c +408 -0
- epyt_flow/EPANET/compile_linux.sh +1 -1
- epyt_flow/EPANET/compile_macos.sh +2 -2
- epyt_flow/VERSION +1 -1
- epyt_flow/__init__.py +1 -1
- epyt_flow/gym/scenario_control_env.py +26 -3
- epyt_flow/simulation/backend/my_epyt.py +58 -13
- epyt_flow/simulation/events/quality_events.py +6 -6
- epyt_flow/simulation/events/sensor_faults.py +24 -24
- epyt_flow/simulation/events/system_event.py +3 -3
- epyt_flow/simulation/scada/scada_data.py +10 -14
- epyt_flow/simulation/scenario_simulator.py +100 -20
- epyt_flow/topology.py +8 -1
- epyt_flow/uncertainty/model_uncertainty.py +292 -150
- epyt_flow/uncertainty/uncertainties.py +2 -2
- {epyt_flow-0.13.1.dist-info → epyt_flow-0.14.1.dist-info}/METADATA +4 -4
- {epyt_flow-0.13.1.dist-info → epyt_flow-0.14.1.dist-info}/RECORD +60 -54
- {epyt_flow-0.13.1.dist-info → epyt_flow-0.14.1.dist-info}/WHEEL +1 -1
- epyt_flow/EPANET/EPANET/SRC_engines/Readme_SRC_Engines.txt +0 -18
- epyt_flow/EPANET/EPANET/SRC_engines/epanet2.def +0 -131
- epyt_flow/EPANET/EPANET/SRC_engines/main.c +0 -93
- {epyt_flow-0.13.1.dist-info → epyt_flow-0.14.1.dist-info}/licenses/LICENSE +0 -0
- {epyt_flow-0.13.1.dist-info → epyt_flow-0.14.1.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,408 @@
|
|
|
1
|
+
/*
|
|
2
|
+
******************************************************************************
|
|
3
|
+
Project: OWA EPANET
|
|
4
|
+
Version: 2.3
|
|
5
|
+
Module: validate.c
|
|
6
|
+
Description: validates project data
|
|
7
|
+
Authors: see AUTHORS
|
|
8
|
+
Copyright: see AUTHORS
|
|
9
|
+
License: see LICENSE
|
|
10
|
+
Last Updated: 03/18/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
|
+
#include "text.h"
|
|
22
|
+
|
|
23
|
+
// Exported functions
|
|
24
|
+
int validateproject(Project *);
|
|
25
|
+
void reindextanks(Project *);
|
|
26
|
+
|
|
27
|
+
int validatetanks(Project *pr)
|
|
28
|
+
/*
|
|
29
|
+
**-------------------------------------------------------------------
|
|
30
|
+
** Input: none
|
|
31
|
+
** Output: returns 1 if successful, 0 if not
|
|
32
|
+
** Purpose: checks for valid tank levels.
|
|
33
|
+
**-------------------------------------------------------------------
|
|
34
|
+
*/
|
|
35
|
+
{
|
|
36
|
+
Network *net = &pr->network;
|
|
37
|
+
int i, j, n, result = 1, levelerr;
|
|
38
|
+
char errmsg[MAXMSG+1] = "";
|
|
39
|
+
Stank *tank;
|
|
40
|
+
Scurve *curve;
|
|
41
|
+
double elev;
|
|
42
|
+
|
|
43
|
+
for (j = 1; j <= net->Ntanks; j++)
|
|
44
|
+
{
|
|
45
|
+
tank = &net->Tank[j];
|
|
46
|
+
if (tank->A == 0.0) continue; // Skip reservoirs
|
|
47
|
+
|
|
48
|
+
// Check for valid lower/upper tank levels
|
|
49
|
+
levelerr = 0;
|
|
50
|
+
if (tank->H0 > tank->Hmax ||
|
|
51
|
+
tank->Hmin > tank->Hmax ||
|
|
52
|
+
tank->H0 < tank->Hmin
|
|
53
|
+
) levelerr = 1;
|
|
54
|
+
|
|
55
|
+
// Check that tank heights are within volume curve
|
|
56
|
+
elev = net->Node[tank->Node].El;
|
|
57
|
+
i = tank->Vcurve;
|
|
58
|
+
if (i > 0)
|
|
59
|
+
{
|
|
60
|
+
curve = &net->Curve[i];
|
|
61
|
+
n = curve->Npts - 1;
|
|
62
|
+
if ((tank->Hmin - elev) * pr->Ucf[ELEV] < curve->X[0] - TINY ||
|
|
63
|
+
(tank->Hmax - elev) * pr->Ucf[ELEV] > curve->X[n] + TINY)
|
|
64
|
+
{
|
|
65
|
+
levelerr = 1;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Report error in levels if found
|
|
70
|
+
if (levelerr)
|
|
71
|
+
{
|
|
72
|
+
sprintf(pr->Msg, "Error 225: %s node %s", geterrmsg(225, errmsg),
|
|
73
|
+
net->Node[tank->Node].ID);
|
|
74
|
+
writeline(pr, pr->Msg);
|
|
75
|
+
result = 0;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return result;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
int validatepatterns(Project *pr)
|
|
82
|
+
/*
|
|
83
|
+
**-------------------------------------------------------------------
|
|
84
|
+
** Input: none
|
|
85
|
+
** Output: returns 1 if successful, 0 if not
|
|
86
|
+
** Purpose: checks if time patterns have data.
|
|
87
|
+
**-------------------------------------------------------------------
|
|
88
|
+
*/
|
|
89
|
+
{
|
|
90
|
+
Network *net = &pr->network;
|
|
91
|
+
int j, result = 1;
|
|
92
|
+
char errmsg[MAXMSG+1] = "";
|
|
93
|
+
|
|
94
|
+
if (pr->network.Pattern != NULL)
|
|
95
|
+
{
|
|
96
|
+
for (j = 0; j <= pr->network.Npats; j++)
|
|
97
|
+
{
|
|
98
|
+
if (pr->network.Pattern[j].Length == 0)
|
|
99
|
+
{
|
|
100
|
+
sprintf(pr->Msg, "Error 232: %s %s", geterrmsg(232, errmsg),
|
|
101
|
+
pr->network.Pattern[j].ID);
|
|
102
|
+
writeline(pr, pr->Msg);
|
|
103
|
+
result = 0;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return result;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
int validatecurves(Project *pr)
|
|
111
|
+
/*
|
|
112
|
+
**-------------------------------------------------------------------
|
|
113
|
+
** Input: none
|
|
114
|
+
** Output: returns 1 if successful, 0 if not
|
|
115
|
+
** Purpose: checks if data curves have data.
|
|
116
|
+
**-------------------------------------------------------------------
|
|
117
|
+
*/
|
|
118
|
+
{
|
|
119
|
+
int i, j, npts, result = 1;
|
|
120
|
+
char errmsg[MAXMSG+1] = "";
|
|
121
|
+
Scurve *curve;
|
|
122
|
+
|
|
123
|
+
if (pr->network.Curve != NULL)
|
|
124
|
+
{
|
|
125
|
+
for (j = 1; j <= pr->network.Ncurves; j++)
|
|
126
|
+
{
|
|
127
|
+
// Check that curve has data
|
|
128
|
+
curve = &pr->network.Curve[j];
|
|
129
|
+
npts = curve->Npts;
|
|
130
|
+
if (npts == 0)
|
|
131
|
+
{
|
|
132
|
+
sprintf(pr->Msg, "Error 231: %s %s", geterrmsg(231, errmsg),
|
|
133
|
+
curve->ID);
|
|
134
|
+
writeline(pr, pr->Msg);
|
|
135
|
+
result = 0;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Check that x values are increasing
|
|
139
|
+
for (i = 1; i < npts; i++)
|
|
140
|
+
{
|
|
141
|
+
if (curve->X[i-1] >= curve->X[i])
|
|
142
|
+
{
|
|
143
|
+
sprintf(pr->Msg, "Error 230: %s %s", geterrmsg(230, errmsg),
|
|
144
|
+
curve->ID);
|
|
145
|
+
writeline(pr, pr->Msg);
|
|
146
|
+
result = 0;
|
|
147
|
+
break;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
return result;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
int powerfuncpump(double h0, double h1, double h2, double q1, double q2,
|
|
156
|
+
double *a, double *b, double *c)
|
|
157
|
+
/*
|
|
158
|
+
**---------------------------------------------------------
|
|
159
|
+
** Input: h0 = shutoff head
|
|
160
|
+
** h1 = design head
|
|
161
|
+
** h2 = head at max. flow
|
|
162
|
+
** q1 = design flow
|
|
163
|
+
** q2 = max. flow
|
|
164
|
+
** Output: *a, *b, *c = pump curve coeffs. (H = a-bQ^c),
|
|
165
|
+
** Returns 1 if successful, 0 otherwise.
|
|
166
|
+
** Purpose: computes coeffs. for a power function pump curve
|
|
167
|
+
**----------------------------------------------------------
|
|
168
|
+
*/
|
|
169
|
+
{
|
|
170
|
+
double h4, h5;
|
|
171
|
+
|
|
172
|
+
if (h0 < TINY || h0 - h1 < TINY || h1 - h2 < TINY ||
|
|
173
|
+
q1 < TINY || q2 - q1 < TINY
|
|
174
|
+
) return 0;
|
|
175
|
+
*a = h0;
|
|
176
|
+
h4 = h0 - h1;
|
|
177
|
+
h5 = h0 - h2;
|
|
178
|
+
*c = log(h5 / h4) / log(q2 / q1);
|
|
179
|
+
if (*c <= 0.0 || *c > 20.0) return 0;
|
|
180
|
+
*b = -h4 / pow(q1, *c);
|
|
181
|
+
if (*b >= 0.0) return 0;
|
|
182
|
+
return 1;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
int customcurvepump(Project *pr, Spump *pump, Scurve *curve)
|
|
186
|
+
/*
|
|
187
|
+
**-------------------------------------------------------------------
|
|
188
|
+
** Input: pump = a pump object
|
|
189
|
+
** curve = a data curve object
|
|
190
|
+
** Output: returns an error code
|
|
191
|
+
** Purpose: computes properties for a pump with a custom pump curve.
|
|
192
|
+
**-------------------------------------------------------------------
|
|
193
|
+
*/
|
|
194
|
+
{
|
|
195
|
+
int m, npts = curve->Npts;
|
|
196
|
+
pump->Ptype = CUSTOM;
|
|
197
|
+
for (m = 1; m < npts; m++)
|
|
198
|
+
{
|
|
199
|
+
// Curve must have continuously decreasing head (the Y value)
|
|
200
|
+
if (curve->Y[m] >= curve->Y[m - 1])
|
|
201
|
+
{
|
|
202
|
+
pump->Ptype = NOCURVE;
|
|
203
|
+
return 227;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
pump->Qmax = curve->X[npts - 1];
|
|
207
|
+
pump->Q0 = (curve->X[0] + pump->Qmax) / 2.0 / pr->Ucf[FLOW];
|
|
208
|
+
pump->Qmax /= pr->Ucf[FLOW];
|
|
209
|
+
pump->Hmax = curve->Y[0] / pr->Ucf[HEAD];
|
|
210
|
+
return 0;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
int pumpcurvepump(Project *pr, Spump *pump, Scurve *curve)
|
|
214
|
+
/*
|
|
215
|
+
**-------------------------------------------------------------------
|
|
216
|
+
** Input: pump = a pump object
|
|
217
|
+
** curve = a data curve object
|
|
218
|
+
** Output: returns an error code
|
|
219
|
+
** Purpose: computes properties for a pump assigned a pump curve.
|
|
220
|
+
**-------------------------------------------------------------------
|
|
221
|
+
*/
|
|
222
|
+
{
|
|
223
|
+
double a, b, c, h0 = 0.0, h1 = 0.0, h2 = 0.0, q1 = 0.0, q2 = 0.0;
|
|
224
|
+
int npts = curve->Npts;
|
|
225
|
+
|
|
226
|
+
curve->Type = PUMP_CURVE;
|
|
227
|
+
|
|
228
|
+
// Generic power function curve
|
|
229
|
+
if (npts == 1)
|
|
230
|
+
{
|
|
231
|
+
pump->Ptype = POWER_FUNC;
|
|
232
|
+
q1 = curve->X[0];
|
|
233
|
+
h1 = curve->Y[0];
|
|
234
|
+
h0 = 1.33334 * h1;
|
|
235
|
+
q2 = 2.0 * q1;
|
|
236
|
+
h2 = 0.0;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// 3 point curve with shutoff head
|
|
240
|
+
else if (npts == 3 && curve->X[0] == 0.0)
|
|
241
|
+
{
|
|
242
|
+
pump->Ptype = POWER_FUNC;
|
|
243
|
+
h0 = curve->Y[0];
|
|
244
|
+
q1 = curve->X[1];
|
|
245
|
+
h1 = curve->Y[1];
|
|
246
|
+
q2 = curve->X[2];
|
|
247
|
+
h2 = curve->Y[2];
|
|
248
|
+
}
|
|
249
|
+
else return customcurvepump(pr, pump, curve);
|
|
250
|
+
|
|
251
|
+
// Compute shape factors & limits of power function curves
|
|
252
|
+
if (!powerfuncpump(h0, h1, h2, q1, q2, &a, &b, &c))
|
|
253
|
+
{
|
|
254
|
+
pump->Ptype = NOCURVE;
|
|
255
|
+
return 227;
|
|
256
|
+
}
|
|
257
|
+
else
|
|
258
|
+
{
|
|
259
|
+
pump->H0 = -a / pr->Ucf[HEAD];
|
|
260
|
+
pump->R = -b * (pow(pr->Ucf[FLOW], c) / pr->Ucf[HEAD]);
|
|
261
|
+
pump->N = c;
|
|
262
|
+
pump->Q0 = q1 / pr->Ucf[FLOW];
|
|
263
|
+
pump->Qmax = pow((-a / b), (1.0 / c)) / pr->Ucf[FLOW];
|
|
264
|
+
pump->Hmax = h0 / pr->Ucf[HEAD];
|
|
265
|
+
}
|
|
266
|
+
return 0;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
int constpowerpump(Project *pr, Spump *pump)
|
|
270
|
+
/*
|
|
271
|
+
**-------------------------------------------------------------------
|
|
272
|
+
** Input: pump = a pump object
|
|
273
|
+
** Output: returns an error code
|
|
274
|
+
** Purpose: computes properties for a constant power pump.
|
|
275
|
+
**-------------------------------------------------------------------
|
|
276
|
+
*/
|
|
277
|
+
{
|
|
278
|
+
pump->Ptype = CONST_HP;
|
|
279
|
+
pump->H0 = 0.0;
|
|
280
|
+
pump->R = -8.814 * pr->network.Link[pump->Link].Km / pr->Ucf[POWER];
|
|
281
|
+
pump->N = -1.0;
|
|
282
|
+
pump->Hmax = BIG; // No head limit
|
|
283
|
+
pump->Qmax = BIG; // No flow limit
|
|
284
|
+
pump->Q0 = 1.0; // Init. flow = 1 cfs
|
|
285
|
+
return 0;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
int validatepumps(Project *pr)
|
|
289
|
+
/*
|
|
290
|
+
**-------------------------------------------------------------------
|
|
291
|
+
** Input: none
|
|
292
|
+
** Output: returns 1 if successful, 0 if not
|
|
293
|
+
** Purpose: checks if pumps are assigned valid pump curve data.
|
|
294
|
+
**-------------------------------------------------------------------
|
|
295
|
+
*/
|
|
296
|
+
{
|
|
297
|
+
Network *net = &pr->network;
|
|
298
|
+
int i, errcode, result = 1;
|
|
299
|
+
char errmsg[MAXMSG+1] = "";
|
|
300
|
+
Spump *pump;
|
|
301
|
+
|
|
302
|
+
for (i = 1; i <= net->Npumps; i++)
|
|
303
|
+
{
|
|
304
|
+
// Pump has a designated pump curve
|
|
305
|
+
pump = &net->Pump[i];
|
|
306
|
+
if (pump->Hcurve > 0)
|
|
307
|
+
errcode = pumpcurvepump(pr, pump, &net->Curve[pump->Hcurve]);
|
|
308
|
+
|
|
309
|
+
// Pump has a constant power setting
|
|
310
|
+
else if (net->Link[pump->Link].Km > 0.0)
|
|
311
|
+
errcode = constpowerpump(pr, pump);
|
|
312
|
+
|
|
313
|
+
// Pump has no pump curve info assigned
|
|
314
|
+
else
|
|
315
|
+
{
|
|
316
|
+
pump->Ptype = NOCURVE;
|
|
317
|
+
errcode = 226;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
if (errcode)
|
|
321
|
+
{
|
|
322
|
+
sprintf(pr->Msg, "Error %d: %s %s",
|
|
323
|
+
errcode, geterrmsg(errcode, errmsg), net->Link[pump->Link].ID);
|
|
324
|
+
writeline(pr, pr->Msg);
|
|
325
|
+
result = 0;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
return result;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
int validateproject(Project *pr)
|
|
332
|
+
/*
|
|
333
|
+
*--------------------------------------------------------------
|
|
334
|
+
* Input: none
|
|
335
|
+
* Output: returns error code
|
|
336
|
+
* Purpose: checks for valid network data.
|
|
337
|
+
*--------------------------------------------------------------
|
|
338
|
+
*/
|
|
339
|
+
{
|
|
340
|
+
int errcode = 0;
|
|
341
|
+
if (pr->network.Nnodes < 2) return 223;
|
|
342
|
+
if (pr->network.Ntanks == 0) return 224;
|
|
343
|
+
if (!validatetanks(pr)) errcode = 110;
|
|
344
|
+
if (!validatepumps(pr)) errcode = 110;
|
|
345
|
+
if (!validatepatterns(pr)) errcode = 110;
|
|
346
|
+
if (!validatecurves(pr)) errcode = 110;
|
|
347
|
+
return errcode;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
void reindextanks(Project *pr)
|
|
351
|
+
/*
|
|
352
|
+
*--------------------------------------------------------------
|
|
353
|
+
* Input: none
|
|
354
|
+
* Output: none
|
|
355
|
+
* Purpose: adjusts tank node indexes when the number of
|
|
356
|
+
* junctions created from an input file is less than
|
|
357
|
+
* the total number of junction lines in the file.
|
|
358
|
+
*--------------------------------------------------------------
|
|
359
|
+
*/
|
|
360
|
+
{
|
|
361
|
+
Network *net = &pr->network;
|
|
362
|
+
Parser *parser = &pr->parser;
|
|
363
|
+
Quality *qual = &pr->quality;
|
|
364
|
+
Scontrol *control;
|
|
365
|
+
int i, j, ndiff, n1, n2, size;
|
|
366
|
+
|
|
367
|
+
// ndiff = # unused entries in Node array before first tank node
|
|
368
|
+
ndiff = parser->MaxJuncs - net->Njuncs;
|
|
369
|
+
if (ndiff > 0)
|
|
370
|
+
{
|
|
371
|
+
for (i = 1; i <= net->Ntanks; ++i)
|
|
372
|
+
{
|
|
373
|
+
// n1 is current tank index in Node array, n2 is adjusted index
|
|
374
|
+
n1 = net->Tank[i].Node;
|
|
375
|
+
n2 = n1 - ndiff;
|
|
376
|
+
|
|
377
|
+
// Update the tank node's hash table entry
|
|
378
|
+
hashtable_update(net->NodeHashTable, net->Node[n1].ID, n2);
|
|
379
|
+
|
|
380
|
+
// Update the tank's node index
|
|
381
|
+
net->Tank[i].Node = n2;
|
|
382
|
+
|
|
383
|
+
// Re-position tank node in Node array
|
|
384
|
+
net->Node[n2] = net->Node[n1];
|
|
385
|
+
|
|
386
|
+
// Replace all references to old tank node index with new one
|
|
387
|
+
for (j = 1; j <= net->Nlinks; ++j)
|
|
388
|
+
{
|
|
389
|
+
if (net->Link[j].N1 == n1) net->Link[j].N1 = n2;
|
|
390
|
+
if (net->Link[j].N2 == n1) net->Link[j].N2 = n2;
|
|
391
|
+
}
|
|
392
|
+
for (j = 1; j <= net->Ncontrols; ++j)
|
|
393
|
+
{
|
|
394
|
+
control = &net->Control[j];
|
|
395
|
+
if (control->Node == n1) control->Node = n2;
|
|
396
|
+
}
|
|
397
|
+
adjusttankrules(pr, -ndiff);
|
|
398
|
+
if (qual->TraceNode == n1) qual->TraceNode = n2;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
// Reallocate the Node array (shouldn't fail as new size < old size)
|
|
402
|
+
parser->MaxJuncs = net->Njuncs;
|
|
403
|
+
parser->MaxNodes = net->Njuncs + net->Ntanks;
|
|
404
|
+
size = (net->Nnodes + 2) * sizeof(Snode);
|
|
405
|
+
net->Node = (Snode *)realloc(net->Node, size);
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
2
|
mkdir -p "../customlibs/"
|
|
3
|
-
gcc -w -O3 -march=native -shared -Wl,-soname,libepanet2_2.so -fPIC -o "../customlibs/libepanet2_2.so" EPANET/SRC_engines/*.c -IEPANET/SRC_engines/include -lc -lm -pthread
|
|
3
|
+
gcc -w -O3 -march=native -shared -Wl,-soname,libepanet2_2.so -fPIC -o "../customlibs/libepanet2_2.so" EPANET/SRC_engines/*.c -IEPANET/SRC_engines/include EPANET/SRC_engines/util/*.c -IEPANET/SRC_engines/util/ -lc -lm -pthread
|
|
4
4
|
gcc -w -O3 -march=native -fPIC -shared -Wl,-soname,libepanetmsx2_2_0.so -o "../customlibs/libepanetmsx2_2_0.so" -fopenmp -Depanetmsx_EXPORTS -IEPANET-MSX/Src/include -IEPANET/SRC_engines/include EPANET-MSX/Src/*.c -Wl,-rpath=. "../customlibs/libepanet2_2.so" -lm -lgomp -lpthread
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
2
|
mkdir -p "../customlibs/"
|
|
3
|
-
gcc-
|
|
4
|
-
gcc-
|
|
3
|
+
gcc-15 -w -O3 -march=native -dynamiclib -fPIC -install_name libepanet2_2.dylib -o "../customlibs/libepanet2_2.dylib" EPANET/SRC_engines/*.c -IEPANET/SRC_engines/include EPANET/SRC_engines/util/*.c -IEPANET/SRC_engines/util/ -lc -lm -pthread
|
|
4
|
+
gcc-15 -w -O3 -march=native -dynamiclib -fPIC -install_name libepanetmsx2_2_0.dylib -o "../customlibs/libepanetmsx2_2_0.dylib" -fopenmp -Depanetmsx_EXPORTS -IEPANET-MSX/Src/include -IEPANET/SRC_engines/include EPANET-MSX/Src/*.c -L'../customlibs' -lepanet2_2 -lm -lgomp -lpthread
|
epyt_flow/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.
|
|
1
|
+
0.14.1
|
epyt_flow/__init__.py
CHANGED
|
@@ -39,4 +39,4 @@ def compile_libraries_unix(lib_epanet_name: str, compile_script_name: str,
|
|
|
39
39
|
if sys.platform.startswith("linux"):
|
|
40
40
|
compile_libraries_unix("libepanet2_2.so", "compile_linux.sh")
|
|
41
41
|
elif sys.platform.startswith("darwin"):
|
|
42
|
-
compile_libraries_unix("libepanet2_2.dylib", "compile_macos.sh", gcc_name="gcc-
|
|
42
|
+
compile_libraries_unix("libepanet2_2.dylib", "compile_macos.sh", gcc_name="gcc-15")
|
|
@@ -23,6 +23,10 @@ class ScenarioControlEnv(ABC):
|
|
|
23
23
|
autoreset : `bool`, optional
|
|
24
24
|
If True, environment is automatically reset if terminated.
|
|
25
25
|
|
|
26
|
+
The default is False.
|
|
27
|
+
reapply_uncertainties_at_reset : `bool`, optional
|
|
28
|
+
If True, the uncertainties are re-applied to the original properties at each reset.
|
|
29
|
+
|
|
26
30
|
The default is False.
|
|
27
31
|
|
|
28
32
|
Attributes
|
|
@@ -36,7 +40,9 @@ class ScenarioControlEnv(ABC):
|
|
|
36
40
|
_hydraulic_scada_data : :class:`~epyt_flow.simulation.scada.scada_data.ScadaData`, protected
|
|
37
41
|
SCADA data from the hydraulic simulation -- only used if EPANET-MSX is used in the control scenario.
|
|
38
42
|
"""
|
|
39
|
-
def __init__(self, scenario_config: ScenarioConfig, autoreset: bool = False,
|
|
43
|
+
def __init__(self, scenario_config: ScenarioConfig, autoreset: bool = False,
|
|
44
|
+
reapply_uncertainties_at_reset: bool = False,
|
|
45
|
+
**kwds):
|
|
40
46
|
if not isinstance(scenario_config, ScenarioConfig):
|
|
41
47
|
raise TypeError("'scenario_config' must be an instance of " +
|
|
42
48
|
"'epyt_flow.simulation.ScenarioConfig' " +
|
|
@@ -44,12 +50,15 @@ class ScenarioControlEnv(ABC):
|
|
|
44
50
|
if not isinstance(autoreset, bool):
|
|
45
51
|
raise TypeError("'autoreset' must be an instance of 'bool' " +
|
|
46
52
|
f"but not of '{type(autoreset)}'")
|
|
53
|
+
if not isinstance(reapply_uncertainties_at_reset, bool):
|
|
54
|
+
raise TypeError("")
|
|
47
55
|
|
|
48
56
|
self._scenario_config = scenario_config
|
|
49
57
|
self._scenario_sim = None
|
|
50
58
|
self._sim_generator = None
|
|
51
59
|
self.__autoreset = autoreset
|
|
52
60
|
self._hydraulic_scada_data = None
|
|
61
|
+
self.__reapply_uncertainties_at_reset = reapply_uncertainties_at_reset
|
|
53
62
|
|
|
54
63
|
super().__init__(**kwds)
|
|
55
64
|
|
|
@@ -65,6 +74,18 @@ class ScenarioControlEnv(ABC):
|
|
|
65
74
|
"""
|
|
66
75
|
return self.__autoreset
|
|
67
76
|
|
|
77
|
+
@property
|
|
78
|
+
def reapply_uncertainties_at_reset(self) -> bool:
|
|
79
|
+
"""
|
|
80
|
+
True, if the uncertainties are re-applied to the original properties at each reset.
|
|
81
|
+
|
|
82
|
+
Returns
|
|
83
|
+
-------
|
|
84
|
+
`bool`
|
|
85
|
+
True, if the uncertainties are re-applied to the original properties at each reset.
|
|
86
|
+
"""
|
|
87
|
+
return self.__reapply_uncertainties_at_reset
|
|
88
|
+
|
|
68
89
|
def __enter__(self):
|
|
69
90
|
return self
|
|
70
91
|
|
|
@@ -117,14 +138,16 @@ class ScenarioControlEnv(ABC):
|
|
|
117
138
|
# Run hydraulic simulation first
|
|
118
139
|
hyd_export = os.path.join(get_temp_folder(), f"epytflow_env_MSX_{uuid.uuid4()}.hyd")
|
|
119
140
|
sim = self._scenario_sim.run_hydraulic_simulation
|
|
120
|
-
self._hydraulic_scada_data = sim(hyd_export=hyd_export
|
|
141
|
+
self._hydraulic_scada_data = sim(hyd_export=hyd_export,
|
|
142
|
+
reapply_uncertainties=self.__reapply_uncertainties_at_reset)
|
|
121
143
|
|
|
122
144
|
# Run advanced quality analysis (EPANET-MSX) on top of the computed hydraulics
|
|
123
145
|
gen = self._scenario_sim.run_advanced_quality_simulation_as_generator
|
|
124
146
|
self._sim_generator = gen(hyd_export, support_abort=True)
|
|
125
147
|
else:
|
|
126
148
|
gen = self._scenario_sim.run_hydraulic_simulation_as_generator
|
|
127
|
-
self._sim_generator = gen(support_abort=True
|
|
149
|
+
self._sim_generator = gen(support_abort=True,
|
|
150
|
+
reapply_uncertainties=self.__reapply_uncertainties_at_reset)
|
|
128
151
|
|
|
129
152
|
return self._next_sim_itr()
|
|
130
153
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"""
|
|
2
2
|
This module contains a wrapper for EPyT that allows a better error/warning handling.
|
|
3
3
|
"""
|
|
4
|
+
import warnings
|
|
4
5
|
from ctypes import byref, create_string_buffer
|
|
5
6
|
from epyt import epanet
|
|
6
7
|
from epyt.epanet import epanetapi, epanetmsxapi
|
|
@@ -30,6 +31,10 @@ class EPyT(epanet):
|
|
|
30
31
|
self.LibEPANETpath = self.api.LibEPANETpath
|
|
31
32
|
self.LibEPANET = self.api.LibEPANET
|
|
32
33
|
|
|
34
|
+
def _logFunctionError(self, function_name):
|
|
35
|
+
# Do not print warnings.
|
|
36
|
+
pass
|
|
37
|
+
|
|
33
38
|
def loadMSXFile(self, msxname, customMSXlib=None, ignore_properties=False):
|
|
34
39
|
super().loadMSXFile(msxname, customMSXlib, ignore_properties)
|
|
35
40
|
|
|
@@ -40,17 +45,25 @@ class EPyT(epanet):
|
|
|
40
45
|
msxrealfile=self.MSXFile)
|
|
41
46
|
return self.msx
|
|
42
47
|
|
|
43
|
-
def set_error_handling(self, raise_exception_on_error: bool
|
|
48
|
+
def set_error_handling(self, raise_exception_on_error: bool, warn_on_error: bool,
|
|
49
|
+
ignore_error_codes: list[int]) -> None:
|
|
44
50
|
"""
|
|
45
51
|
Specifies the behavior in the case of an error/warning --
|
|
46
|
-
i.e. should an exception be raised or not?
|
|
52
|
+
i.e. should an exception or warning be raised or not?
|
|
47
53
|
|
|
48
54
|
Parameters
|
|
49
55
|
----------
|
|
50
56
|
raise_exception_on_error : `bool`
|
|
51
57
|
True if an exception should be raise, False otherwise.
|
|
58
|
+
warn_on_error : `bool`
|
|
59
|
+
True if a warning should be generated, False otherwise.
|
|
60
|
+
ignore_error_codes : `list[int]`
|
|
61
|
+
List of error codes that should be ignored -- i.e., no exception or
|
|
62
|
+
warning will be generated.
|
|
52
63
|
"""
|
|
53
|
-
self.api.set_error_handling(raise_exception_on_error)
|
|
64
|
+
self.api.set_error_handling(raise_exception_on_error, warn_on_error, ignore_error_codes)
|
|
65
|
+
if self.msx is not None:
|
|
66
|
+
self.msx.set_error_handling(raise_exception_on_error, warn_on_error, ignore_error_codes)
|
|
54
67
|
|
|
55
68
|
def was_last_func_successful(self) -> bool:
|
|
56
69
|
"""
|
|
@@ -96,24 +109,39 @@ class MyEpanetMsxAPI(epanetmsxapi):
|
|
|
96
109
|
Wrapper for the `epyt.epanet.epanetmsxapi <https://epanet-python-toolkit-epyt.readthedocs.io/en/latest/api.html#epyt.epanet.epanetmsxapi>`_
|
|
97
110
|
class adding error/warning storage functionalities.
|
|
98
111
|
"""
|
|
99
|
-
def __init__(self, raise_on_error: bool = True,
|
|
112
|
+
def __init__(self, raise_on_error: bool = True, warn_on_error: bool = False,
|
|
113
|
+
ignore_error_codes: list[int] = [], **kwds):
|
|
100
114
|
self.__raise_on_error = raise_on_error
|
|
115
|
+
self.__warn_on_error = warn_on_error
|
|
116
|
+
self.__ignore_error_codes = ignore_error_codes
|
|
101
117
|
self.__last_error_code = None
|
|
102
118
|
self.__last_error_desc = None
|
|
103
119
|
|
|
104
120
|
super().__init__(**kwds)
|
|
105
121
|
|
|
106
|
-
def
|
|
122
|
+
def _logFunctionError(self, function_name):
|
|
123
|
+
# Do not print warnings.
|
|
124
|
+
pass
|
|
125
|
+
|
|
126
|
+
def set_error_handling(self, raise_on_error: bool, warn_on_error: bool,
|
|
127
|
+
ignore_error_codes: list[int] = []) -> None:
|
|
107
128
|
"""
|
|
108
129
|
Specifies the behavior in the case of an error/warning --
|
|
109
|
-
i.e. should an exception be raised or not?
|
|
130
|
+
i.e. should an exception or warning be raised or not?
|
|
110
131
|
|
|
111
132
|
Parameters
|
|
112
133
|
----------
|
|
113
134
|
raise_exception_on_error : `bool`
|
|
114
135
|
True if an exception should be raise, False otherwise.
|
|
136
|
+
warn_on_error : `bool`
|
|
137
|
+
True if a warning should be generated, False otherwise.
|
|
138
|
+
ignore_error_codes : `list[int]`
|
|
139
|
+
List of error codes that should be ignored -- i.e., no exception or
|
|
140
|
+
warning will be generated.
|
|
115
141
|
"""
|
|
116
142
|
self.__raise_on_error = raise_on_error
|
|
143
|
+
self.__warn_on_error = warn_on_error
|
|
144
|
+
self.__ignore_error_codes = ignore_error_codes
|
|
117
145
|
|
|
118
146
|
def get_last_error_desc(self) -> str:
|
|
119
147
|
"""
|
|
@@ -161,8 +189,11 @@ class MyEpanetMsxAPI(epanetmsxapi):
|
|
|
161
189
|
self.__last_error_code = err_code
|
|
162
190
|
self.__last_error_desc = error_desc.value.decode()
|
|
163
191
|
|
|
164
|
-
if self.
|
|
165
|
-
|
|
192
|
+
if self.__last_error_code not in self.__ignore_error_codes:
|
|
193
|
+
if self.__warn_on_error:
|
|
194
|
+
warnings.warn(self.__last_error_desc, RuntimeWarning)
|
|
195
|
+
if self.__raise_on_error:
|
|
196
|
+
raise RuntimeError(self.__last_error_desc)
|
|
166
197
|
|
|
167
198
|
def MSXopen(self, msxfile, msxrealfile):
|
|
168
199
|
self._reset_error()
|
|
@@ -286,24 +317,35 @@ class MyEpanetAPI(epanetapi):
|
|
|
286
317
|
Wrapper for the `epyt.epanet.epanetapi <https://epanet-python-toolkit-epyt.readthedocs.io/en/latest/api.html#epyt.epanet.epanetapi>`_
|
|
287
318
|
class adding error/warning storage functionalities.
|
|
288
319
|
"""
|
|
289
|
-
def __init__(self, raise_on_error: bool = True,
|
|
320
|
+
def __init__(self, raise_on_error: bool = True, warn_on_error: bool = False,
|
|
321
|
+
ignore_error_codes: list[int] = [], **kwds):
|
|
290
322
|
self.__raise_on_error = raise_on_error
|
|
323
|
+
self.__warn_on_error = warn_on_error
|
|
324
|
+
self.__ignore_error_codes = ignore_error_codes
|
|
291
325
|
self.__last_error_code = None
|
|
292
326
|
self.__last_error_desc = None
|
|
293
327
|
|
|
294
328
|
super().__init__(**kwds)
|
|
295
329
|
|
|
296
|
-
def set_error_handling(self, raise_on_error: bool
|
|
330
|
+
def set_error_handling(self, raise_on_error: bool, warn_on_error: bool,
|
|
331
|
+
ignore_error_codes: list[int] = []) -> None:
|
|
297
332
|
"""
|
|
298
333
|
Specifies the behavior in the case of an error/warning --
|
|
299
|
-
i.e. should an exception be raised or not?
|
|
334
|
+
i.e. should an exception or warning be raised or not?
|
|
300
335
|
|
|
301
336
|
Parameters
|
|
302
337
|
----------
|
|
303
338
|
raise_exception_on_error : `bool`
|
|
304
339
|
True if an exception should be raise, False otherwise.
|
|
340
|
+
warn_on_error : `bool`
|
|
341
|
+
True if a warning should be generated, False otherwise.
|
|
342
|
+
ignore_error_codes : `list[int]`
|
|
343
|
+
List of error codes that should be ignored -- i.e., no exception or
|
|
344
|
+
warning will be generated.
|
|
305
345
|
"""
|
|
306
346
|
self.__raise_on_error = raise_on_error
|
|
347
|
+
self.__warn_on_error = warn_on_error
|
|
348
|
+
self.__ignore_error_codes = ignore_error_codes
|
|
307
349
|
|
|
308
350
|
def get_last_error_desc(self) -> str:
|
|
309
351
|
"""
|
|
@@ -353,8 +395,11 @@ class MyEpanetAPI(epanetapi):
|
|
|
353
395
|
self.__last_error_code = self.errcode
|
|
354
396
|
self.__last_error_desc = error_desc.value.decode()
|
|
355
397
|
|
|
356
|
-
if self.
|
|
357
|
-
|
|
398
|
+
if self.__last_error_code not in self.__ignore_error_codes:
|
|
399
|
+
if self.__warn_on_error:
|
|
400
|
+
warnings.warn(self.__last_error_desc, RuntimeWarning)
|
|
401
|
+
if self.__raise_on_error:
|
|
402
|
+
raise RuntimeError(self.__last_error_desc)
|
|
358
403
|
|
|
359
404
|
def ENepanet(self, inpfile="", rptfile="", binfile=""):
|
|
360
405
|
self._reset_error()
|