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.
Files changed (63) hide show
  1. epyt_flow/EPANET/EPANET/SRC_engines/AUTHORS +40 -8
  2. epyt_flow/EPANET/EPANET/SRC_engines/LICENSE +3 -3
  3. epyt_flow/EPANET/EPANET/SRC_engines/enumstxt.h +24 -7
  4. epyt_flow/EPANET/EPANET/SRC_engines/epanet.c +726 -374
  5. epyt_flow/EPANET/EPANET/SRC_engines/epanet2.c +128 -32
  6. epyt_flow/EPANET/EPANET/SRC_engines/errors.dat +7 -1
  7. epyt_flow/EPANET/EPANET/SRC_engines/flowbalance.c +186 -0
  8. epyt_flow/EPANET/EPANET/SRC_engines/funcs.h +40 -14
  9. epyt_flow/EPANET/EPANET/SRC_engines/hash.c +177 -177
  10. epyt_flow/EPANET/EPANET/SRC_engines/hash.h +28 -28
  11. epyt_flow/EPANET/EPANET/SRC_engines/hydcoeffs.c +192 -40
  12. epyt_flow/EPANET/EPANET/SRC_engines/hydraul.c +101 -46
  13. epyt_flow/EPANET/EPANET/SRC_engines/hydsolver.c +85 -24
  14. epyt_flow/EPANET/EPANET/SRC_engines/hydstatus.c +29 -63
  15. epyt_flow/EPANET/EPANET/SRC_engines/include/epanet2.h +70 -37
  16. epyt_flow/EPANET/EPANET/SRC_engines/include/epanet2_2.h +408 -234
  17. epyt_flow/EPANET/EPANET/SRC_engines/include/epanet2_enums.h +87 -37
  18. epyt_flow/EPANET/EPANET/SRC_engines/inpfile.c +153 -79
  19. epyt_flow/EPANET/EPANET/SRC_engines/input1.c +59 -94
  20. epyt_flow/EPANET/EPANET/SRC_engines/input2.c +73 -202
  21. epyt_flow/EPANET/EPANET/SRC_engines/input3.c +446 -351
  22. epyt_flow/EPANET/EPANET/SRC_engines/leakage.c +527 -0
  23. epyt_flow/EPANET/EPANET/SRC_engines/mempool.c +8 -4
  24. epyt_flow/EPANET/EPANET/SRC_engines/mempool.h +23 -23
  25. epyt_flow/EPANET/EPANET/SRC_engines/output.c +5 -4
  26. epyt_flow/EPANET/EPANET/SRC_engines/project.c +407 -75
  27. epyt_flow/EPANET/EPANET/SRC_engines/quality.c +12 -2
  28. epyt_flow/EPANET/EPANET/SRC_engines/qualreact.c +70 -13
  29. epyt_flow/EPANET/EPANET/SRC_engines/qualroute.c +7 -5
  30. epyt_flow/EPANET/EPANET/SRC_engines/report.c +88 -20
  31. epyt_flow/EPANET/EPANET/SRC_engines/rules.c +144 -6
  32. epyt_flow/EPANET/EPANET/SRC_engines/smatrix.c +19 -19
  33. epyt_flow/EPANET/EPANET/SRC_engines/text.h +16 -5
  34. epyt_flow/EPANET/EPANET/SRC_engines/types.h +73 -19
  35. epyt_flow/EPANET/EPANET/SRC_engines/util/cstr_helper.c +59 -0
  36. epyt_flow/EPANET/EPANET/SRC_engines/util/cstr_helper.h +38 -0
  37. epyt_flow/EPANET/EPANET/SRC_engines/util/errormanager.c +92 -0
  38. epyt_flow/EPANET/EPANET/SRC_engines/util/errormanager.h +39 -0
  39. epyt_flow/EPANET/EPANET/SRC_engines/util/filemanager.c +212 -0
  40. epyt_flow/EPANET/EPANET/SRC_engines/util/filemanager.h +81 -0
  41. epyt_flow/EPANET/EPANET/SRC_engines/validate.c +408 -0
  42. epyt_flow/EPANET/compile_linux.sh +1 -1
  43. epyt_flow/EPANET/compile_macos.sh +2 -2
  44. epyt_flow/VERSION +1 -1
  45. epyt_flow/__init__.py +1 -1
  46. epyt_flow/gym/scenario_control_env.py +26 -3
  47. epyt_flow/simulation/backend/my_epyt.py +58 -13
  48. epyt_flow/simulation/events/quality_events.py +6 -6
  49. epyt_flow/simulation/events/sensor_faults.py +24 -24
  50. epyt_flow/simulation/events/system_event.py +3 -3
  51. epyt_flow/simulation/scada/scada_data.py +10 -14
  52. epyt_flow/simulation/scenario_simulator.py +100 -20
  53. epyt_flow/topology.py +8 -1
  54. epyt_flow/uncertainty/model_uncertainty.py +292 -150
  55. epyt_flow/uncertainty/uncertainties.py +2 -2
  56. {epyt_flow-0.13.1.dist-info → epyt_flow-0.14.1.dist-info}/METADATA +4 -4
  57. {epyt_flow-0.13.1.dist-info → epyt_flow-0.14.1.dist-info}/RECORD +60 -54
  58. {epyt_flow-0.13.1.dist-info → epyt_flow-0.14.1.dist-info}/WHEEL +1 -1
  59. epyt_flow/EPANET/EPANET/SRC_engines/Readme_SRC_Engines.txt +0 -18
  60. epyt_flow/EPANET/EPANET/SRC_engines/epanet2.def +0 -131
  61. epyt_flow/EPANET/EPANET/SRC_engines/main.c +0 -93
  62. {epyt_flow-0.13.1.dist-info → epyt_flow-0.14.1.dist-info}/licenses/LICENSE +0 -0
  63. {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-12 -w -O3 -march=native -dynamiclib -fPIC -install_name libepanet2_2.dylib -o "../customlibs/libepanet2_2.dylib" EPANET/SRC_engines/*.c -IEPANET/SRC_engines/include -lc -lm -pthread
4
- gcc-12 -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
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.13.1
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-12")
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, **kwds):
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) -> None:
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, **kwds):
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 set_error_handling(self, raise_on_error: bool) -> None:
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.__raise_on_error:
165
- raise RuntimeError(self.__last_error_desc)
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, **kwds):
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) -> None:
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.__raise_on_error:
357
- raise RuntimeError(self.__last_error_desc)
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()