epanet-plus 0.0.1__cp313-cp313-win_amd64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of epanet-plus might be problematic. Click here for more details.

Files changed (105) hide show
  1. docs/conf.py +67 -0
  2. epanet-msx-src/dispersion.h +27 -0
  3. epanet-msx-src/hash.c +107 -0
  4. epanet-msx-src/hash.h +28 -0
  5. epanet-msx-src/include/epanetmsx.h +104 -0
  6. epanet-msx-src/include/epanetmsx_export.h +42 -0
  7. epanet-msx-src/mathexpr.c +937 -0
  8. epanet-msx-src/mathexpr.h +39 -0
  9. epanet-msx-src/mempool.c +204 -0
  10. epanet-msx-src/mempool.h +24 -0
  11. epanet-msx-src/msxchem.c +1285 -0
  12. epanet-msx-src/msxcompiler.c +368 -0
  13. epanet-msx-src/msxdict.h +42 -0
  14. epanet-msx-src/msxdispersion.c +586 -0
  15. epanet-msx-src/msxerr.c +116 -0
  16. epanet-msx-src/msxfile.c +260 -0
  17. epanet-msx-src/msxfuncs.c +175 -0
  18. epanet-msx-src/msxfuncs.h +35 -0
  19. epanet-msx-src/msxinp.c +1504 -0
  20. epanet-msx-src/msxout.c +398 -0
  21. epanet-msx-src/msxproj.c +791 -0
  22. epanet-msx-src/msxqual.c +2011 -0
  23. epanet-msx-src/msxrpt.c +400 -0
  24. epanet-msx-src/msxtank.c +422 -0
  25. epanet-msx-src/msxtoolkit.c +1164 -0
  26. epanet-msx-src/msxtypes.h +551 -0
  27. epanet-msx-src/msxutils.c +524 -0
  28. epanet-msx-src/msxutils.h +56 -0
  29. epanet-msx-src/newton.c +158 -0
  30. epanet-msx-src/newton.h +34 -0
  31. epanet-msx-src/rk5.c +287 -0
  32. epanet-msx-src/rk5.h +39 -0
  33. epanet-msx-src/ros2.c +293 -0
  34. epanet-msx-src/ros2.h +35 -0
  35. epanet-msx-src/smatrix.c +816 -0
  36. epanet-msx-src/smatrix.h +29 -0
  37. epanet-src/AUTHORS +60 -0
  38. epanet-src/LICENSE +21 -0
  39. epanet-src/enumstxt.h +151 -0
  40. epanet-src/epanet.c +5937 -0
  41. epanet-src/epanet2.c +961 -0
  42. epanet-src/epanet2.def +131 -0
  43. epanet-src/errors.dat +79 -0
  44. epanet-src/flowbalance.c +186 -0
  45. epanet-src/funcs.h +219 -0
  46. epanet-src/genmmd.c +1000 -0
  47. epanet-src/hash.c +177 -0
  48. epanet-src/hash.h +28 -0
  49. epanet-src/hydcoeffs.c +1303 -0
  50. epanet-src/hydraul.c +1164 -0
  51. epanet-src/hydsolver.c +781 -0
  52. epanet-src/hydstatus.c +442 -0
  53. epanet-src/include/epanet2.h +466 -0
  54. epanet-src/include/epanet2_2.h +1962 -0
  55. epanet-src/include/epanet2_enums.h +518 -0
  56. epanet-src/inpfile.c +884 -0
  57. epanet-src/input1.c +672 -0
  58. epanet-src/input2.c +970 -0
  59. epanet-src/input3.c +2265 -0
  60. epanet-src/leakage.c +527 -0
  61. epanet-src/mempool.c +146 -0
  62. epanet-src/mempool.h +24 -0
  63. epanet-src/output.c +853 -0
  64. epanet-src/project.c +1691 -0
  65. epanet-src/quality.c +695 -0
  66. epanet-src/qualreact.c +800 -0
  67. epanet-src/qualroute.c +696 -0
  68. epanet-src/report.c +1559 -0
  69. epanet-src/rules.c +1500 -0
  70. epanet-src/smatrix.c +871 -0
  71. epanet-src/text.h +508 -0
  72. epanet-src/types.h +928 -0
  73. epanet-src/util/cstr_helper.c +59 -0
  74. epanet-src/util/cstr_helper.h +38 -0
  75. epanet-src/util/errormanager.c +92 -0
  76. epanet-src/util/errormanager.h +39 -0
  77. epanet-src/util/filemanager.c +212 -0
  78. epanet-src/util/filemanager.h +81 -0
  79. epanet-src/validate.c +408 -0
  80. epanet.cp313-win_amd64.pyd +0 -0
  81. epanet_plus/VERSION +1 -0
  82. epanet_plus/__init__.py +8 -0
  83. epanet_plus/epanet_plus.c +118 -0
  84. epanet_plus/epanet_toolkit.py +2730 -0
  85. epanet_plus/epanet_wrapper.py +2414 -0
  86. epanet_plus/include/epanet_plus.h +9 -0
  87. epanet_plus-0.0.1.dist-info/METADATA +152 -0
  88. epanet_plus-0.0.1.dist-info/RECORD +105 -0
  89. epanet_plus-0.0.1.dist-info/WHEEL +5 -0
  90. epanet_plus-0.0.1.dist-info/licenses/LICENSE +21 -0
  91. epanet_plus-0.0.1.dist-info/top_level.txt +11 -0
  92. examples/basic_usage.py +35 -0
  93. python-extension/ext.c +344 -0
  94. python-extension/pyepanet.c +2133 -0
  95. python-extension/pyepanet.h +143 -0
  96. python-extension/pyepanet2.c +1823 -0
  97. python-extension/pyepanet2.h +141 -0
  98. python-extension/pyepanet_plus.c +37 -0
  99. python-extension/pyepanet_plus.h +4 -0
  100. python-extension/pyepanetmsx.c +388 -0
  101. python-extension/pyepanetmsx.h +35 -0
  102. tests/test_epanet.py +16 -0
  103. tests/test_epanetmsx.py +36 -0
  104. tests/test_epyt.py +114 -0
  105. tests/test_load_inp_from_buffer.py +18 -0
epanet-src/qualroute.c ADDED
@@ -0,0 +1,696 @@
1
+ /*
2
+ ******************************************************************************
3
+ Project: OWA EPANET
4
+ Version: 2.3
5
+ Module: qualroute.c
6
+ Description: computes water quality transport over a single time step
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 <math.h>
17
+
18
+ #include "mempool.h"
19
+ #include "types.h"
20
+
21
+ // Macro to compute the volume of a link
22
+ #define LINKVOL(k) (0.785398 * net->Link[(k)].Len * SQR(net->Link[(k)].Diam))
23
+
24
+ // Macro to get link flow compatible with flow saved to hydraulics file
25
+ #define LINKFLOW(k) ((hyd->LinkStatus[k] <= CLOSED) ? 0.0 : hyd->LinkFlow[k])
26
+
27
+ // Exported functions
28
+ int sortnodes(Project *);
29
+ void transport(Project *, long);
30
+ void initsegs(Project *);
31
+ void reversesegs(Project *, int);
32
+ void addseg(Project *, int, double, double);
33
+
34
+ // Imported functions
35
+ extern double findsourcequal(Project *, int, double, long);
36
+ extern void reactpipes(Project *, long);
37
+ extern void reacttanks(Project *, long);
38
+ extern double mixtank(Project *, int, double, double, double);
39
+
40
+ // Local functions
41
+ static void evalnodeinflow(Project *, int, long, double *, double *);
42
+ static void evalnodeoutflow(Project *, int, double, long);
43
+ static double findnodequal(Project *, int, double, double, double, long);
44
+ static double noflowqual(Project *, int);
45
+ static void updatemassbalance(Project *, int, double, double, long);
46
+ static int selectnonstacknode(Project *, int, int *);
47
+
48
+
49
+ void transport(Project *pr, long tstep)
50
+ /*
51
+ **--------------------------------------------------------------
52
+ ** Input: tstep = length of current time step
53
+ ** Output: none
54
+ ** Purpose: transports constituent mass through the pipe network
55
+ ** under a period of constant hydraulic conditions.
56
+ **--------------------------------------------------------------
57
+ */
58
+ {
59
+ Network *net = &pr->network;
60
+ Hydraul *hyd = &pr->hydraul;
61
+ Quality *qual = &pr->quality;
62
+
63
+ int j, k, m, n;
64
+ double volin, massin, volout, nodequal;
65
+ Padjlist alink;
66
+
67
+ // React contents of each pipe and tank
68
+ if (qual->Reactflag)
69
+ {
70
+ reactpipes(pr, tstep);
71
+ reacttanks(pr, tstep);
72
+ }
73
+
74
+ // Analyze each node in topological order
75
+ for (j = 1; j <= net->Nnodes; j++)
76
+ {
77
+ // ... index of node to be processed
78
+ n = qual->SortedNodes[j];
79
+
80
+ // ... zero out mass & flow volumes for this node
81
+ volin = 0.0;
82
+ massin = 0.0;
83
+ volout = 0.0;
84
+
85
+ // ... examine each link with flow into the node
86
+ for (alink = net->Adjlist[n]; alink != NULL; alink = alink->next)
87
+ {
88
+ // ... k is index of next link incident on node n
89
+ k = alink->link;
90
+
91
+ // ... link has flow into node - add it to node's inflow
92
+ // (m is index of link's downstream node)
93
+ m = net->Link[k].N2;
94
+ if (qual->FlowDir[k] < 0) m = net->Link[k].N1;
95
+ if (m == n)
96
+ {
97
+ evalnodeinflow(pr, k, tstep, &volin, &massin);
98
+ }
99
+
100
+ // ... link has flow out of node - add it to node's outflow
101
+ else volout += fabs(LINKFLOW(k));
102
+ }
103
+
104
+ // ... if node is a junction, add on any external outflow (e.g., demands)
105
+ if (net->Node[n].Type == JUNCTION)
106
+ {
107
+ volout += MAX(0.0, hyd->NodeDemand[n]);
108
+ }
109
+
110
+ // ... convert from outflow rate to volume
111
+ volout *= tstep;
112
+
113
+ // ... find the concentration of flow leaving the node
114
+ nodequal = findnodequal(pr, n, volin, massin, volout, tstep);
115
+
116
+ // ... examine each link with flow out of the node
117
+ for (alink = net->Adjlist[n]; alink != NULL; alink = alink->next)
118
+ {
119
+ // ... link k incident on node n has upstream node m equal to n
120
+ k = alink->link;
121
+ m = net->Link[k].N1;
122
+ if (qual->FlowDir[k] < 0) m = net->Link[k].N2;
123
+ if (m == n)
124
+ {
125
+ // ... send flow at new node concen. into link
126
+ evalnodeoutflow(pr, k, nodequal, tstep);
127
+ }
128
+ }
129
+ updatemassbalance(pr, n, massin, volout, tstep);
130
+ }
131
+ }
132
+
133
+ void evalnodeinflow(Project *pr, int k, long tstep, double *volin,
134
+ double *massin)
135
+ /*
136
+ **--------------------------------------------------------------
137
+ ** Input: k = link index
138
+ ** tstep = quality routing time step
139
+ ** Output: volin = flow volume entering a node
140
+ ** massin = constituent mass entering a node
141
+ ** Purpose: adds the contribution of a link's outflow volume
142
+ ** and constituent mass to the total inflow into its
143
+ ** downstream node over a time step.
144
+ **--------------------------------------------------------------
145
+ */
146
+ {
147
+ Hydraul *hyd = &pr->hydraul;
148
+ Quality *qual = &pr->quality;
149
+
150
+ double q, v, vseg;
151
+ Pseg seg;
152
+
153
+ // Get flow rate (q) and flow volume (v) through link
154
+ q = LINKFLOW(k);
155
+ v = fabs(q) * tstep;
156
+
157
+ // Transport flow volume v from link's leading segments into downstream
158
+ // node, removing segments once their full volume is consumed
159
+ while (v > 0.0)
160
+ {
161
+ seg = qual->FirstSeg[k];
162
+ if (!seg) break;
163
+
164
+ // ... volume transported from first segment is smaller of
165
+ // remaining flow volume & segment volume
166
+ vseg = seg->v;
167
+ vseg = MIN(vseg, v);
168
+
169
+ // ... update total volume & mass entering downstream node
170
+ *volin += vseg;
171
+ *massin += vseg * seg->c;
172
+
173
+ // ... reduce remaining flow volume by amount transported
174
+ v -= vseg;
175
+
176
+ // ... if all of segment's volume was transferred
177
+ if (v >= 0.0 && vseg >= seg->v)
178
+ {
179
+ // ... replace this leading segment with the one behind it
180
+ qual->FirstSeg[k] = seg->prev;
181
+ if (qual->FirstSeg[k] == NULL) qual->LastSeg[k] = NULL;
182
+
183
+ // ... recycle the used up segment
184
+ seg->prev = qual->FreeSeg;
185
+ qual->FreeSeg = seg;
186
+ qual->MassBalance.segCount--;
187
+ }
188
+
189
+ // ... otherwise just reduce this segment's volume
190
+ else seg->v -= vseg;
191
+ }
192
+ }
193
+
194
+
195
+ double findnodequal(Project *pr, int n, double volin,
196
+ double massin, double volout, long tstep)
197
+ /*
198
+ **--------------------------------------------------------------
199
+ ** Input: n = node index
200
+ ** volin = flow volume entering node
201
+ ** massin = mass entering node
202
+ ** volout = flow volume leaving node
203
+ ** tstep = length of current time step
204
+ ** Output: returns water quality in a node's outflow
205
+ ** Purpose: computes a node's new quality from its inflow
206
+ ** volume and mass, including any source contribution.
207
+ **--------------------------------------------------------------
208
+ */
209
+ {
210
+ Network *net = &pr->network;
211
+ Hydraul *hyd = &pr->hydraul;
212
+ Quality *qual = &pr->quality;
213
+
214
+ // Node is a junction - update its water quality
215
+ if (net->Node[n].Type == JUNCTION)
216
+ {
217
+ // ... dilute inflow with any external negative demand
218
+ volin -= MIN(0.0, hyd->NodeDemand[n]) * tstep;
219
+
220
+ // ... new concen. is mass inflow / volume inflow
221
+ if (volin > 0.0) qual->NodeQual[n] = massin / volin;
222
+
223
+ // ... if no inflow adjust quality for reaction in connecting pipes
224
+ else if (qual->Reactflag) qual->NodeQual[n] = noflowqual(pr, n);
225
+ }
226
+
227
+ // Node is a tank - use its mixing model to update its quality
228
+ else if (net->Node[n].Type == TANK)
229
+ {
230
+ qual->NodeQual[n] = mixtank(pr, n, volin, massin, volout);
231
+ }
232
+
233
+ // Add any external quality source onto node's concen.
234
+ qual->SourceQual = 0.0;
235
+
236
+ // For source tracing analysis find tracer added at source node
237
+ if (qual->Qualflag == TRACE)
238
+ {
239
+ if (n == qual->TraceNode)
240
+ {
241
+ // ... quality added to network is difference between tracer
242
+ // concentration (100 mg/L) and current node quality
243
+ if (net->Node[n].Type == RESERVOIR) qual->SourceQual = 100.0;
244
+ else qual->SourceQual = MAX(100.0 - qual->NodeQual[n], 0.0);
245
+ qual->NodeQual[n] = 100.0;
246
+ }
247
+ return qual->NodeQual[n];
248
+ }
249
+
250
+ // Find quality contributed by any external chemical source
251
+ else qual->SourceQual = findsourcequal(pr, n, volout, tstep);
252
+ if (qual->SourceQual == 0.0) return qual->NodeQual[n];
253
+
254
+ // Combine source quality with node quality
255
+ switch (net->Node[n].Type)
256
+ {
257
+ case JUNCTION:
258
+ qual->NodeQual[n] += qual->SourceQual;
259
+ return qual->NodeQual[n];
260
+
261
+ case TANK:
262
+ return qual->NodeQual[n] + qual->SourceQual;
263
+
264
+ case RESERVOIR:
265
+ qual->NodeQual[n] = qual->SourceQual;
266
+ return qual->SourceQual;
267
+ }
268
+ return qual->NodeQual[n];
269
+ }
270
+
271
+
272
+ double noflowqual(Project *pr, int n)
273
+ /*
274
+ **--------------------------------------------------------------
275
+ ** Input: n = node index
276
+ ** Output: quality for node n
277
+ ** Purpose: sets the quality for a junction node that has no
278
+ ** inflow to the average of the quality in its
279
+ ** adjoining link segments.
280
+ ** Note: this function is only used for reactive substances.
281
+ **--------------------------------------------------------------
282
+ */
283
+ {
284
+ Network *net = &pr->network;
285
+ Quality *qual = &pr->quality;
286
+
287
+ int k, inflow, kount = 0;
288
+ double c = 0.0;
289
+ FlowDirection dir;
290
+ Padjlist alink;
291
+
292
+ // Examine each link incident on the node
293
+ for (alink = net->Adjlist[n]; alink != NULL; alink = alink->next)
294
+ {
295
+ // ... index of an incident link
296
+ k = alink->link;
297
+ dir = qual->FlowDir[k];
298
+
299
+ // Node n is link's downstream node - add quality
300
+ // of link's first segment to average
301
+ if (net->Link[k].N2 == n && dir >= 0) inflow = TRUE;
302
+ else if (net->Link[k].N1 == n && dir < 0) inflow = TRUE;
303
+ else inflow = FALSE;
304
+ if (inflow == TRUE && qual->FirstSeg[k] != NULL)
305
+ {
306
+ c += qual->FirstSeg[k]->c;
307
+ kount++;
308
+ }
309
+
310
+ // Node n is link's upstream node - add quality
311
+ // of link's last segment to average
312
+ else if (inflow == FALSE && qual->LastSeg[k] != NULL)
313
+ {
314
+ c += qual->LastSeg[k]->c;
315
+ kount++;
316
+ }
317
+ }
318
+ if (kount > 0) c = c / (double)kount;
319
+ return c;
320
+ }
321
+
322
+
323
+ void evalnodeoutflow(Project *pr, int k, double c, long tstep)
324
+ /*
325
+ **--------------------------------------------------------------
326
+ ** Input: k = link index
327
+ ** c = quality from upstream node
328
+ ** tstep = time step
329
+ ** Output: none
330
+ ** Purpose: releases flow volume and mass from the upstream
331
+ ** node of a link over a time step.
332
+ **--------------------------------------------------------------
333
+ */
334
+ {
335
+ Hydraul *hyd = &pr->hydraul;
336
+ Quality *qual = &pr->quality;
337
+
338
+ double v;
339
+ Pseg seg;
340
+
341
+ // Find flow volume (v) released over time step
342
+ v = fabs(LINKFLOW(k)) * tstep;
343
+ if (v == 0.0) return;
344
+
345
+ // Release flow and mass into upstream end of the link
346
+
347
+ // ... case where link has a last (most upstream) segment
348
+ seg = qual->LastSeg[k];
349
+ if (seg)
350
+ {
351
+ // ... if node quality close to segment quality then mix
352
+ // the nodal outflow volume with the segment's volume
353
+ if (fabs(seg->c - c) < qual->Ctol)
354
+ {
355
+ seg->c = (seg->c*seg->v + c*v) / (seg->v + v);
356
+ seg->v += v;
357
+ }
358
+
359
+ // ... otherwise add a new segment at upstream end of link
360
+ else addseg(pr, k, v, c);
361
+ }
362
+
363
+ // ... link has no segments so add one
364
+ else addseg(pr, k, v, c);
365
+ }
366
+
367
+
368
+ void updatemassbalance(Project *pr, int n, double massin,
369
+ double volout, long tstep)
370
+ /*
371
+ **--------------------------------------------------------------
372
+ ** Input: n = node index
373
+ ** massin = mass inflow to node
374
+ ** volout = outflow volume from node
375
+ ** Output: none
376
+ ** Purpose: Adds a node's external mass inflow and outflow
377
+ ** over the current time step to the network's
378
+ ** overall mass balance.
379
+ **--------------------------------------------------------------
380
+ */
381
+ {
382
+ Network *net = &pr->network;
383
+ Hydraul *hyd = &pr->hydraul;
384
+ Quality *qual = &pr->quality;
385
+
386
+ double masslost = 0.0,
387
+ massadded = 0.0;
388
+
389
+ switch (net->Node[n].Type)
390
+ {
391
+ // Junctions lose mass from outflow demand & gain it from source inflow
392
+ case JUNCTION:
393
+ masslost = MAX(0.0, hyd->NodeDemand[n]) * tstep * qual->NodeQual[n];
394
+ massadded = qual->SourceQual * volout;
395
+ break;
396
+
397
+ // Reservoirs add mass from quality source if specified or from a fixed
398
+ // initial quality
399
+ case RESERVOIR:
400
+ masslost = massin;
401
+ if (qual->SourceQual > 0.0) massadded = qual->SourceQual * volout;
402
+ else massadded = qual->NodeQual[n] * volout;
403
+ break;
404
+
405
+ // Tanks add mass only from external source inflow
406
+ case TANK:
407
+ massadded = qual->SourceQual * volout;
408
+ break;
409
+ }
410
+ qual->MassBalance.outflow += masslost;
411
+ qual->MassBalance.inflow += massadded;
412
+ }
413
+
414
+
415
+ int sortnodes(Project *pr)
416
+ /*
417
+ **--------------------------------------------------------------
418
+ ** Input: none
419
+ ** Output: returns an error code
420
+ ** Purpose: topologically sorts nodes from upstream to downstream.
421
+ ** Note: links with negligible flow are ignored since they can
422
+ ** create spurious cycles that cause the sort to fail.
423
+ **--------------------------------------------------------------
424
+ */
425
+ {
426
+ Network *net = &pr->network;
427
+ Quality *qual = &pr->quality;
428
+
429
+ int i, j, k, n;
430
+ int *indegree = NULL;
431
+ int *stack = NULL;
432
+ int stacksize = 0;
433
+ int numsorted = 0;
434
+ int errcode = 0;
435
+ FlowDirection dir;
436
+ Padjlist alink;
437
+
438
+ // Allocate an array to count # links with inflow to each node
439
+ // and for a stack to hold nodes waiting to be processed
440
+ indegree = (int *)calloc(net->Nnodes + 1, sizeof(int));
441
+ stack = (int *)calloc(net->Nnodes + 1, sizeof(int));
442
+ if (indegree && stack)
443
+ {
444
+ // Count links with "non-negligible" inflow to each node
445
+ for (k = 1; k <= net->Nlinks; k++)
446
+ {
447
+ dir = qual->FlowDir[k];
448
+ if (dir == POSITIVE) n = net->Link[k].N2;
449
+ else if (dir == NEGATIVE) n = net->Link[k].N1;
450
+ else continue;
451
+ indegree[n]++;
452
+ }
453
+
454
+ // Place nodes with no inflow onto a stack
455
+ for (i = 1; i <= net->Nnodes; i++)
456
+ {
457
+ if (indegree[i] == 0)
458
+ {
459
+ stacksize++;
460
+ stack[stacksize] = i;
461
+ }
462
+ }
463
+
464
+ // Examine each node on the stack until none are left
465
+ while (numsorted < net->Nnodes)
466
+ {
467
+ // ... if stack is empty then a cycle exists
468
+ if (stacksize == 0)
469
+ {
470
+ // ... add a non-sorted node connected to a sorted one to stack
471
+ j = selectnonstacknode(pr, numsorted, indegree);
472
+ if (j == 0) break; // This shouldn't happen.
473
+ indegree[j] = 0;
474
+ stacksize++;
475
+ stack[stacksize] = j;
476
+ }
477
+
478
+ // ... make the last node added to the stack the next
479
+ // in sorted order & remove it from the stack
480
+ i = stack[stacksize];
481
+ stacksize--;
482
+ numsorted++;
483
+ qual->SortedNodes[numsorted] = i;
484
+
485
+ // ... for each outflow link from this node reduce the in-degree
486
+ // of its downstream node
487
+ for (alink = net->Adjlist[i]; alink != NULL; alink = alink->next)
488
+ {
489
+ // ... k is the index of the next link incident on node i
490
+ k = alink->link;
491
+
492
+ // ... skip link if flow is negligible
493
+ if (qual->FlowDir[k] == 0) continue;
494
+
495
+ // ... link has flow out of node (downstream node n not equal to i)
496
+ n = net->Link[k].N2;
497
+ if (qual->FlowDir[k] < 0) n = net->Link[k].N1;
498
+
499
+ // ... reduce degree of node n
500
+ if (n != i && indegree[n] > 0)
501
+ {
502
+ indegree[n]--;
503
+
504
+ // ... no more degree left so add node n to stack
505
+ if (indegree[n] == 0)
506
+ {
507
+ stacksize++;
508
+ stack[stacksize] = n;
509
+ }
510
+ }
511
+ }
512
+ }
513
+ }
514
+ else errcode = 101;
515
+ if (numsorted < net->Nnodes) errcode = 120;
516
+ FREE(indegree);
517
+ FREE(stack);
518
+ return errcode;
519
+ }
520
+
521
+
522
+ int selectnonstacknode(Project *pr, int numsorted, int *indegree)
523
+ /*
524
+ **--------------------------------------------------------------
525
+ ** Input: numsorted = number of nodes that have been sorted
526
+ ** indegree = number of inflow links to each node
527
+ ** Output: returns a node index
528
+ ** Purpose: selects a next node for sorting when a cycle exists.
529
+ **--------------------------------------------------------------
530
+ */
531
+ {
532
+ Network *net = &pr->network;
533
+ Quality *qual = &pr->quality;
534
+
535
+ int i, m, n;
536
+ Padjlist alink;
537
+
538
+ // Examine each sorted node in last in - first out order
539
+ for (i = numsorted; i > 0; i--)
540
+ {
541
+ // For each link connected to the sorted node
542
+ m = qual->SortedNodes[i];
543
+ for (alink = net->Adjlist[m]; alink != NULL; alink = alink->next)
544
+ {
545
+ // ... n is the node of link k opposite to node m
546
+ n = alink->node;
547
+
548
+ // ... select node n if it still has inflow links
549
+ if (indegree[n] > 0) return n;
550
+ }
551
+ }
552
+
553
+ // If no node was selected by the above process then return the
554
+ // first node that still has inflow links remaining
555
+ for (i = 1; i <= net->Nnodes; i++)
556
+ {
557
+ if (indegree[i] > 0) return i;
558
+ }
559
+
560
+ // If all else fails return 0 indicating that no node was selected
561
+ return 0;
562
+ }
563
+
564
+
565
+ void initsegs(Project *pr)
566
+ /*
567
+ **--------------------------------------------------------------
568
+ ** Input: none
569
+ ** Output: none
570
+ ** Purpose: initializes water quality volume segments in each
571
+ ** pipe and tank.
572
+ **--------------------------------------------------------------
573
+ */
574
+ {
575
+ Network *net = &pr->network;
576
+ Quality *qual = &pr->quality;
577
+
578
+ int j, k;
579
+ double c, v, v1;
580
+
581
+ // Add one segment with assigned downstream node quality to each pipe
582
+ for (k = 1; k <= net->Nlinks; k++)
583
+ {
584
+ qual->FirstSeg[k] = NULL;
585
+ qual->LastSeg[k] = NULL;
586
+ if (net->Link[k].Type == PIPE)
587
+ {
588
+ v = LINKVOL(k);
589
+ j = net->Link[k].N2;
590
+ c = qual->NodeQual[j];
591
+ addseg(pr, k, v, c);
592
+ }
593
+ }
594
+
595
+ // Initialize segments in tanks
596
+ for (j = 1; j <= net->Ntanks; j++)
597
+ {
598
+ // Skip reservoirs
599
+ if (net->Tank[j].A == 0.0) continue;
600
+
601
+ // Establish initial tank quality & volume
602
+ k = net->Tank[j].Node;
603
+ c = net->Node[k].C0;
604
+ v = net->Tank[j].V0;
605
+
606
+ // Create one volume segment for entire tank
607
+ k = net->Nlinks + j;
608
+ qual->FirstSeg[k] = NULL;
609
+ qual->LastSeg[k] = NULL;
610
+ addseg(pr, k, v, c);
611
+
612
+ // Create a 2nd segment for the 2-compartment tank model
613
+ if (!qual->OutOfMemory && net->Tank[j].MixModel == MIX2)
614
+ {
615
+ // ... mixing zone segment
616
+ v1 = MAX(0, v - net->Tank[j].V1frac * net->Tank[j].Vmax);
617
+ qual->FirstSeg[k]->v = v1;
618
+
619
+ // ... stagnant zone segment
620
+ v = v - v1;
621
+ addseg(pr, k, v, c);
622
+ }
623
+ }
624
+ }
625
+
626
+
627
+ void reversesegs(Project *pr, int k)
628
+ /*
629
+ **--------------------------------------------------------------
630
+ ** Input: k = link index
631
+ ** Output: none
632
+ ** Purpose: re-orients a link's segments when flow reverses.
633
+ **--------------------------------------------------------------
634
+ */
635
+ {
636
+ Quality *qual = &pr->quality;
637
+ Pseg seg, nseg, pseg;
638
+
639
+ seg = qual->FirstSeg[k];
640
+ qual->FirstSeg[k] = qual->LastSeg[k];
641
+ qual->LastSeg[k] = seg;
642
+ pseg = NULL;
643
+ while (seg != NULL)
644
+ {
645
+ nseg = seg->prev;
646
+ seg->prev = pseg;
647
+ pseg = seg;
648
+ seg = nseg;
649
+ }
650
+ }
651
+
652
+
653
+ void addseg(Project *pr, int k, double v, double c)
654
+ /*
655
+ **-------------------------------------------------------------
656
+ ** Input: k = segment chain index
657
+ ** v = segment volume
658
+ ** c = segment quality
659
+ ** Output: none
660
+ ** Purpose: adds a segment to the start of a link
661
+ ** upstream of its current last segment.
662
+ **-------------------------------------------------------------
663
+ */
664
+ {
665
+ Quality *qual = &pr->quality;
666
+ Pseg seg;
667
+
668
+ // Grab the next free segment from the segment pool if available
669
+ if (qual->FreeSeg != NULL)
670
+ {
671
+ seg = qual->FreeSeg;
672
+ qual->FreeSeg = seg->prev;
673
+ }
674
+
675
+ // Otherwise allocate a new segment
676
+ else
677
+ {
678
+ seg = (struct Sseg *) mempool_alloc(qual->SegPool, sizeof(struct Sseg));
679
+ if (seg == NULL)
680
+ {
681
+ qual->OutOfMemory = TRUE;
682
+ return;
683
+ }
684
+ }
685
+
686
+ // Assign volume and quality to the segment
687
+ seg->v = v;
688
+ seg->c = c;
689
+
690
+ // Add the new segment to the end of the segment chain
691
+ seg->prev = NULL;
692
+ if (qual->FirstSeg[k] == NULL) qual->FirstSeg[k] = seg;
693
+ if (qual->LastSeg[k] != NULL) qual->LastSeg[k]->prev = seg;
694
+ qual->LastSeg[k] = seg;
695
+ qual->MassBalance.segCount++;
696
+ }