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