epanet-plus 0.0.1__cp312-cp312-win32.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.cp312-win32.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/report.c ADDED
@@ -0,0 +1,1559 @@
1
+ /*
2
+ ******************************************************************************
3
+ Project: OWA EPANET
4
+ Version: 2.3
5
+ Module: report.c
6
+ Description: procedures for writing formatted text to a report file
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
+
18
+ #ifdef _WIN32
19
+ #define snprintf _snprintf
20
+ #endif
21
+
22
+ #include <math.h>
23
+ #include <time.h>
24
+
25
+ #include "types.h"
26
+ #include "funcs.h"
27
+ #include "hash.h"
28
+ #include "text.h"
29
+
30
+ #define MAXCOUNT 10 // Max. # of disconnected nodes listed
31
+
32
+ // Defined in ENUMSTXT.H
33
+ extern char *NodeTxt[];
34
+ extern char *LinkTxt[];
35
+ extern char *StatTxt[];
36
+ extern char *TstatTxt[];
37
+ extern char *RptFormTxt[];
38
+ extern char *DemandModelTxt[];
39
+
40
+ // Local functions
41
+ typedef REAL4 *Pfloat;
42
+ static void writenodetable(Project *, Pfloat *);
43
+ static void writelinktable(Project *, Pfloat *);
44
+ static void writeenergy(Project *);
45
+ static int writeresults(Project *);
46
+ static int disconnected(Project *);
47
+ static void marknodes(Project *, int, int *, char *);
48
+ static void getclosedlink(Project *, int, char *, int *);
49
+ static void writelimits(Project *, int, int);
50
+ static int checklimits(Report *, double *, int, int);
51
+ static char *fillstr(char *, char, int);
52
+ static int getnodetype(Network *, int);
53
+
54
+ int clearreport(Project *pr)
55
+ /*
56
+ **------------------------------------------------------
57
+ ** Input: none
58
+ ** Output: returns error code
59
+ ** Purpose: clears contents of a project's report file
60
+ **------------------------------------------------------
61
+ */
62
+ {
63
+ Report *rpt = &pr->report;
64
+ if (rpt->RptFile == NULL) return 0;
65
+ if (freopen(rpt->Rpt1Fname, "w", rpt->RptFile) == NULL) return 303;
66
+ writelogo(pr);
67
+ return 0;
68
+ }
69
+
70
+ int copyreport(Project* pr, const char *filename)
71
+ /*
72
+ **------------------------------------------------------
73
+ ** Input: filename = name of file to copy to
74
+ ** Output: returns error code
75
+ ** Purpose: copies contents of a project's report file
76
+ **------------------------------------------------------
77
+ */
78
+ {
79
+ FILE *tfile;
80
+ int c;
81
+ Report *rpt = &pr->report;
82
+
83
+ // Check that project's report file exists
84
+ if (rpt->RptFile == NULL) return 0;
85
+
86
+ // Open the new destination file
87
+ tfile = fopen(filename, "w");
88
+ if (tfile == NULL) return 303;
89
+
90
+ // Re-open project's report file in read mode
91
+ fclose(rpt->RptFile);
92
+ rpt->RptFile = fopen(rpt->Rpt1Fname, "r");
93
+
94
+ // Copy contents of project's report file
95
+ if (rpt->RptFile)
96
+ {
97
+ while ((c = fgetc(rpt->RptFile)) != EOF) fputc(c, tfile);
98
+ fclose(rpt->RptFile);
99
+ }
100
+
101
+ // Close destination file
102
+ fclose(tfile);
103
+
104
+ // Re-open project's report file in append mode
105
+ rpt->RptFile = fopen(rpt->Rpt1Fname, "a");
106
+ if (rpt->RptFile == NULL) return 303;
107
+ return 0;
108
+ }
109
+
110
+ int writereport(Project *pr)
111
+ /*
112
+ **------------------------------------------------------
113
+ ** Input: none
114
+ ** Output: returns error code
115
+ ** Purpose: writes formatted output report to file
116
+ **------------------------------------------------------
117
+ */
118
+ {
119
+ Report *rpt = &pr->report;
120
+ Parser *parser = &pr->parser;
121
+
122
+ int tflag;
123
+ FILE *tfile;
124
+ int errcode = 0;
125
+
126
+ // If no secondary report file specified then
127
+ // write formatted output to primary report file
128
+ rpt->Fprinterr = FALSE;
129
+ if (rpt->Rptflag && strlen(rpt->Rpt2Fname) == 0 && rpt->RptFile != NULL)
130
+ {
131
+ if (rpt->Energyflag) writeenergy(pr);
132
+ errcode = writeresults(pr);
133
+ }
134
+
135
+ // A secondary report file was specified
136
+ else if (strlen(rpt->Rpt2Fname) > 0)
137
+ {
138
+ // If secondary report file has same name as either input
139
+ // or primary report file then use primary report file.
140
+ if (strcomp(rpt->Rpt2Fname, parser->InpFname) ||
141
+ strcomp(rpt->Rpt2Fname, rpt->Rpt1Fname))
142
+ {
143
+ if (rpt->Energyflag) writeenergy(pr);
144
+ errcode = writeresults(pr);
145
+ }
146
+
147
+ // Otherwise write report to secondary report file
148
+ else
149
+ {
150
+ // Try to open file
151
+ tfile = rpt->RptFile;
152
+ tflag = rpt->Rptflag;
153
+ if ((rpt->RptFile = fopen(rpt->Rpt2Fname, "wt")) == NULL)
154
+ {
155
+ rpt->RptFile = tfile;
156
+ rpt->Rptflag = tflag;
157
+ errcode = 303;
158
+ }
159
+
160
+ // Write full formatted report to file
161
+ else
162
+ {
163
+ rpt->Rptflag = 1;
164
+ writelogo(pr);
165
+ if (rpt->Summaryflag) writesummary(pr);
166
+ if (rpt->Energyflag) writeenergy(pr);
167
+ errcode = writeresults(pr);
168
+ fclose(rpt->RptFile);
169
+ rpt->RptFile = tfile;
170
+ rpt->Rptflag = tflag;
171
+ }
172
+ }
173
+ }
174
+
175
+ // Special error handler for write-to-file error
176
+ if (rpt->Fprinterr) errmsg(pr, 309);
177
+ return errcode;
178
+ }
179
+
180
+ void writelogo(Project *pr)
181
+ /*
182
+ **--------------------------------------------------------------
183
+ ** Input: none
184
+ ** Output: none
185
+ ** Purpose: writes program logo to report file.
186
+ **--------------------------------------------------------------
187
+ */
188
+ {
189
+ Report *rpt = &pr->report;
190
+
191
+ int version;
192
+ int major;
193
+ int minor;
194
+ int patch;
195
+ char s[80];
196
+ time_t timer; // time_t structure & functions time() &
197
+ // ctime() are defined in time.h
198
+
199
+ version = CODEVERSION;
200
+ major = version / 10000;
201
+ minor = (version % 10000) / 100;
202
+ patch = version % 100;
203
+
204
+ time(&timer);
205
+ strcpy(rpt->DateStamp, ctime(&timer));
206
+ rpt->PageNum = 1;
207
+ rpt->LineNum = 2;
208
+ fprintf(rpt->RptFile, FMT18);
209
+ fprintf(rpt->RptFile, "%s", rpt->DateStamp);
210
+ writeline(pr, LOGO1);
211
+ writeline(pr, LOGO2);
212
+ writeline(pr, LOGO3);
213
+ writeline(pr, LOGO4);
214
+ sprintf(s, LOGO5, major, minor, patch);
215
+ writeline(pr, s);
216
+ writeline(pr, LOGO6);
217
+ writeline(pr, "");
218
+ }
219
+
220
+ void writesummary(Project *pr)
221
+ /*
222
+ **--------------------------------------------------------------
223
+ ** Input: none
224
+ ** Output: none
225
+ ** Purpose: writes summary system information to report file
226
+ **--------------------------------------------------------------
227
+ */
228
+ {
229
+ Network *net = &pr->network;
230
+ Hydraul *hyd = &pr->hydraul;
231
+ Quality *qual = &pr->quality;
232
+ Report *rpt = &pr->report;
233
+ Parser *parser = &pr->parser;
234
+ Times *time = &pr->times;
235
+
236
+ char s[MAXLINE + 1];
237
+ int i;
238
+ int nres = 0;
239
+
240
+ for (i = 0; i < 3; i++)
241
+ {
242
+ if (strlen(pr->Title[i]) > 0)
243
+ {
244
+ sprintf(s, "%-.70s", pr->Title[i]);
245
+ writeline(pr, s);
246
+ }
247
+ }
248
+ writeline(pr, " ");
249
+ sprintf(s, FMT19, parser->InpFname);
250
+ writeline(pr, s);
251
+ sprintf(s, FMT20, net->Njuncs);
252
+ writeline(pr, s);
253
+ for (i = 1; i <= net->Ntanks; i++) if (net->Tank[i].A == 0.0) nres++;
254
+ sprintf(s, FMT21a, nres);
255
+ writeline(pr, s);
256
+ sprintf(s, FMT21b, net->Ntanks - nres);
257
+ writeline(pr, s);
258
+ sprintf(s, FMT22, net->Npipes);
259
+ writeline(pr, s);
260
+ sprintf(s, FMT23, net->Npumps);
261
+ writeline(pr, s);
262
+ sprintf(s, FMT24, net->Nvalves);
263
+ writeline(pr, s);
264
+ sprintf(s, FMT25, RptFormTxt[hyd->Formflag]);
265
+ writeline(pr, s);
266
+ sprintf(s, FMT25a, DemandModelTxt[hyd->DemandModel]);
267
+ writeline(pr, s);
268
+ sprintf(s, FMT26, time->Hstep * pr->Ucf[TIME], rpt->Field[TIME].Units);
269
+ writeline(pr, s);
270
+ sprintf(s, FMT27, hyd->Hacc);
271
+ writeline(pr, s);
272
+
273
+ if (hyd->HeadErrorLimit > 0.0)
274
+ {
275
+ sprintf(s, FMT27d, hyd->HeadErrorLimit*pr->Ucf[HEAD], rpt->Field[HEAD].Units);
276
+ writeline(pr, s);
277
+ }
278
+ if (hyd->FlowChangeLimit > 0.0)
279
+ {
280
+ sprintf(s, FMT27e, hyd->FlowChangeLimit*pr->Ucf[FLOW], rpt->Field[FLOW].Units);
281
+ writeline(pr, s);
282
+ }
283
+
284
+ sprintf(s, FMT27a, hyd->CheckFreq);
285
+ writeline(pr, s);
286
+ sprintf(s, FMT27b, hyd->MaxCheck);
287
+ writeline(pr, s);
288
+ sprintf(s, FMT27c, hyd->DampLimit);
289
+ writeline(pr, s);
290
+ sprintf(s, FMT28, hyd->MaxIter);
291
+ writeline(pr, s);
292
+
293
+ if (qual->Qualflag == NONE || time->Dur == 0.0) sprintf(s, FMT29);
294
+ else if (qual->Qualflag == CHEM) sprintf(s, FMT30, qual->ChemName);
295
+ else if (qual->Qualflag == TRACE) sprintf(s, FMT31, net->Node[qual->TraceNode].ID);
296
+ else if (qual->Qualflag == AGE) sprintf(s, FMT32);
297
+ writeline(pr, s);
298
+ if (qual->Qualflag != NONE && time->Dur > 0)
299
+ {
300
+ sprintf(s, FMT33, (float)time->Qstep / 60.0);
301
+ writeline(pr, s);
302
+ sprintf(s, FMT34, qual->Ctol * pr->Ucf[QUALITY], rpt->Field[QUALITY].Units);
303
+ writeline(pr, s);
304
+ }
305
+
306
+ sprintf(s, FMT36, hyd->SpGrav);
307
+ writeline(pr, s);
308
+ sprintf(s, FMT37a, hyd->Viscos / VISCOS);
309
+ writeline(pr, s);
310
+ sprintf(s, FMT37b, qual->Diffus / DIFFUS);
311
+ writeline(pr, s);
312
+ sprintf(s, FMT38, hyd->Dmult);
313
+ writeline(pr, s);
314
+ sprintf(s, FMT39, time->Dur * pr->Ucf[TIME], rpt->Field[TIME].Units);
315
+ writeline(pr, s);
316
+
317
+ if (rpt->Rptflag)
318
+ {
319
+ sprintf(s, FMT40);
320
+ writeline(pr, s);
321
+ if (rpt->Nodeflag == 0) writeline(pr, FMT41);
322
+ if (rpt->Nodeflag == 1) writeline(pr, FMT42);
323
+ if (rpt->Nodeflag == 2) writeline(pr, FMT43);
324
+ writelimits(pr, DEMAND, QUALITY);
325
+ if (rpt->Linkflag == 0) writeline(pr, FMT44);
326
+ if (rpt->Linkflag == 1) writeline(pr, FMT45);
327
+ if (rpt->Linkflag == 2) writeline(pr, FMT46);
328
+ writelimits(pr, DIAM, HEADLOSS);
329
+ }
330
+ writeline(pr, " ");
331
+ }
332
+
333
+ void writehydstat(Project *pr, int iter, double relerr)
334
+ /*
335
+ **--------------------------------------------------------------
336
+ ** Input: iter = # iterations to find hydraulic solution
337
+ ** relerr = convergence error in hydraulic solution
338
+ ** Output: none
339
+ ** Purpose: writes hydraulic status report for solution found
340
+ ** at current time period to report file
341
+ **--------------------------------------------------------------
342
+ */
343
+ {
344
+ Network *net = &pr->network;
345
+ Hydraul *hyd = &pr->hydraul;
346
+ Report *rpt = &pr->report;
347
+ Times *time = &pr->times;
348
+
349
+ int i, n;
350
+ double *NodeDemand;
351
+ char s1[MAXLINE + 1];
352
+ char atime[13];
353
+ StatusType newstat;
354
+ Stank *Tank = net->Tank;
355
+ Slink *Link = net->Link;
356
+
357
+ // Display system status
358
+ strcpy(atime, clocktime(rpt->Atime, time->Htime));
359
+ if (iter > 0)
360
+ {
361
+ if (relerr <= hyd->Hacc) sprintf(s1, FMT58, atime, iter);
362
+ else sprintf(s1, FMT59, atime, iter, relerr);
363
+ writeline(pr, s1);
364
+ if (hyd->DemandModel == PDA && hyd->DeficientNodes > 0)
365
+ {
366
+ if (hyd->DeficientNodes == 1)
367
+ sprintf(s1, FMT69a, hyd->DemandReduction);
368
+ else
369
+ sprintf(s1, FMT69b, hyd->DeficientNodes, hyd->DemandReduction);
370
+ writeline(pr, s1);
371
+ }
372
+ }
373
+
374
+ // Display status changes for tanks:
375
+ // D[n] is net inflow to tank at node n;
376
+ // old tank status is stored in OldStatus[]
377
+ // at indexes Nlinks+1 to Nlinks+Ntanks.
378
+ for (i = 1; i <= net->Ntanks; i++)
379
+ {
380
+ n = net->Tank[i].Node;
381
+ NodeDemand = hyd->NodeDemand;
382
+ if (ABS(NodeDemand[n]) < 0.001) newstat = CLOSED;
383
+ else if (NodeDemand[n] < 0.0) newstat = EMPTYING;
384
+ else if (NodeDemand[n] > 0.0)
385
+ {
386
+ if (Tank[i].A > 0.0 && ABS(hyd->NodeHead[n] - Tank[i].Hmax) < 0.001)
387
+ newstat = OVERFLOWING;
388
+ else newstat = FILLING;
389
+ }
390
+ else newstat = hyd->OldStatus[net->Nlinks + i];
391
+ if (newstat != hyd->OldStatus[net->Nlinks + i])
392
+ {
393
+ if (Tank[i].A > 0.0)
394
+ {
395
+ snprintf(s1, MAXLINE, FMT50, atime, net->Node[n].ID, StatTxt[newstat],
396
+ (hyd->NodeHead[n] - net->Node[n].El) * pr->Ucf[HEAD],
397
+ rpt->Field[HEAD].Units);
398
+ }
399
+ else
400
+ {
401
+ snprintf(s1, MAXLINE, FMT51, atime, net->Node[n].ID, StatTxt[newstat]);
402
+ }
403
+ writeline(pr, s1);
404
+ hyd->OldStatus[net->Nlinks + i] = newstat;
405
+ }
406
+ }
407
+
408
+ // Display status changes for links
409
+ for (i = 1; i <= net->Nlinks; i++)
410
+ {
411
+ if (hyd->LinkStatus[i] != hyd->OldStatus[i])
412
+ {
413
+ if (time->Htime == 0)
414
+ {
415
+ sprintf(s1, FMT52, atime, LinkTxt[(int)net->Link[i].Type],
416
+ net->Link[i].ID, StatTxt[(int)hyd->LinkStatus[i]]);
417
+ }
418
+ else sprintf(s1, FMT53, atime, LinkTxt[Link[i].Type], net->Link[i].ID,
419
+ StatTxt[hyd->OldStatus[i]], StatTxt[hyd->LinkStatus[i]]);
420
+ writeline(pr, s1);
421
+ hyd->OldStatus[i] = hyd->LinkStatus[i];
422
+ }
423
+ }
424
+ writeline(pr, " ");
425
+ }
426
+
427
+ void writeflowbalance(Project *pr)
428
+ /*
429
+ **-------------------------------------------------------------
430
+ ** Input: none
431
+ ** Output: none
432
+ ** Purpose: writes hydraulic flow balance ratio to report file.
433
+ **-------------------------------------------------------------
434
+ */
435
+ {
436
+ Hydraul *hyd = &pr->hydraul;
437
+ Report *rpt = &pr->report;
438
+ char s1[MAXMSG+1];
439
+ double ucf = pr->Ucf[FLOW];
440
+
441
+ snprintf(s1, MAXMSG, "Hydraulic Flow Balance (%s)", rpt->Field[DEMAND].Units);
442
+ writeline(pr, s1);
443
+ snprintf(s1, MAXMSG, "================================");
444
+ writeline(pr, s1);
445
+ snprintf(s1, MAXMSG, "Total Inflow: %12.3f", hyd->FlowBalance.totalInflow*ucf);
446
+ writeline(pr, s1);
447
+ snprintf(s1, MAXMSG, "Consumer Demand: %12.3f", hyd->FlowBalance.consumerDemand*ucf);
448
+ writeline(pr, s1);
449
+ snprintf(s1, MAXMSG, "Demand Deficit: %12.3f", hyd->FlowBalance.deficitDemand*ucf);
450
+ writeline(pr, s1);
451
+ snprintf(s1, MAXMSG, "Emitter Flow: %12.3f", hyd->FlowBalance.emitterDemand*ucf);
452
+ writeline(pr, s1);
453
+ snprintf(s1, MAXMSG, "Leakage Flow: %12.3f", hyd->FlowBalance.leakageDemand*ucf);
454
+ writeline(pr, s1);
455
+ snprintf(s1, MAXMSG, "Total Outflow: %12.3f", hyd->FlowBalance.totalOutflow*ucf);
456
+ writeline(pr, s1);
457
+ snprintf(s1, MAXMSG, "Storage Flow: %12.3f", hyd->FlowBalance.storageDemand*ucf);
458
+ writeline(pr, s1);
459
+ snprintf(s1, MAXMSG, "Flow Ratio: %12.3f", hyd->FlowBalance.ratio);
460
+ writeline(pr, s1);
461
+ snprintf(s1, MAXMSG, "================================\n");
462
+ writeline(pr, s1);
463
+ }
464
+
465
+ void writemassbalance(Project *pr)
466
+ /*
467
+ **-------------------------------------------------------------
468
+ ** Input: none
469
+ ** Output: none
470
+ ** Purpose: writes water quality mass balance ratio
471
+ ** (Outflow + Final Storage) / Inflow + Initial Storage
472
+ ** to report file.
473
+ **-------------------------------------------------------------
474
+ */
475
+ {
476
+ Quality *qual = &pr->quality;
477
+
478
+ char s1[MAXMSG+1];
479
+ char *units[] = {"", " (mg)", " (ug)", " (hrs)"};
480
+ int kunits = 0;
481
+
482
+ if (qual->Qualflag == TRACE) kunits = 1;
483
+ else if (qual->Qualflag == AGE) kunits = 3;
484
+ else
485
+ {
486
+ if (match(qual->ChemUnits, "mg")) kunits = 1;
487
+ else if (match(qual->ChemUnits, "ug")) kunits = 2;
488
+ }
489
+
490
+ snprintf(s1, MAXMSG, "Water Quality Mass Balance%s", units[kunits]);
491
+ writeline(pr, s1);
492
+ snprintf(s1, MAXMSG, "================================");
493
+ writeline(pr, s1);
494
+ snprintf(s1, MAXMSG, "Initial Mass: %12.5e", qual->MassBalance.initial);
495
+ writeline(pr, s1);
496
+ snprintf(s1, MAXMSG, "Mass Inflow: %12.5e", qual->MassBalance.inflow);
497
+ writeline(pr, s1);
498
+ snprintf(s1, MAXMSG, "Mass Outflow: %12.5e", qual->MassBalance.outflow);
499
+ writeline(pr, s1);
500
+ snprintf(s1, MAXMSG, "Mass Reacted: %12.5e", qual->MassBalance.reacted);
501
+ writeline(pr, s1);
502
+ snprintf(s1, MAXMSG, "Final Mass: %12.5e", qual->MassBalance.final);
503
+ writeline(pr, s1);
504
+ snprintf(s1, MAXMSG, "Mass Ratio: %-.5f", qual->MassBalance.ratio);
505
+ writeline(pr, s1);
506
+ snprintf(s1, MAXMSG, "Total Segments: %d", qual->MassBalance.segCount);
507
+ writeline(pr, s1);
508
+ snprintf(s1, MAXMSG, "================================\n");
509
+ writeline(pr, s1);
510
+ }
511
+
512
+ void writeenergy(Project *pr)
513
+ /*
514
+ **-------------------------------------------------------------
515
+ ** Input: none
516
+ ** Output: none
517
+ ** Purpose: writes energy usage report to report file
518
+ **-------------------------------------------------------------
519
+ */
520
+ {
521
+ Network *net = &pr->network;
522
+ Hydraul *hyd = &pr->hydraul;
523
+ Report *rpt = &pr->report;
524
+
525
+ int j;
526
+ double csum;
527
+ char s[MAXLINE + 1];
528
+ Spump *pump;
529
+
530
+ if (net->Npumps == 0) return;
531
+ writeline(pr, " ");
532
+ writeheader(pr,ENERHDR, 0);
533
+
534
+ csum = 0.0;
535
+ for (j = 1; j <= net->Npumps; j++)
536
+ {
537
+ pump = &net->Pump[j];
538
+ csum += pump->Energy.TotalCost;
539
+ if (rpt->LineNum == (long)rpt->PageSize) writeheader(pr, ENERHDR, 1);
540
+
541
+ sprintf(s, "%-8s %6.2f %6.2f %9.2f %9.2f %9.2f %9.2f",
542
+ net->Link[pump->Link].ID, pump->Energy.TimeOnLine,
543
+ pump->Energy.Efficiency, pump->Energy.KwHrsPerFlow,
544
+ pump->Energy.KwHrs, pump->Energy.MaxKwatts,
545
+ pump->Energy.TotalCost);
546
+ writeline(pr, s);
547
+ }
548
+
549
+ fillstr(s, '-', 63);
550
+ writeline(pr, s);
551
+ sprintf(s, FMT74, "", hyd->Emax * hyd->Dcost);
552
+ writeline(pr, s);
553
+ sprintf(s, FMT75, "", csum + hyd->Emax * hyd->Dcost);
554
+ writeline(pr, s);
555
+ writeline(pr, " ");
556
+ }
557
+
558
+ int writeresults(Project *pr)
559
+ /*
560
+ **--------------------------------------------------------------
561
+ ** Input: none
562
+ ** Output: returns error code
563
+ ** Purpose: writes simulation results to report file
564
+ **--------------------------------------------------------------
565
+ */
566
+ {
567
+ Network *net = &pr->network;
568
+ Outfile *out = &pr->outfile;
569
+ Report *rpt = &pr->report;
570
+ Times *time = &pr->times;
571
+
572
+ int j, m, n,
573
+ np, // Reporting period counter
574
+ nnv, // # node variables reported on
575
+ nlv; // # link variables reported on
576
+ int errcode = 0;
577
+ Pfloat *x; // Array of pointers to floats (i.e., a 2-D array)
578
+ FILE *outFile = out->OutFile;
579
+
580
+ //-----------------------------------------------------------
581
+ // NOTE: The OutFile contains results for 4 node variables
582
+ // (demand, head, pressure, & quality) and 8 link
583
+ // variables (flow, velocity, headloss, quality,
584
+ // status, setting, reaction rate & friction factor)
585
+ // at each reporting time.
586
+ //-----------------------------------------------------------
587
+
588
+ // Return if no nodes or links selected for reporting
589
+ // or if no node or link report variables enabled
590
+ if (!rpt->Nodeflag && !rpt->Linkflag) return errcode;
591
+
592
+ nnv = 0;
593
+ for (j = ELEV; j <= QUALITY; j++) nnv += rpt->Field[j].Enabled;
594
+ nlv = 0;
595
+ for (j = LENGTH; j <= FRICTION; j++) nlv += rpt->Field[j].Enabled;
596
+ if (nnv == 0 && nlv == 0) return errcode;
597
+
598
+ // Return if no output file
599
+ if (outFile == NULL) outFile = fopen(pr->outfile.OutFname, "rb");
600
+ if (outFile == NULL) return 106;
601
+
602
+ // Allocate memory for output variables:
603
+ // m = larger of # node variables & # link variables
604
+ // n = larger of # nodes & # links
605
+ m = MAX((QUALITY - DEMAND + 1), (FRICTION - FLOW + 1));
606
+ n = MAX((net->Nnodes + 1), (net->Nlinks + 1));
607
+ x = (Pfloat *)calloc(m, sizeof(Pfloat));
608
+ ERRCODE(MEMCHECK(x));
609
+ if (errcode) return errcode;
610
+ for (j = 0; j < m; j++)
611
+ {
612
+ x[j] = (REAL4 *)calloc(n, sizeof(REAL4));
613
+ if (x[j] == NULL) errcode = 101;
614
+ }
615
+ if (!errcode)
616
+ {
617
+ // Re-position output file & initialize report time
618
+ fseek(outFile, out->OutOffset2, SEEK_SET);
619
+ time->Htime = time->Rstart;
620
+
621
+ // For each reporting time:
622
+ for (np = 1; np <= rpt->Nperiods; np++)
623
+ {
624
+ // Read in node results & write node table
625
+ // (Remember to offset x[j] by 1 because array is zero-based)
626
+ for (j = DEMAND; j <= QUALITY; j++)
627
+ {
628
+ fread((x[j - DEMAND]) + 1, sizeof(REAL4), net->Nnodes, outFile);
629
+ }
630
+ if (nnv > 0 && rpt->Nodeflag > 0) writenodetable(pr, x);
631
+
632
+ // Read in link results & write link table
633
+ for (j = FLOW; j <= FRICTION; j++)
634
+ {
635
+ fread((x[j - FLOW]) + 1, sizeof(REAL4), net->Nlinks, outFile);
636
+ }
637
+ if (nlv > 0 && rpt->Linkflag > 0) writelinktable(pr, x);
638
+ time->Htime += time->Rstep;
639
+ }
640
+ }
641
+
642
+ // Free output file
643
+ if (outFile != NULL)
644
+ {
645
+ fclose(outFile);
646
+ outFile = NULL;
647
+ }
648
+
649
+ // Free allocated memory
650
+ for (j = 0; j < m; j++) free(x[j]);
651
+ free(x);
652
+ return errcode;
653
+ }
654
+
655
+ void writenodetable(Project *pr, Pfloat *x)
656
+ /*
657
+ **---------------------------------------------------------------
658
+ ** Input: x = pointer to node results for current time
659
+ ** Output: none
660
+ ** Purpose: writes node results for current time to report file
661
+ **---------------------------------------------------------------
662
+ */
663
+ {
664
+ Network *net = &pr->network;
665
+ Report *rpt = &pr->report;
666
+
667
+ int i, j;
668
+ char s[MAXLINE + 1], s1[16];
669
+ double y[MAXVAR];
670
+ Snode *node;
671
+
672
+ // Write table header
673
+ writeheader(pr, NODEHDR, 0);
674
+
675
+ // For each node:
676
+ for (i = 1; i <= net->Nnodes; i++)
677
+ {
678
+ // Place node's results for each variable in y
679
+ node = &net->Node[i];
680
+ y[ELEV] = node->El * pr->Ucf[ELEV];
681
+ for (j = DEMAND; j <= QUALITY; j++) y[j] = *((x[j - DEMAND]) + i);
682
+
683
+ // Check if node gets reported on
684
+ if ((rpt->Nodeflag == 1 || node->Rpt) &&
685
+ checklimits(rpt, y, ELEV, QUALITY))
686
+ {
687
+ // Check if new page needed
688
+ if (rpt->LineNum == (long)rpt->PageSize) writeheader(pr, NODEHDR, 1);
689
+
690
+ // Add node ID and each reported field to string s
691
+ sprintf(s, "%-15s", node->ID);
692
+ for (j = ELEV; j <= QUALITY; j++)
693
+ {
694
+ if (rpt->Field[j].Enabled == TRUE)
695
+ {
696
+ if (fabs(y[j]) > 1.e6) sprintf(s1, "%10.2e", y[j]);
697
+ else sprintf(s1, "%10.*f", rpt->Field[j].Precision, y[j]);
698
+ strcat(s, s1);
699
+ }
700
+ }
701
+
702
+ // Note if node is a reservoir/tank
703
+ if (i > net->Njuncs)
704
+ {
705
+ strcat(s, " ");
706
+ strcat(s, NodeTxt[getnodetype(net, i)]);
707
+ }
708
+
709
+ // Write results for node to report file
710
+ writeline(pr, s);
711
+ }
712
+ }
713
+ writeline(pr, " ");
714
+ }
715
+
716
+ void writelinktable(Project *pr, Pfloat *x)
717
+ /*
718
+ **---------------------------------------------------------------
719
+ ** Input: x = pointer to link results for current time
720
+ ** Output: none
721
+ ** Purpose: writes link results for current time to report file
722
+ **---------------------------------------------------------------
723
+ */
724
+ {
725
+ Network *net = &pr->network;
726
+ Report *rpt = &pr->report;
727
+
728
+ int i, j, k;
729
+ char s[MAXLINE + 1], s1[16];
730
+ double y[MAXVAR];
731
+ double *Ucf = pr->Ucf;
732
+ Slink *Link = net->Link;
733
+
734
+ // Write table header
735
+ writeheader(pr, LINKHDR, 0);
736
+
737
+ // For each link:
738
+ for (i = 1; i <= net->Nlinks; i++)
739
+ {
740
+ // Place results for each link variable in y
741
+ y[LENGTH] = Link[i].Len * Ucf[LENGTH];
742
+ y[DIAM] = Link[i].Diam * Ucf[DIAM];
743
+ for (j = FLOW; j <= FRICTION; j++) y[j] = *((x[j - FLOW]) + i);
744
+
745
+ // Check if link gets reported on
746
+ if ((rpt->Linkflag == 1 || Link[i].Rpt) && checklimits(rpt, y, DIAM, FRICTION))
747
+ {
748
+ // Check if new page needed
749
+ if (rpt->LineNum == (long)rpt->PageSize) writeheader(pr, LINKHDR, 1);
750
+
751
+ // Add link ID and each reported field to string s
752
+ sprintf(s, "%-15s", Link[i].ID);
753
+ for (j = LENGTH; j <= FRICTION; j++)
754
+ {
755
+ if (rpt->Field[j].Enabled == TRUE)
756
+ {
757
+ if (j == STATUS)
758
+ {
759
+ if (y[j] <= CLOSED) k = CLOSED;
760
+ else if (y[j] == ACTIVE) k = ACTIVE;
761
+ else k = OPEN;
762
+ sprintf(s1, "%10s", StatTxt[k]);
763
+ }
764
+ else
765
+ {
766
+ if (fabs(y[j]) > 1.e6) sprintf(s1, "%10.2e", y[j]);
767
+ else sprintf(s1, "%10.*f", rpt->Field[j].Precision, y[j]);
768
+ }
769
+ strcat(s, s1);
770
+ }
771
+ }
772
+
773
+ // Note if link is a pump or valve
774
+ if ((j = Link[i].Type) > PIPE)
775
+ {
776
+ strcat(s, " ");
777
+ strcat(s, LinkTxt[j]);
778
+ }
779
+
780
+ // Write results for link
781
+ writeline(pr, s);
782
+ }
783
+ }
784
+ writeline(pr, " ");
785
+ }
786
+
787
+ void writeheader(Project *pr, int type, int contin)
788
+ /*
789
+ **--------------------------------------------------------------
790
+ ** Input: type = table type
791
+ ** contin = table continuation flag
792
+ ** Output: none
793
+ ** Purpose: writes column headings for output report tables
794
+ **--------------------------------------------------------------
795
+ */
796
+ {
797
+ Report *rpt = &pr->report;
798
+ Quality *qual = &pr->quality;
799
+ Parser *parser = &pr->parser;
800
+ Times *time = &pr->times;
801
+
802
+ char s[MAXLINE + 1], s1[MAXLINE + 1], s2[MAXLINE + 1], s3[MAXLINE + 1];
803
+ int i, n;
804
+
805
+ // Move to next page if < 11 lines remain on current page
806
+ if (rpt->Rptflag && rpt->LineNum + 11 > (long)rpt->PageSize)
807
+ {
808
+ while (rpt->LineNum < (long)rpt->PageSize) writeline(pr, " ");
809
+ }
810
+ writeline(pr, " ");
811
+
812
+ // Hydraulic Status Table
813
+ if (type == STATHDR)
814
+ {
815
+ sprintf(s, FMT49);
816
+ if (contin) strcat(s, t_CONTINUED);
817
+ writeline(pr, s);
818
+ fillstr(s, '-', 70);
819
+ writeline(pr, s);
820
+ }
821
+
822
+ // Energy Usage Table
823
+ if (type == ENERHDR)
824
+ {
825
+ if (parser->Unitsflag == SI) strcpy(s1, t_perM3);
826
+ else strcpy(s1, t_perMGAL);
827
+ sprintf(s, FMT71);
828
+ if (contin) strcat(s, t_CONTINUED);
829
+ writeline(pr, s);
830
+ fillstr(s, '-', 63);
831
+ writeline(pr, s);
832
+ sprintf(s, FMT72);
833
+ writeline(pr, s);
834
+ sprintf(s, FMT73, s1);
835
+ writeline(pr, s);
836
+ fillstr(s, '-', 63);
837
+ writeline(pr, s);
838
+ }
839
+
840
+ // Node Results Table
841
+ if (type == NODEHDR)
842
+ {
843
+ if (rpt->Tstatflag == RANGE) sprintf(s, FMT76, t_DIFFER);
844
+ else if (rpt->Tstatflag != SERIES)
845
+ {
846
+ sprintf(s, FMT76, TstatTxt[rpt->Tstatflag]);
847
+ }
848
+ else if (time->Dur == 0) sprintf(s, FMT77);
849
+ else sprintf(s, FMT78, clocktime(rpt->Atime, time->Htime));
850
+ if (contin) strcat(s, t_CONTINUED);
851
+ writeline(pr, s);
852
+
853
+ n = 15;
854
+ sprintf(s2, "%15s", "");
855
+ strcpy(s, t_NODEID);
856
+ sprintf(s3, "%-15s", s);
857
+
858
+ for (i = ELEV; i < QUALITY; i++)
859
+ {
860
+ if (rpt->Field[i].Enabled == TRUE)
861
+ {
862
+ n += 10;
863
+ sprintf(s, "%10s", rpt->Field[i].Name);
864
+ strcat(s2, s);
865
+ sprintf(s, "%10s", rpt->Field[i].Units);
866
+ strcat(s3, s);
867
+ }
868
+ }
869
+
870
+ if (rpt->Field[QUALITY].Enabled == TRUE)
871
+ {
872
+ n += 10;
873
+ sprintf(s, "%10s", qual->ChemName);
874
+ strcat(s2, s);
875
+ sprintf(s, "%10s", qual->ChemUnits);
876
+ strcat(s3, s);
877
+ }
878
+ fillstr(s1, '-', n);
879
+ writeline(pr, s1);
880
+ writeline(pr, s2);
881
+ writeline(pr, s3);
882
+ writeline(pr, s1);
883
+ }
884
+
885
+ // Link Results Table
886
+ if (type == LINKHDR)
887
+ {
888
+ if (rpt->Tstatflag == RANGE) sprintf(s, FMT79, t_DIFFER);
889
+ else if (rpt->Tstatflag != SERIES)
890
+ {
891
+ sprintf(s, FMT79, TstatTxt[rpt->Tstatflag]);
892
+ }
893
+ else if (time->Dur == 0) sprintf(s, FMT80);
894
+ else sprintf(s, FMT81, clocktime(rpt->Atime, time->Htime));
895
+ if (contin) strcat(s, t_CONTINUED);
896
+ writeline(pr, s);
897
+
898
+ n = 15;
899
+ sprintf(s2, "%15s", "");
900
+ strcpy(s, t_LINKID);
901
+ sprintf(s3, "%-15s", s);
902
+ for (i = LENGTH; i <= FRICTION; i++)
903
+ {
904
+ if (rpt->Field[i].Enabled == TRUE)
905
+ {
906
+ n += 10;
907
+ sprintf(s, "%10s", rpt->Field[i].Name);
908
+ strcat(s2, s);
909
+ sprintf(s, "%10s", rpt->Field[i].Units);
910
+ strcat(s3, s);
911
+ }
912
+ }
913
+ fillstr(s1, '-', n);
914
+ writeline(pr, s1);
915
+ writeline(pr, s2);
916
+ writeline(pr, s3);
917
+ writeline(pr, s1);
918
+ }
919
+ }
920
+
921
+ void writeline(Project *pr, const char *s)
922
+ /*
923
+ **--------------------------------------------------------------
924
+ ** Input: *s = text string
925
+ ** Output: none
926
+ ** Purpose: writes a line of output to report file
927
+ **--------------------------------------------------------------
928
+ */
929
+ {
930
+ Report *rpt = &pr->report;
931
+
932
+ if (pr->report.reportCallback != NULL)
933
+ {
934
+ pr->report.reportCallback(pr->report.reportCallbackUserData, pr, s);
935
+ return;
936
+ }
937
+
938
+ if (rpt->RptFile == NULL) return;
939
+ if (rpt->Rptflag)
940
+ {
941
+ if (rpt->LineNum == (long)rpt->PageSize)
942
+ {
943
+ rpt->PageNum++;
944
+ if (fprintf(rpt->RptFile, FMT82, (int)rpt->PageNum, pr->Title[0]) < 0)
945
+ {
946
+ rpt->Fprinterr = TRUE;
947
+ }
948
+ rpt->LineNum = 3;
949
+ }
950
+ }
951
+ if (fprintf(rpt->RptFile, "\n %s", s) < 0) rpt->Fprinterr = TRUE;
952
+ rpt->LineNum++;
953
+ }
954
+
955
+ void writerelerr(Project *pr, int iter, double relerr)
956
+ /*
957
+ **-----------------------------------------------------------------
958
+ ** Input: iter = current iteration of hydraulic solution
959
+ ** relerr = current convergence error
960
+ ** Output: none
961
+ ** Purpose: writes out convergence status of hydraulic solution
962
+ **-----------------------------------------------------------------
963
+ */
964
+ {
965
+ Report *rpt = &pr->report;
966
+ Times *time = &pr->times;
967
+
968
+ if (iter == 0)
969
+ {
970
+ sprintf(pr->Msg, FMT64, clocktime(rpt->Atime, time->Htime));
971
+ writeline(pr, pr->Msg);
972
+ }
973
+ else
974
+ {
975
+ sprintf(pr->Msg, FMT65, iter, relerr);
976
+ writeline(pr, pr->Msg);
977
+ }
978
+ }
979
+
980
+ void writestatchange(Project *pr, int k, char s1, char s2)
981
+ /*
982
+ **--------------------------------------------------------------
983
+ ** Input: k = link index
984
+ ** s1 = old link status
985
+ ** s2 = new link status
986
+ ** Output: none
987
+ ** Purpose: writes change in link status to output report
988
+ **--------------------------------------------------------------
989
+ */
990
+ {
991
+ Network *net = &pr->network;
992
+ Hydraul *hyd = &pr->hydraul;
993
+
994
+ int j1, j2;
995
+ double setting;
996
+ double *Ucf = pr->Ucf;
997
+ double *LinkSetting = hyd->LinkSetting;
998
+ Slink *Link = net->Link;
999
+
1000
+ // We have a pump/valve setting change instead of a status change
1001
+ if (s1 == s2)
1002
+ {
1003
+ setting = LinkSetting[k];
1004
+ switch (Link[k].Type)
1005
+ {
1006
+ case PRV:
1007
+ case PSV:
1008
+ case PBV:
1009
+ setting *= Ucf[PRESSURE];
1010
+ break;
1011
+ case FCV:
1012
+ setting *= Ucf[FLOW];
1013
+ break;
1014
+ default:
1015
+ break;
1016
+ }
1017
+ sprintf(pr->Msg, FMT56, LinkTxt[Link[k].Type], Link[k].ID, setting);
1018
+ writeline(pr, pr->Msg);
1019
+ return;
1020
+ }
1021
+
1022
+ // We have a status change - write the old & new status types
1023
+ if (s1 == ACTIVE) j1 = ACTIVE;
1024
+ else if (s1 <= CLOSED) j1 = CLOSED;
1025
+ else j1 = OPEN;
1026
+ if (s2 == ACTIVE) j2 = ACTIVE;
1027
+ else if (s2 <= CLOSED) j2 = CLOSED;
1028
+ else j2 = OPEN;
1029
+ if (j1 != j2)
1030
+ {
1031
+ sprintf(pr->Msg, FMT57, LinkTxt[Link[k].Type], Link[k].ID, StatTxt[j1],
1032
+ StatTxt[j2]);
1033
+ writeline(pr, pr->Msg);
1034
+ }
1035
+ }
1036
+
1037
+ void writecontrolaction(Project *pr, int k, int i)
1038
+ /*
1039
+ ----------------------------------------------------------------
1040
+ ** Input: k = link index
1041
+ ** i = control index
1042
+ ** Output: none
1043
+ ** Purpose: writes control action taken to status report
1044
+ **--------------------------------------------------------------
1045
+ */
1046
+ {
1047
+ Network *net = &pr->network;
1048
+ Report *rpt = &pr->report;
1049
+ Times *time = &pr->times;
1050
+
1051
+ int n;
1052
+ Snode *Node = net->Node;
1053
+ Slink *Link = net->Link;
1054
+ Scontrol *Control = net->Control;
1055
+
1056
+ switch (Control[i].Type)
1057
+ {
1058
+ case LOWLEVEL:
1059
+ case HILEVEL:
1060
+ n = Control[i].Node;
1061
+ sprintf(pr->Msg, FMT54, clocktime(rpt->Atime, time->Htime),
1062
+ LinkTxt[Link[k].Type], Link[k].ID,
1063
+ NodeTxt[getnodetype(net, n)], Node[n].ID);
1064
+ break;
1065
+
1066
+ case TIMER:
1067
+ case TIMEOFDAY:
1068
+ sprintf(pr->Msg, FMT55, clocktime(rpt->Atime, time->Htime),
1069
+ LinkTxt[Link[k].Type], Link[k].ID);
1070
+ break;
1071
+ default:
1072
+ return;
1073
+ }
1074
+ writeline(pr, pr->Msg);
1075
+ }
1076
+
1077
+ void writeruleaction(Project *pr, int k, char *ruleID)
1078
+ /*
1079
+ **--------------------------------------------------------------
1080
+ ** Input: k = link index
1081
+ ** *ruleID = rule ID
1082
+ ** Output: none
1083
+ ** Purpose: writes rule action taken to status report
1084
+ **--------------------------------------------------------------
1085
+ */
1086
+ {
1087
+ Network *net = &pr->network;
1088
+ Report *rpt = &pr->report;
1089
+ Times *time = &pr->times;
1090
+
1091
+ Slink *Link = net->Link;
1092
+
1093
+ sprintf(pr->Msg, FMT63, clocktime(rpt->Atime, time->Htime),
1094
+ LinkTxt[Link[k].Type], Link[k].ID, ruleID);
1095
+ writeline(pr, pr->Msg);
1096
+ }
1097
+
1098
+ int writehydwarn(Project *pr, int iter, double relerr)
1099
+ /*
1100
+ **--------------------------------------------------------------
1101
+ ** Input: iter = # iterations to find hydraulic solution
1102
+ ** Output: warning flag code
1103
+ ** Purpose: writes hydraulic warning message to report file
1104
+ **
1105
+ ** Note: Warning conditions checked in following order:
1106
+ ** 1. System balanced but unstable
1107
+ ** 2. Negative pressures
1108
+ ** 3. FCV cannot supply flow or PRV/PSV cannot maintain pressure
1109
+ ** 4. Pump out of range
1110
+ ** 5. Network disconnected
1111
+ ** 6. System unbalanced
1112
+ **--------------------------------------------------------------
1113
+ */
1114
+ {
1115
+ Network *net = &pr->network;
1116
+ Hydraul *hyd = &pr->hydraul;
1117
+ Report *rpt = &pr->report;
1118
+ Times *time = &pr->times;
1119
+
1120
+ int i, j;
1121
+ char flag = 0;
1122
+ int s;
1123
+ Snode *node;
1124
+ Slink *link;
1125
+ Spump *pump;
1126
+
1127
+ // Check if system unstable
1128
+ if (iter > hyd->MaxIter && relerr <= hyd->Hacc)
1129
+ {
1130
+ sprintf(pr->Msg, WARN02, clocktime(rpt->Atime, time->Htime));
1131
+ if (rpt->Messageflag) writeline(pr, pr->Msg);
1132
+ flag = 2;
1133
+ }
1134
+
1135
+ // Check for pressure deficient nodes
1136
+ if (hyd->DemandModel == DDA)
1137
+ {
1138
+ hyd->DeficientNodes = 0;
1139
+ for (i = 1; i <= net->Njuncs; i++)
1140
+ {
1141
+ node = &net->Node[i];
1142
+ if (hyd->NodeHead[i] < node->El && hyd->NodeDemand[i] > 0.0)
1143
+ hyd->DeficientNodes++;
1144
+ }
1145
+ if (hyd->DeficientNodes > 0)
1146
+ {
1147
+ if (rpt->Messageflag)
1148
+ {
1149
+ sprintf(pr->Msg, WARN06, clocktime(rpt->Atime, time->Htime));
1150
+ writeline(pr, pr->Msg);
1151
+ }
1152
+ flag = 6;
1153
+ }
1154
+ }
1155
+
1156
+ // Check for abnormal valve condition
1157
+ for (i = 1; i <= net->Nvalves; i++)
1158
+ {
1159
+ j = net->Valve[i].Link;
1160
+ link = &net->Link[j];
1161
+ if (hyd->LinkStatus[j] >= XFCV)
1162
+ {
1163
+ if (rpt->Messageflag)
1164
+ {
1165
+ sprintf(pr->Msg, WARN05, LinkTxt[link->Type], link->ID,
1166
+ StatTxt[hyd->LinkStatus[j]],
1167
+ clocktime(rpt->Atime, time->Htime));
1168
+ writeline(pr, pr->Msg);
1169
+ }
1170
+ flag = 5;
1171
+ }
1172
+ }
1173
+
1174
+ // Check for abnormal pump condition
1175
+ for (i = 1; i <= net->Npumps; i++)
1176
+ {
1177
+ pump = &net->Pump[i];
1178
+ j = pump->Link;
1179
+ s = hyd->LinkStatus[j];
1180
+ if (hyd->LinkStatus[j] >= OPEN)
1181
+ {
1182
+ if (hyd->LinkFlow[j] > hyd->LinkSetting[j] * pump->Qmax) s = XFLOW;
1183
+ if (hyd->LinkFlow[j] < 0.0) s = XHEAD;
1184
+ }
1185
+ if (s == XHEAD || s == XFLOW)
1186
+ {
1187
+ if (rpt->Messageflag)
1188
+ {
1189
+ sprintf(pr->Msg, WARN04, net->Link[j].ID, StatTxt[s],
1190
+ clocktime(rpt->Atime, time->Htime));
1191
+ writeline(pr, pr->Msg);
1192
+ }
1193
+ flag = 4;
1194
+ }
1195
+ }
1196
+
1197
+ // Check if system is unbalanced
1198
+ if (iter > hyd->MaxIter && relerr > hyd->Hacc)
1199
+ {
1200
+ if (rpt->Messageflag)
1201
+ {
1202
+ sprintf(pr->Msg, WARN01, clocktime(rpt->Atime, time->Htime));
1203
+ if (hyd->ExtraIter == -1) strcat(pr->Msg, t_HALTED);
1204
+ writeline(pr, pr->Msg);
1205
+ }
1206
+ flag = 1;
1207
+ }
1208
+
1209
+ // Check for disconnected network & update project's warning flag
1210
+ if (flag > 0)
1211
+ {
1212
+ disconnected(pr);
1213
+ pr->Warnflag = flag;
1214
+ if (rpt->Messageflag) writeline(pr, " ");
1215
+ }
1216
+ return flag;
1217
+ }
1218
+
1219
+ void writehyderr(Project *pr, int errnode)
1220
+ /*
1221
+ **-----------------------------------------------------------
1222
+ ** Input: none
1223
+ ** Output: none
1224
+ ** Purpose: outputs status & checks connectivity when
1225
+ ** network hydraulic equations cannot be solved.
1226
+ **-----------------------------------------------------------
1227
+ */
1228
+ {
1229
+ Network *net = &pr->network;
1230
+ Report *rpt = &pr->report;
1231
+ Times *time = &pr->times;
1232
+
1233
+ Snode *Node = net->Node;
1234
+
1235
+ if (rpt->Messageflag)
1236
+ {
1237
+ sprintf(pr->Msg, FMT62, clocktime(rpt->Atime, time->Htime),
1238
+ Node[errnode].ID);
1239
+ writeline(pr, pr->Msg);
1240
+ }
1241
+ writehydstat(pr, 0, 0);
1242
+ disconnected(pr);
1243
+ }
1244
+
1245
+ int disconnected(Project *pr)
1246
+ /*
1247
+ **-------------------------------------------------------------------
1248
+ ** Input: None
1249
+ ** Output: Returns number of disconnected nodes
1250
+ ** Purpose: Tests current hydraulic solution to see if any closed
1251
+ ** links have caused the network to become disconnected.
1252
+ **-------------------------------------------------------------------
1253
+ */
1254
+ {
1255
+ Network *net = &pr->network;
1256
+ Hydraul *hyd = &pr->hydraul;
1257
+ Report *rpt = &pr->report;
1258
+ Times *time = &pr->times;
1259
+
1260
+ int i, j;
1261
+ int count, mcount;
1262
+ int errcode = 0;
1263
+ int *nodelist;
1264
+ char *marked;
1265
+ Snode *node;
1266
+
1267
+ // Allocate memory for node list & marked list
1268
+ nodelist = (int *)calloc(net->Nnodes + 1, sizeof(int));
1269
+ marked = (char *)calloc(net->Nnodes + 1, sizeof(char));
1270
+ ERRCODE(MEMCHECK(nodelist));
1271
+ ERRCODE(MEMCHECK(marked));
1272
+
1273
+ // If allocation fails return with 0 nodes disconnected
1274
+ if (errcode)
1275
+ {
1276
+ free(nodelist);
1277
+ free(marked);
1278
+ return (0);
1279
+ }
1280
+
1281
+ // Place tanks on node list and marked list
1282
+ for (i = 1; i <= net->Ntanks; i++)
1283
+ {
1284
+ j = net->Njuncs + i;
1285
+ nodelist[i] = j;
1286
+ marked[j] = 1;
1287
+ }
1288
+
1289
+ // Place junctions with negative demands on the lists
1290
+ mcount = net->Ntanks;
1291
+ for (i = 1; i <= net->Njuncs; i++)
1292
+ {
1293
+ if (hyd->NodeDemand[i] < 0.0)
1294
+ {
1295
+ mcount++;
1296
+ nodelist[mcount] = i;
1297
+ marked[i] = 1;
1298
+ }
1299
+ }
1300
+
1301
+ // Mark all nodes that can be connected to tanks
1302
+ // and count number of nodes remaining unmarked
1303
+ marknodes(pr, mcount, nodelist, marked);
1304
+ j = 0;
1305
+ count = 0;
1306
+ for (i = 1; i <= net->Njuncs; i++)
1307
+ {
1308
+ node = &net->Node[i];
1309
+ if (!marked[i] && hyd->NodeDemand[i] != 0.0)
1310
+ {
1311
+ count++;
1312
+ if (count <= MAXCOUNT && rpt->Messageflag)
1313
+ {
1314
+ sprintf(pr->Msg, WARN03a, node->ID,
1315
+ clocktime(rpt->Atime, time->Htime));
1316
+ writeline(pr, pr->Msg);
1317
+ }
1318
+ j = i; // Last unmarked node
1319
+ }
1320
+ }
1321
+
1322
+ // Report number of unmarked nodes and find closed link
1323
+ // on path from node j back to a tank
1324
+ if (count > 0 && rpt->Messageflag)
1325
+ {
1326
+ if (count > MAXCOUNT)
1327
+ {
1328
+ sprintf(pr->Msg, WARN03b, count - MAXCOUNT,
1329
+ clocktime(rpt->Atime, time->Htime));
1330
+ writeline(pr, pr->Msg);
1331
+ }
1332
+ getclosedlink(pr, j, marked, nodelist);
1333
+ }
1334
+
1335
+ // Free allocated memory
1336
+ free(nodelist);
1337
+ free(marked);
1338
+ return count;
1339
+ }
1340
+
1341
+ void marknodes(Project *pr, int m, int *nodelist, char *marked)
1342
+ /*
1343
+ **----------------------------------------------------------------
1344
+ ** Input: m = number of source nodes
1345
+ ** nodelist[] = list of nodes to be traced from
1346
+ ** marked[] = TRUE if node connected to source
1347
+ ** Output: None.
1348
+ ** Purpose: Marks all junction nodes connected to tanks.
1349
+ **----------------------------------------------------------------
1350
+ */
1351
+ {
1352
+ Network *net = &pr->network;
1353
+ Hydraul *hyd = &pr->hydraul;
1354
+
1355
+ int i, j, k, n;
1356
+ Padjlist alink;
1357
+
1358
+ // Scan each successive entry of node list
1359
+ n = 1;
1360
+ while (n <= m)
1361
+ {
1362
+ // Scan all nodes connected to current node
1363
+ i = nodelist[n];
1364
+ for (alink = net->Adjlist[i]; alink != NULL; alink = alink->next)
1365
+ {
1366
+ // Get indexes of connecting link and node
1367
+ k = alink->link;
1368
+ j = alink->node;
1369
+ if (marked[j]) continue;
1370
+
1371
+ // Check if valve connection is in correct direction
1372
+ switch (net->Link[k].Type)
1373
+ {
1374
+ case CVPIPE:
1375
+ case PRV:
1376
+ case PSV:
1377
+ if (j == net->Link[k].N1) continue;
1378
+ break;
1379
+ default:
1380
+ break;
1381
+ }
1382
+
1383
+ // Mark connection node if link not closed
1384
+ if (hyd->LinkStatus[k] > CLOSED)
1385
+ {
1386
+ marked[j] = 1;
1387
+ m++;
1388
+ nodelist[m] = j;
1389
+ }
1390
+ }
1391
+ n++;
1392
+ }
1393
+ }
1394
+
1395
+ void getclosedlink(Project *pr, int i, char *marked, int *stack)
1396
+ /*
1397
+ **----------------------------------------------------------------
1398
+ ** Input: i = junction index
1399
+ ** marked[] = marks nodes already examined
1400
+ ** stack[] = stack to hold nodes to examine
1401
+ ** Output: None.
1402
+ ** Purpose: Determines if a closed link connects to junction i.
1403
+ **----------------------------------------------------------------
1404
+ */
1405
+ {
1406
+ Network *net = &pr->network;
1407
+
1408
+ int j, k;
1409
+ Padjlist alink;
1410
+
1411
+ int top = 0;
1412
+
1413
+ // Mark the current junction as examined and push onto stack
1414
+ marked[i] = 2;
1415
+ stack[top] = i;
1416
+
1417
+ while (top >= 0) {
1418
+ i = stack[top--];
1419
+ alink = net->Adjlist[i];
1420
+
1421
+ // Iterate through each link adjacent to the current node
1422
+ while (alink != NULL) {
1423
+ k = alink->link;
1424
+ j = alink->node;
1425
+
1426
+ // Skip nodes that have already been examined
1427
+ if (marked[j] == 2) {
1428
+ alink = alink->next;
1429
+ continue;
1430
+ }
1431
+
1432
+ // If a closed link is found, return and display a warning message
1433
+ if (marked[j] == 1) {
1434
+ sprintf(pr->Msg, WARN03c, net->Link[k].ID);
1435
+ writeline(pr, pr->Msg);
1436
+ return;
1437
+ }
1438
+
1439
+ // Mark the node as examined and push it onto the stack
1440
+ marked[j] = 2;
1441
+ stack[++top] = j;
1442
+ alink = alink->next;
1443
+ }
1444
+ }
1445
+
1446
+ }
1447
+
1448
+ void writelimits(Project *pr, int j1, int j2)
1449
+ /*
1450
+ **--------------------------------------------------------------
1451
+ ** Input: j1 = index of first output variable
1452
+ ** j2 = index of last output variable
1453
+ ** Output: none
1454
+ ** Purpose: writes reporting criteria to output report
1455
+ **--------------------------------------------------------------
1456
+ */
1457
+ {
1458
+ Report *rpt = &pr->report;
1459
+ int j;
1460
+
1461
+ for (j = j1; j <= j2; j++)
1462
+ {
1463
+ if (rpt->Field[j].RptLim[LOW] < BIG)
1464
+ {
1465
+ sprintf(pr->Msg, FMT47, rpt->Field[j].Name,
1466
+ rpt->Field[j].RptLim[LOW],
1467
+ rpt->Field[j].Units);
1468
+ writeline(pr, pr->Msg);
1469
+ }
1470
+ if (rpt->Field[j].RptLim[HI] > -BIG)
1471
+ {
1472
+ sprintf(pr->Msg, FMT48, rpt->Field[j].Name,
1473
+ rpt->Field[j].RptLim[HI],
1474
+ rpt->Field[j].Units);
1475
+ writeline(pr, pr->Msg);
1476
+ }
1477
+ }
1478
+ }
1479
+
1480
+ int checklimits(Report *rpt, double *y, int j1, int j2)
1481
+ /*
1482
+ **--------------------------------------------------------------
1483
+ ** Input: *y = array of output results
1484
+ ** j1 = index of first output variable
1485
+ ** j2 = index of last output variable
1486
+ ** Output: returns 1 if criteria met, 0 otherwise
1487
+ ** Purpose: checks if output reporting criteria is met
1488
+ **--------------------------------------------------------------
1489
+ */
1490
+ {
1491
+ int j;
1492
+ for (j = j1; j <= j2; j++)
1493
+ {
1494
+ if (y[j] > rpt->Field[j].RptLim[LOW] ||
1495
+ y[j] < rpt->Field[j].RptLim[HI]
1496
+ ) return 0;
1497
+ }
1498
+ return 1;
1499
+ }
1500
+
1501
+ void writetime(Project *pr, char *fmt)
1502
+ /*
1503
+ **----------------------------------------------------------------
1504
+ ** Input: fmt = format string
1505
+ ** Output: none
1506
+ ** Purpose: writes starting/ending time of a run to report file
1507
+ **----------------------------------------------------------------
1508
+ */
1509
+ {
1510
+ time_t timer;
1511
+ time(&timer);
1512
+ sprintf(pr->Msg, fmt, ctime(&timer));
1513
+ writeline(pr, pr->Msg);
1514
+ }
1515
+
1516
+ char *clocktime(char *atime, long seconds)
1517
+ /*
1518
+ **--------------------------------------------------------------
1519
+ ** Input: seconds = time in seconds
1520
+ ** Output: atime = time in hrs:min
1521
+ ** (returns pointer to atime)
1522
+ ** Purpose: converts time in seconds to hours:minutes format
1523
+ **--------------------------------------------------------------
1524
+ */
1525
+ {
1526
+ long h, m, s;
1527
+ h = seconds / 3600;
1528
+ m = seconds % 3600 / 60;
1529
+ s = seconds - 3600 * h - 60 * m;
1530
+ sprintf(atime, "%01d:%02d:%02d", (int)h, (int)m, (int)s);
1531
+ return atime;
1532
+ }
1533
+
1534
+ char *fillstr(char *s, char ch, int n)
1535
+ /*
1536
+ **---------------------------------------------------------
1537
+ ** Fills n bytes of s to character ch.
1538
+ ** NOTE: does not check for overwriting s.
1539
+ **---------------------------------------------------------
1540
+ */
1541
+ {
1542
+ int i;
1543
+ for (i = 0; i <= n; i++) s[i] = ch;
1544
+ s[n + 1] = '\0';
1545
+ return (s);
1546
+ }
1547
+
1548
+ int getnodetype(Network *net, int i)
1549
+ /*
1550
+ **---------------------------------------------------------
1551
+ ** Determines type of node with index i
1552
+ ** (junction = 0, reservoir = 1, tank = 2).
1553
+ **---------------------------------------------------------
1554
+ */
1555
+ {
1556
+ if (i <= net->Njuncs) return 0;
1557
+ if (net->Tank[i - net->Njuncs].A == 0.0) return 1;
1558
+ return 2;
1559
+ }