epyt-flow 0.1.0__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 (131) hide show
  1. epyt_flow/EPANET/EPANET/SRC_engines/AUTHORS +28 -0
  2. epyt_flow/EPANET/EPANET/SRC_engines/LICENSE +21 -0
  3. epyt_flow/EPANET/EPANET/SRC_engines/Readme_SRC_Engines.txt +18 -0
  4. epyt_flow/EPANET/EPANET/SRC_engines/enumstxt.h +134 -0
  5. epyt_flow/EPANET/EPANET/SRC_engines/epanet.c +5578 -0
  6. epyt_flow/EPANET/EPANET/SRC_engines/epanet2.c +865 -0
  7. epyt_flow/EPANET/EPANET/SRC_engines/epanet2.def +131 -0
  8. epyt_flow/EPANET/EPANET/SRC_engines/errors.dat +73 -0
  9. epyt_flow/EPANET/EPANET/SRC_engines/funcs.h +193 -0
  10. epyt_flow/EPANET/EPANET/SRC_engines/genmmd.c +1000 -0
  11. epyt_flow/EPANET/EPANET/SRC_engines/hash.c +177 -0
  12. epyt_flow/EPANET/EPANET/SRC_engines/hash.h +28 -0
  13. epyt_flow/EPANET/EPANET/SRC_engines/hydcoeffs.c +1151 -0
  14. epyt_flow/EPANET/EPANET/SRC_engines/hydraul.c +1117 -0
  15. epyt_flow/EPANET/EPANET/SRC_engines/hydsolver.c +720 -0
  16. epyt_flow/EPANET/EPANET/SRC_engines/hydstatus.c +476 -0
  17. epyt_flow/EPANET/EPANET/SRC_engines/include/epanet2.h +431 -0
  18. epyt_flow/EPANET/EPANET/SRC_engines/include/epanet2_2.h +1786 -0
  19. epyt_flow/EPANET/EPANET/SRC_engines/include/epanet2_enums.h +468 -0
  20. epyt_flow/EPANET/EPANET/SRC_engines/inpfile.c +810 -0
  21. epyt_flow/EPANET/EPANET/SRC_engines/input1.c +707 -0
  22. epyt_flow/EPANET/EPANET/SRC_engines/input2.c +864 -0
  23. epyt_flow/EPANET/EPANET/SRC_engines/input3.c +2170 -0
  24. epyt_flow/EPANET/EPANET/SRC_engines/main.c +93 -0
  25. epyt_flow/EPANET/EPANET/SRC_engines/mempool.c +142 -0
  26. epyt_flow/EPANET/EPANET/SRC_engines/mempool.h +24 -0
  27. epyt_flow/EPANET/EPANET/SRC_engines/output.c +852 -0
  28. epyt_flow/EPANET/EPANET/SRC_engines/project.c +1359 -0
  29. epyt_flow/EPANET/EPANET/SRC_engines/quality.c +685 -0
  30. epyt_flow/EPANET/EPANET/SRC_engines/qualreact.c +743 -0
  31. epyt_flow/EPANET/EPANET/SRC_engines/qualroute.c +694 -0
  32. epyt_flow/EPANET/EPANET/SRC_engines/report.c +1489 -0
  33. epyt_flow/EPANET/EPANET/SRC_engines/rules.c +1362 -0
  34. epyt_flow/EPANET/EPANET/SRC_engines/smatrix.c +871 -0
  35. epyt_flow/EPANET/EPANET/SRC_engines/text.h +497 -0
  36. epyt_flow/EPANET/EPANET/SRC_engines/types.h +874 -0
  37. epyt_flow/EPANET/EPANET-MSX/MSX_Updates.txt +53 -0
  38. epyt_flow/EPANET/EPANET-MSX/Src/dispersion.h +27 -0
  39. epyt_flow/EPANET/EPANET-MSX/Src/hash.c +107 -0
  40. epyt_flow/EPANET/EPANET-MSX/Src/hash.h +28 -0
  41. epyt_flow/EPANET/EPANET-MSX/Src/include/epanetmsx.h +102 -0
  42. epyt_flow/EPANET/EPANET-MSX/Src/include/epanetmsx_export.h +42 -0
  43. epyt_flow/EPANET/EPANET-MSX/Src/mathexpr.c +937 -0
  44. epyt_flow/EPANET/EPANET-MSX/Src/mathexpr.h +39 -0
  45. epyt_flow/EPANET/EPANET-MSX/Src/mempool.c +204 -0
  46. epyt_flow/EPANET/EPANET-MSX/Src/mempool.h +24 -0
  47. epyt_flow/EPANET/EPANET-MSX/Src/msxchem.c +1285 -0
  48. epyt_flow/EPANET/EPANET-MSX/Src/msxcompiler.c +368 -0
  49. epyt_flow/EPANET/EPANET-MSX/Src/msxdict.h +42 -0
  50. epyt_flow/EPANET/EPANET-MSX/Src/msxdispersion.c +586 -0
  51. epyt_flow/EPANET/EPANET-MSX/Src/msxerr.c +116 -0
  52. epyt_flow/EPANET/EPANET-MSX/Src/msxfile.c +260 -0
  53. epyt_flow/EPANET/EPANET-MSX/Src/msxfuncs.c +175 -0
  54. epyt_flow/EPANET/EPANET-MSX/Src/msxfuncs.h +35 -0
  55. epyt_flow/EPANET/EPANET-MSX/Src/msxinp.c +1504 -0
  56. epyt_flow/EPANET/EPANET-MSX/Src/msxout.c +401 -0
  57. epyt_flow/EPANET/EPANET-MSX/Src/msxproj.c +791 -0
  58. epyt_flow/EPANET/EPANET-MSX/Src/msxqual.c +2010 -0
  59. epyt_flow/EPANET/EPANET-MSX/Src/msxrpt.c +400 -0
  60. epyt_flow/EPANET/EPANET-MSX/Src/msxtank.c +422 -0
  61. epyt_flow/EPANET/EPANET-MSX/Src/msxtoolkit.c +1164 -0
  62. epyt_flow/EPANET/EPANET-MSX/Src/msxtypes.h +551 -0
  63. epyt_flow/EPANET/EPANET-MSX/Src/msxutils.c +524 -0
  64. epyt_flow/EPANET/EPANET-MSX/Src/msxutils.h +56 -0
  65. epyt_flow/EPANET/EPANET-MSX/Src/newton.c +158 -0
  66. epyt_flow/EPANET/EPANET-MSX/Src/newton.h +34 -0
  67. epyt_flow/EPANET/EPANET-MSX/Src/rk5.c +287 -0
  68. epyt_flow/EPANET/EPANET-MSX/Src/rk5.h +39 -0
  69. epyt_flow/EPANET/EPANET-MSX/Src/ros2.c +293 -0
  70. epyt_flow/EPANET/EPANET-MSX/Src/ros2.h +35 -0
  71. epyt_flow/EPANET/EPANET-MSX/Src/smatrix.c +816 -0
  72. epyt_flow/EPANET/EPANET-MSX/Src/smatrix.h +29 -0
  73. epyt_flow/EPANET/EPANET-MSX/readme.txt +14 -0
  74. epyt_flow/EPANET/compile.sh +4 -0
  75. epyt_flow/VERSION +1 -0
  76. epyt_flow/__init__.py +24 -0
  77. epyt_flow/data/__init__.py +0 -0
  78. epyt_flow/data/benchmarks/__init__.py +11 -0
  79. epyt_flow/data/benchmarks/batadal.py +257 -0
  80. epyt_flow/data/benchmarks/batadal_data.py +28 -0
  81. epyt_flow/data/benchmarks/battledim.py +473 -0
  82. epyt_flow/data/benchmarks/battledim_data.py +51 -0
  83. epyt_flow/data/benchmarks/gecco_water_quality.py +267 -0
  84. epyt_flow/data/benchmarks/leakdb.py +592 -0
  85. epyt_flow/data/benchmarks/leakdb_data.py +18923 -0
  86. epyt_flow/data/benchmarks/water_usage.py +123 -0
  87. epyt_flow/data/networks.py +650 -0
  88. epyt_flow/gym/__init__.py +4 -0
  89. epyt_flow/gym/control_gyms.py +47 -0
  90. epyt_flow/gym/scenario_control_env.py +101 -0
  91. epyt_flow/metrics.py +404 -0
  92. epyt_flow/models/__init__.py +2 -0
  93. epyt_flow/models/event_detector.py +31 -0
  94. epyt_flow/models/sensor_interpolation_detector.py +118 -0
  95. epyt_flow/rest_api/__init__.py +4 -0
  96. epyt_flow/rest_api/base_handler.py +70 -0
  97. epyt_flow/rest_api/res_manager.py +95 -0
  98. epyt_flow/rest_api/scada_data_handler.py +476 -0
  99. epyt_flow/rest_api/scenario_handler.py +352 -0
  100. epyt_flow/rest_api/server.py +106 -0
  101. epyt_flow/serialization.py +438 -0
  102. epyt_flow/simulation/__init__.py +5 -0
  103. epyt_flow/simulation/events/__init__.py +6 -0
  104. epyt_flow/simulation/events/actuator_events.py +259 -0
  105. epyt_flow/simulation/events/event.py +81 -0
  106. epyt_flow/simulation/events/leakages.py +404 -0
  107. epyt_flow/simulation/events/sensor_faults.py +267 -0
  108. epyt_flow/simulation/events/sensor_reading_attack.py +185 -0
  109. epyt_flow/simulation/events/sensor_reading_event.py +170 -0
  110. epyt_flow/simulation/events/system_event.py +88 -0
  111. epyt_flow/simulation/parallel_simulation.py +147 -0
  112. epyt_flow/simulation/scada/__init__.py +3 -0
  113. epyt_flow/simulation/scada/advanced_control.py +134 -0
  114. epyt_flow/simulation/scada/scada_data.py +1589 -0
  115. epyt_flow/simulation/scada/scada_data_export.py +255 -0
  116. epyt_flow/simulation/scenario_config.py +608 -0
  117. epyt_flow/simulation/scenario_simulator.py +1897 -0
  118. epyt_flow/simulation/scenario_visualizer.py +61 -0
  119. epyt_flow/simulation/sensor_config.py +1289 -0
  120. epyt_flow/topology.py +290 -0
  121. epyt_flow/uncertainty/__init__.py +3 -0
  122. epyt_flow/uncertainty/model_uncertainty.py +302 -0
  123. epyt_flow/uncertainty/sensor_noise.py +73 -0
  124. epyt_flow/uncertainty/uncertainties.py +555 -0
  125. epyt_flow/uncertainty/utils.py +206 -0
  126. epyt_flow/utils.py +306 -0
  127. epyt_flow-0.1.0.dist-info/LICENSE +21 -0
  128. epyt_flow-0.1.0.dist-info/METADATA +139 -0
  129. epyt_flow-0.1.0.dist-info/RECORD +131 -0
  130. epyt_flow-0.1.0.dist-info/WHEEL +5 -0
  131. epyt_flow-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,685 @@
1
+ /*
2
+ ******************************************************************************
3
+ Project: OWA EPANET
4
+ Version: 2.2
5
+ Module: quality.c
6
+ Description: implements EPANET's water quality engine
7
+ Authors: see AUTHORS
8
+ Copyright: see AUTHORS
9
+ License: see LICENSE
10
+ Last Updated: 05/15/2019
11
+ ******************************************************************************
12
+ */
13
+
14
+ #include <stdlib.h>
15
+ #include <stdio.h>
16
+ #include <string.h>
17
+ #include <math.h>
18
+
19
+ #include "mempool.h"
20
+ #include "types.h"
21
+ #include "funcs.h"
22
+
23
+ // Stagnant flow tolerance
24
+ const double Q_STAGNANT = 0.005 / GPMperCFS; // 0.005 gpm = 1.114e-5 cfs
25
+
26
+ // Exported functions
27
+ double findsourcequal(Project *, int, double, long);
28
+
29
+ // Imported functions
30
+ extern char setreactflag(Project *);
31
+ extern double getucf(double);
32
+ extern void ratecoeffs(Project *);
33
+ extern void initsegs(Project *);
34
+ extern void reversesegs(Project *, int);
35
+ extern int sortnodes(Project *);
36
+ extern void transport(Project *, long);
37
+
38
+ // Local functions
39
+ static double sourcequal(Project *, Psource);
40
+ static void evalmassbalance(Project *);
41
+ static double findstoredmass(Project *);
42
+ static int flowdirchanged(Project *);
43
+
44
+
45
+ int openqual(Project *pr)
46
+ /*
47
+ **--------------------------------------------------------------
48
+ ** Input: none
49
+ ** Output: returns error code
50
+ ** Purpose: opens water quality solver
51
+ **--------------------------------------------------------------
52
+ */
53
+ {
54
+ Network *net = &pr->network;
55
+ Quality *qual = &pr->quality;
56
+
57
+ int errcode = 0;
58
+ int n;
59
+
60
+ // Return if no quality analysis requested
61
+ if (qual->Qualflag == NONE) return errcode;
62
+
63
+ // Build nodal adjacency lists if they don't already exist
64
+ if (net->Adjlist == NULL)
65
+ {
66
+ errcode = buildadjlists(net);
67
+ if (errcode ) return errcode;
68
+ }
69
+
70
+ // Create a memory pool for water quality segments
71
+ qual->OutOfMemory = FALSE;
72
+ qual->SegPool = mempool_create();
73
+ if (qual->SegPool == NULL) errcode = 101;
74
+
75
+ // Allocate arrays for link flow direction & reaction rates
76
+ n = net->Nlinks + 1;
77
+ qual->FlowDir = (FlowDirection *)calloc(n, sizeof(FlowDirection));
78
+ qual->PipeRateCoeff = (double *)calloc(n, sizeof(double));
79
+
80
+ // Allocate arrays used for volume segments in links & tanks
81
+ n = net->Nlinks + net->Ntanks + 1;
82
+ qual->FirstSeg = (Pseg *)calloc(n, sizeof(Pseg));
83
+ qual->LastSeg = (Pseg *)calloc(n, sizeof(Pseg));
84
+
85
+ // Allocate memory for topologically sorted nodes
86
+ qual->SortedNodes = (int *)calloc(n, sizeof(int));
87
+
88
+ ERRCODE(MEMCHECK(qual->FlowDir));
89
+ ERRCODE(MEMCHECK(qual->PipeRateCoeff));
90
+ ERRCODE(MEMCHECK(qual->FirstSeg));
91
+ ERRCODE(MEMCHECK(qual->LastSeg));
92
+ ERRCODE(MEMCHECK(qual->SortedNodes));
93
+ return errcode;
94
+ }
95
+
96
+
97
+ int initqual(Project *pr)
98
+ /*
99
+ **--------------------------------------------------------------
100
+ ** Input: none
101
+ ** Output: none
102
+ ** Purpose: re-initializes water quality solver
103
+ **--------------------------------------------------------------
104
+ */
105
+ {
106
+ Network *net = &pr->network;
107
+ Hydraul *hyd = &pr->hydraul;
108
+ Quality *qual = &pr->quality;
109
+ Times *time = &pr->times;
110
+
111
+ int i;
112
+ int errcode = 0;
113
+
114
+ // Re-position hydraulics file
115
+ if (!hyd->OpenHflag)
116
+ {
117
+ fseek(pr->outfile.HydFile, pr->outfile.HydOffset, SEEK_SET);
118
+ }
119
+
120
+ // Set elapsed times to zero
121
+ time->Qtime = 0;
122
+ time->Htime = 0;
123
+ time->Rtime = time->Rstart;
124
+ pr->report.Nperiods = 0;
125
+
126
+ // Initialize node quality
127
+ for (i = 1; i <= net->Nnodes; i++)
128
+ {
129
+ if (qual->Qualflag == TRACE) qual->NodeQual[i] = 0.0;
130
+ else qual->NodeQual[i] = net->Node[i].C0;
131
+ if (net->Node[i].S != NULL) net->Node[i].S->Smass = 0.0;
132
+ }
133
+ if (qual->Qualflag == NONE) return errcode;
134
+
135
+ // Initialize tank quality
136
+ for (i = 1; i <= net->Ntanks; i++)
137
+ {
138
+ net->Tank[i].C = qual->NodeQual[net->Tank[i].Node];
139
+ }
140
+
141
+ // Initialize quality at trace node (if applicable)
142
+ if (qual->Qualflag == TRACE) qual->NodeQual[qual->TraceNode] = 100.0;
143
+
144
+ // Compute Schmidt number
145
+ if (qual->Diffus > 0.0) qual->Sc = hyd->Viscos / qual->Diffus;
146
+ else qual->Sc = 0.0;
147
+
148
+ // Compute unit conversion factor for bulk react. coeff.
149
+ qual->Bucf = getucf(qual->BulkOrder);
150
+ qual->Tucf = getucf(qual->TankOrder);
151
+
152
+ // Check if modeling a reactive substance
153
+ qual->Reactflag = setreactflag(pr);
154
+
155
+ // Reset memory pool used for pipe & tank segments
156
+ qual->FreeSeg = NULL;
157
+ mempool_reset(qual->SegPool);
158
+
159
+ // Create initial set of pipe & tank segments
160
+ initsegs(pr);
161
+
162
+ // Initialize link flow direction indicator
163
+ for (i = 1; i <= net->Nlinks; i++) qual->FlowDir[i] = ZERO_FLOW;
164
+
165
+ // Initialize avg. reaction rates
166
+ qual->Wbulk = 0.0;
167
+ qual->Wwall = 0.0;
168
+ qual->Wtank = 0.0;
169
+ qual->Wsource = 0.0;
170
+
171
+ // Initialize mass balance components
172
+ qual->MassBalance.initial = findstoredmass(pr);
173
+ qual->MassBalance.inflow = 0.0;
174
+ qual->MassBalance.outflow = 0.0;
175
+ qual->MassBalance.reacted = 0.0;
176
+ qual->MassBalance.final = 0.0;
177
+ qual->MassBalance.ratio = 0.0;
178
+ return errcode;
179
+ }
180
+
181
+
182
+ int runqual(Project *pr, long *t)
183
+ /*
184
+ **--------------------------------------------------------------
185
+ ** Input: none
186
+ ** Output: t = current simulation time (sec)
187
+ ** Returns: error code
188
+ ** Purpose: retrieves hydraulics for next hydraulic time step
189
+ ** (at time t) and saves current results to file
190
+ **--------------------------------------------------------------
191
+ */
192
+ {
193
+ Hydraul *hyd = &pr->hydraul;
194
+ Quality *qual = &pr->quality;
195
+ Times *time = &pr->times;
196
+
197
+ long hydtime = 0; // Hydraulic solution time
198
+ long hydstep = 0; // Hydraulic time step
199
+ int errcode = 0;
200
+
201
+ // Update reported simulation time
202
+ *t = time->Qtime;
203
+
204
+ // Read hydraulic solution from hydraulics file
205
+ if (time->Qtime == time->Htime)
206
+ {
207
+ // Read hydraulic results from file
208
+ if (!hyd->OpenHflag)
209
+ {
210
+ if (!readhyd(pr, &hydtime)) return 307;
211
+ if (!readhydstep(pr, &hydstep)) return 307;
212
+ time->Htime = hydtime;
213
+ }
214
+
215
+ // Save current results to output file
216
+ if (time->Htime >= time->Rtime)
217
+ {
218
+ if (pr->outfile.Saveflag)
219
+ {
220
+ errcode = saveoutput(pr);
221
+ pr->report.Nperiods++;
222
+ }
223
+ time->Rtime += time->Rstep;
224
+ }
225
+ if (errcode) return errcode;
226
+
227
+ // If simulating water quality
228
+ if (qual->Qualflag != NONE && time->Qtime < time->Dur)
229
+ {
230
+ // ... compute reaction rate coeffs.
231
+ if (qual->Reactflag && qual->Qualflag != AGE) ratecoeffs(pr);
232
+
233
+ // ... topologically sort network nodes if flow directions change
234
+ if (flowdirchanged(pr) == TRUE)
235
+ {
236
+ errcode = sortnodes(pr);
237
+ }
238
+ }
239
+ if (!hyd->OpenHflag) time->Htime = hydtime + hydstep;
240
+ }
241
+ return errcode;
242
+ }
243
+
244
+
245
+ int nextqual(Project *pr, long *tstep)
246
+ /*
247
+ **--------------------------------------------------------------
248
+ ** Input: none
249
+ ** Output: tstep = time step (sec) over which quality was updated
250
+ ** Returns: error code
251
+ ** Purpose: updates water quality in network until next hydraulic
252
+ ** event occurs (after tstep secs.)
253
+ **--------------------------------------------------------------
254
+ */
255
+ {
256
+ Quality *qual = &pr->quality;
257
+ Times *time = &pr->times;
258
+
259
+ long hydstep; // Time step until next hydraulic event
260
+ long dt, qtime;
261
+ int errcode = 0;
262
+
263
+ // Find time step till next hydraulic event
264
+ *tstep = 0;
265
+ hydstep = 0;
266
+ if (time->Htime <= time->Dur) hydstep = time->Htime - time->Qtime;
267
+
268
+ // Perform water quality routing over this time step
269
+ if (qual->Qualflag != NONE && hydstep > 0)
270
+ {
271
+ // Repeat over each quality time step until tstep is reached
272
+ qtime = 0;
273
+ while (!qual->OutOfMemory && qtime < hydstep)
274
+ {
275
+ dt = MIN(time->Qstep, hydstep - qtime);
276
+ qtime += dt;
277
+ transport(pr, dt);
278
+ }
279
+ if (qual->OutOfMemory) errcode = 101;
280
+ }
281
+
282
+ // Update mass balance ratio
283
+ evalmassbalance(pr);
284
+
285
+ // Update current time
286
+ if (!errcode) *tstep = hydstep;
287
+ time->Qtime += hydstep;
288
+
289
+ // If no more time steps remain
290
+ if (!errcode && *tstep == 0)
291
+ {
292
+ // ... report overall mass balance
293
+ if (qual->Qualflag != NONE && pr->report.Statflag)
294
+ {
295
+ writemassbalance(pr);
296
+ }
297
+
298
+ // ... write the final portion of the binary output file
299
+ if (pr->outfile.Saveflag) errcode = savefinaloutput(pr);
300
+ }
301
+ return errcode;
302
+ }
303
+
304
+
305
+ int stepqual(Project *pr, long *tleft)
306
+ /*
307
+ **--------------------------------------------------------------
308
+ ** Input: none
309
+ ** Output: tleft = time left in simulation
310
+ ** Returns: error code
311
+ ** Purpose: updates quality conditions over a single
312
+ ** quality time step
313
+ **--------------------------------------------------------------
314
+ */
315
+ {
316
+ Quality *qual = &pr->quality;
317
+ Times *time = &pr->times;
318
+
319
+ long dt, hstep, t, tstep;
320
+ int errcode = 0;
321
+
322
+ tstep = time->Qstep;
323
+ do
324
+ {
325
+ // Set local time step to quality time step
326
+ dt = tstep;
327
+
328
+ // Find time step until next hydraulic event
329
+ hstep = time->Htime - time->Qtime;
330
+
331
+ // If next hydraulic event occurs before end of local time step
332
+ if (hstep < dt)
333
+ {
334
+ // ... adjust local time step to next hydraulic event
335
+ dt = hstep;
336
+
337
+ // ... transport quality over local time step
338
+ if (qual->Qualflag != NONE) transport(pr, dt);
339
+ time->Qtime += dt;
340
+
341
+ // ... quit if running quality concurrently with hydraulics
342
+ if (pr->hydraul.OpenHflag) break;
343
+
344
+ // ... otherwise call runqual() to update hydraulics
345
+ errcode = runqual(pr, &t);
346
+ time->Qtime = t;
347
+ }
348
+
349
+ // Otherwise transport quality over current local time step
350
+ else
351
+ {
352
+ if (qual->Qualflag != NONE) transport(pr, dt);
353
+ time->Qtime += dt;
354
+ }
355
+
356
+ // Reduce quality time step by local time step
357
+ tstep -= dt;
358
+ if (qual->OutOfMemory) errcode = 101;
359
+
360
+ } while (!errcode && tstep > 0);
361
+
362
+ // Update mass balance ratio
363
+ evalmassbalance(pr);
364
+
365
+ // Update total simulation time left
366
+ *tleft = time->Dur - time->Qtime;
367
+
368
+ // If no more time steps remain
369
+ if (!errcode && *tleft == 0)
370
+ {
371
+ // ... report overall mass balance
372
+ if (qual->Qualflag != NONE && pr->report.Statflag)
373
+ {
374
+ writemassbalance(pr);
375
+ }
376
+
377
+ // ... write the final portion of the binary output file
378
+ if (pr->outfile.Saveflag) errcode = savefinaloutput(pr);
379
+ }
380
+ return errcode;
381
+ }
382
+
383
+
384
+ int closequal(Project *pr)
385
+ /*
386
+ **--------------------------------------------------------------
387
+ ** Input: none
388
+ ** Output: returns error code
389
+ ** Purpose: closes water quality solver
390
+ **--------------------------------------------------------------
391
+ */
392
+ {
393
+ Quality *qual = &pr->quality;
394
+ int errcode = 0;
395
+
396
+ if (qual->Qualflag != NONE)
397
+ {
398
+ if (qual->SegPool) mempool_delete(qual->SegPool);
399
+ FREE(qual->FirstSeg);
400
+ FREE(qual->LastSeg);
401
+ FREE(qual->PipeRateCoeff);
402
+ FREE(qual->FlowDir);
403
+ FREE(qual->SortedNodes);
404
+ }
405
+ return errcode;
406
+ }
407
+
408
+
409
+ double avgqual(Project *pr, int k)
410
+ /*
411
+ **--------------------------------------------------------------
412
+ ** Input: k = link index
413
+ ** Output: returns quality concentration
414
+ ** Purpose: computes current average quality in link k
415
+ **--------------------------------------------------------------
416
+ */
417
+ {
418
+ Network *net = &pr->network;
419
+ Quality *qual = &pr->quality;
420
+
421
+ double vsum = 0.0, msum = 0.0;
422
+ Pseg seg;
423
+
424
+ if (qual->Qualflag == NONE) return 0.0;
425
+
426
+ // Sum up the quality and volume in each segment of the link
427
+ if (qual->FirstSeg != NULL)
428
+ {
429
+ seg = qual->FirstSeg[k];
430
+ while (seg != NULL)
431
+ {
432
+ vsum += seg->v;
433
+ msum += (seg->c) * (seg->v);
434
+ seg = seg->prev;
435
+ }
436
+ }
437
+
438
+ // Compute average quality if link has volume
439
+ if (vsum > 0.0) return (msum / vsum);
440
+
441
+ // Otherwise use the average quality of the link's end nodes
442
+ else
443
+ {
444
+ return ((qual->NodeQual[net->Link[k].N1] +
445
+ qual->NodeQual[net->Link[k].N2]) / 2.);
446
+ }
447
+ }
448
+
449
+
450
+ double findsourcequal(Project *pr, int n, double volout, long tstep)
451
+ /*
452
+ **---------------------------------------------------------------------
453
+ ** Input: n = node index
454
+ ** volout = volume of node outflow over time step
455
+ ** tstep = current quality time step
456
+ ** Output: returns concentration added by an external quality source.
457
+ ** Purpose: computes contribution (if any) of mass addition from an
458
+ ** external quality source at a node.
459
+ **---------------------------------------------------------------------
460
+ */
461
+ {
462
+ Network *net = &pr->network;
463
+ Hydraul *hyd = &pr->hydraul;
464
+ Quality *qual = &pr->quality;
465
+ Times *time = &pr->times;
466
+
467
+ double massadded = 0.0, c;
468
+ Psource source;
469
+
470
+ // Sources only apply to CHEMICAL analyses
471
+ if (qual->Qualflag != CHEM) return 0.0;
472
+
473
+ // Return 0 if node is not a quality source or has no outflow
474
+ source = net->Node[n].S;
475
+ if (source == NULL) return 0.0;
476
+ if (source->C0 == 0.0) return 0.0;
477
+ if (volout / tstep <= Q_STAGNANT) return 0.0;
478
+
479
+ // Added source concentration depends on source type
480
+ c = sourcequal(pr, source);
481
+ switch (source->Type)
482
+ {
483
+ // Concentration Source:
484
+ case CONCEN:
485
+ if (net->Node[n].Type == JUNCTION)
486
+ {
487
+ // ... source requires a negative demand at the node
488
+ if (hyd->NodeDemand[n] < 0.0)
489
+ {
490
+ c = -c * hyd->NodeDemand[n] * tstep / volout;
491
+ }
492
+ else c = 0.0;
493
+ }
494
+ break;
495
+
496
+ // Mass Inflow Booster Source:
497
+ case MASS:
498
+ // ... convert source input from mass/sec to concentration
499
+ c = c * tstep / volout;
500
+ break;
501
+
502
+ // Setpoint Booster Source:
503
+ // Source quality is difference between source strength
504
+ // & node quality
505
+ case SETPOINT:
506
+ c = MAX(c - qual->NodeQual[n], 0.0);
507
+ break;
508
+
509
+ // Flow-Paced Booster Source:
510
+ // Source quality equals source strength
511
+ case FLOWPACED:
512
+ break;
513
+ }
514
+
515
+ // Source mass added over time step = source concen. * outflow volume
516
+ massadded = c * volout;
517
+
518
+ // Update source's total mass added
519
+ source->Smass += massadded;
520
+
521
+ // Update Wsource
522
+ if (time->Htime >= time->Rstart)
523
+ {
524
+ qual->Wsource += massadded;
525
+ }
526
+ return c;
527
+ }
528
+
529
+
530
+ double sourcequal(Project *pr, Psource source)
531
+ /*
532
+ **--------------------------------------------------------------
533
+ ** Input: source = a water quality source object
534
+ ** Output: returns strength of quality source
535
+ ** Purpose: determines source strength in current time period
536
+ **--------------------------------------------------------------
537
+ */
538
+ {
539
+ Network *net = &pr->network;
540
+ Times *time = &pr->times;
541
+
542
+ int i;
543
+ long k;
544
+ double c;
545
+
546
+ // Get source concentration (or mass flow) in original units
547
+ c = source->C0;
548
+
549
+ // Convert mass flow rate from min. to sec.
550
+ // and convert concen. from liters to cubic feet
551
+ if (source->Type == MASS) c /= 60.0;
552
+ else c /= pr->Ucf[QUALITY];
553
+
554
+ // Apply time pattern if assigned
555
+ i = source->Pat;
556
+ if (i == 0) return c;
557
+ k = ((time->Qtime + time->Pstart) / time->Pstep) %
558
+ (long)net->Pattern[i].Length;
559
+ return (c * net->Pattern[i].F[k]);
560
+ }
561
+
562
+
563
+ void evalmassbalance(Project *pr)
564
+ /*
565
+ **--------------------------------------------------------------
566
+ ** Input: none
567
+ ** Output: none
568
+ ** Purpose: computes the overall mass balance ratio of a
569
+ ** quality constituent.
570
+ **--------------------------------------------------------------
571
+ */
572
+ {
573
+ Quality *qual = &pr->quality;
574
+
575
+ double massin;
576
+ double massout;
577
+ double massreacted;
578
+
579
+ if (qual->Qualflag == NONE) qual->MassBalance.ratio = 1.0;
580
+ else
581
+ {
582
+ qual->MassBalance.final = findstoredmass(pr);
583
+ massin = qual->MassBalance.initial + qual->MassBalance.inflow;
584
+ massout = qual->MassBalance.outflow + qual->MassBalance.final;
585
+ massreacted = qual->MassBalance.reacted;
586
+ if (massreacted > 0.0) massout += massreacted;
587
+ else massin -= massreacted;
588
+ if (massin == 0.0) qual->MassBalance.ratio = 1.0;
589
+ else qual->MassBalance.ratio = massout / massin;
590
+ }
591
+ }
592
+
593
+
594
+ double findstoredmass(Project *pr)
595
+ /*
596
+ **--------------------------------------------------------------
597
+ ** Input: none
598
+ ** Output: returns total constituent mass stored in the network
599
+ ** Purpose: finds the current mass of a constituent stored in
600
+ ** all pipes and tanks.
601
+ **--------------------------------------------------------------
602
+ */
603
+ {
604
+ Network *net = &pr->network;
605
+ Quality *qual = &pr->quality;
606
+
607
+ int i, k;
608
+ double totalmass = 0.0;
609
+ Pseg seg;
610
+
611
+ // Mass residing in each pipe
612
+ for (k = 1; k <= net->Nlinks; k++)
613
+ {
614
+ // Sum up the quality and volume in each segment of the link
615
+ seg = qual->FirstSeg[k];
616
+ while (seg != NULL)
617
+ {
618
+ totalmass += (seg->c) * (seg->v);
619
+ seg = seg->prev;
620
+ }
621
+ }
622
+
623
+ // Mass residing in each tank
624
+ for (i = 1; i <= net->Ntanks; i++)
625
+ {
626
+ // ... skip reservoirs
627
+ if (net->Tank[i].A == 0.0) continue;
628
+
629
+ // ... add up mass in each volume segment
630
+ else
631
+ {
632
+ k = net->Nlinks + i;
633
+ seg = qual->FirstSeg[k];
634
+ while (seg != NULL)
635
+ {
636
+ totalmass += seg->c * seg->v;
637
+ seg = seg->prev;
638
+ }
639
+ }
640
+ }
641
+ return totalmass;
642
+ }
643
+
644
+ int flowdirchanged(Project *pr)
645
+ /*
646
+ **--------------------------------------------------------------
647
+ ** Input: none
648
+ ** Output: returns TRUE if flow direction changes in any link
649
+ ** Purpose: finds new flow directions for each network link.
650
+ **--------------------------------------------------------------
651
+ */
652
+ {
653
+ Hydraul *hyd = &pr->hydraul;
654
+ Quality *qual = &pr->quality;
655
+
656
+ int k;
657
+ int result = FALSE;
658
+ int newdir;
659
+ int olddir;
660
+ double q;
661
+
662
+ // Examine each network link
663
+ for (k = 1; k <= pr->network.Nlinks; k++)
664
+ {
665
+ // Determine sign (+1 or -1) of new flow rate
666
+ olddir = qual->FlowDir[k];
667
+ q = (hyd->LinkStatus[k] <= CLOSED) ? 0.0 : hyd->LinkFlow[k];
668
+ newdir = SGN(q);
669
+
670
+ // Indicate if flow is negligible
671
+ if (fabs(q) < Q_STAGNANT) newdir = 0;
672
+
673
+ // Reverse link's volume segments if flow direction changes sign
674
+ if (newdir * olddir < 0) reversesegs(pr, k);
675
+
676
+ // If flow direction changes either sign or magnitude then set
677
+ // result to true (e.g., if a link's positive flow becomes
678
+ // negligible then the network still needs to be re-sorted)
679
+ if (newdir != olddir) result = TRUE;
680
+
681
+ // ... replace old flow direction with the new direction
682
+ qual->FlowDir[k] = newdir;
683
+ }
684
+ return result;
685
+ }