epanet-plus 0.0.1__cp313-cp313-musllinux_1_2_x86_64.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.cpython-313-x86_64-linux-musl.so +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/input3.c ADDED
@@ -0,0 +1,2265 @@
1
+ /*
2
+ ******************************************************************************
3
+ Project: OWA EPANET
4
+ Version: 2.3
5
+ Module: input3.c
6
+ Description: parses network data from a line of an EPANET input file
7
+ Authors: see AUTHORS
8
+ Copyright: see AUTHORS
9
+ License: see LICENSE
10
+ Last Updated: 04/19/2025
11
+ ******************************************************************************
12
+ */
13
+
14
+ #include <stdlib.h>
15
+ #include <stdio.h>
16
+ #include <string.h>
17
+ #include <math.h>
18
+
19
+ #include "types.h"
20
+ #include "funcs.h"
21
+ #include "hash.h"
22
+ #include "text.h"
23
+
24
+ // Defined in ENUMSTXT.H
25
+ extern char *MixTxt[];
26
+ extern char *Fldname[];
27
+ extern char *DemandModelTxt[];
28
+ extern char *BackflowTxt[];
29
+ extern char *CurveTypeTxt[];
30
+
31
+ // Imported Functions
32
+ extern int addnodeID(Network *, int, char *);
33
+ extern int addlinkID(Network *, int, char *);
34
+ extern int getunitsoption(Project *, char *);
35
+ extern int getheadlossoption(Project *, char *);
36
+
37
+ // Local functions
38
+ static double gettokvalue(Project *, double, int, int *, int *);
39
+ static int getlinknodes(Project *, int *, int *);
40
+ static int optionchoice(Project *, int);
41
+ static int optionvalue(Project *, int);
42
+ static int getpumpcurve(Project *, int);
43
+ static void changestatus(Network *, int, StatusType, double);
44
+ static int setError(Parser *, int, int);
45
+
46
+
47
+ int setError(Parser *parser, int tokindex, int errcode)
48
+ /*
49
+ **--------------------------------------------------------------
50
+ ** Input: tokindex = index of input line token
51
+ ** errcode = an error code
52
+ ** Output: returns error code
53
+ ** Purpose: records index of token from line of input associated
54
+ ** with an error
55
+ **--------------------------------------------------------------
56
+ */
57
+ {
58
+ parser->ErrTok = tokindex;
59
+ return errcode;
60
+ }
61
+
62
+ int juncdata(Project *pr)
63
+ /*
64
+ **--------------------------------------------------------------
65
+ ** Input: none
66
+ ** Output: returns error code
67
+ ** Purpose: processes junction data
68
+ ** Format:
69
+ ** [JUNCTIONS]
70
+ ** id elev. (demand) (demand pattern)
71
+ **--------------------------------------------------------------
72
+ */
73
+ {
74
+ Network *net = &pr->network;
75
+ Parser *parser = &pr->parser;
76
+ Hydraul *hyd = &pr->hydraul;
77
+
78
+ int p = 0; // time pattern index
79
+ int n; // number of tokens
80
+ int njuncs; // number of network junction nodes
81
+ double el = 0.0, // elevation
82
+ d = 0.0, // base demand
83
+ x;
84
+ Snode *node;
85
+ int errcode = 0;
86
+ int errtok = -1;
87
+
88
+ // Add new junction to data base
89
+ if (net->Nnodes == parser->MaxNodes) return 200;
90
+ errcode = addnodeID(net, net->Njuncs + 1, parser->Tok[0]);
91
+ if (errcode > 0) return setError(parser, 0, errcode);
92
+ net->Njuncs++;
93
+ net->Nnodes++;
94
+
95
+ // Check for valid data
96
+ n = parser->Ntokens;
97
+ if (n > 1)
98
+ {
99
+ if (!getfloat(parser->Tok[1], &x))
100
+ {
101
+ errcode = 202;
102
+ errtok = 1;
103
+ }
104
+ else el = x;
105
+ }
106
+ if (!errcode && n > 2)
107
+ {
108
+ if (!getfloat(parser->Tok[2], &x))
109
+ {
110
+ errcode = 202;
111
+ errtok = 2;
112
+ }
113
+ else d = x;
114
+ }
115
+ if (!errcode && n > 3)
116
+ {
117
+ p = findpattern(net, parser->Tok[3]);
118
+ if (p < 0)
119
+ {
120
+ errcode = 205;
121
+ errtok = 3;
122
+ }
123
+ }
124
+
125
+ // Save junction data
126
+ njuncs = net->Njuncs;
127
+ node = &net->Node[njuncs];
128
+ node->X = MISSING;
129
+ node->Y = MISSING;
130
+ node->El = el;
131
+ node->C0 = 0.0;
132
+ node->S = NULL;
133
+ node->Ke = 0.0;
134
+ node->Rpt = 0;
135
+ node->ResultIndex = 0;
136
+ node->Type = JUNCTION;
137
+ node->Comment = xstrcpy(&node->Comment, parser->Comment, MAXMSG);
138
+
139
+ // Create a demand for the junction and use NodeDemand as an indicator
140
+ // to be used when processing demands from the [DEMANDS] section
141
+ if (!adddemand(node, d, p, NULL)) return 101;
142
+ hyd->NodeDemand[njuncs] = d;
143
+
144
+ // Return error code
145
+ if (errcode > 0) return setError(parser, errtok, errcode);
146
+ return 0;
147
+ }
148
+
149
+ int tankdata(Project *pr)
150
+ /*
151
+ **--------------------------------------------------------------
152
+ ** Input: none
153
+ ** Output: returns error code
154
+ ** Purpose: processes tank & reservoir data
155
+ ** Format:
156
+ ** [RESERVOIRS]
157
+ ** id elev (pattern)
158
+ ** [TANKS]
159
+ ** id elev initlevel minlevel maxlevel diam (minvol vcurve)
160
+ **--------------------------------------------------------------
161
+ */
162
+ {
163
+ Network *net = &pr->network;
164
+ Parser *parser = &pr->parser;
165
+
166
+ int i, // Node index
167
+ n, // # data items
168
+ pattern = 0, // Time pattern index
169
+ curve = 0, // Curve index
170
+ overflow = FALSE;// Overflow indicator
171
+
172
+ double el = 0.0, // Elevation
173
+ initlevel = 0.0, // Initial level
174
+ minlevel = 0.0, // Minimum level
175
+ maxlevel = 0.0, // Maximum level
176
+ minvol = 0.0, // Minimum volume
177
+ diam = 0.0, // Diameter
178
+ area; // X-sect. area
179
+ Snode *node;
180
+ Stank *tank;
181
+
182
+ int errcode = 0;
183
+ int errtok = -1;
184
+ double x;
185
+
186
+ // Add new tank to data base
187
+ if (net->Ntanks == parser->MaxTanks ||
188
+ net->Nnodes == parser->MaxNodes) return 200;
189
+ i = parser->MaxJuncs + net->Ntanks + 1;
190
+ errcode = addnodeID(net, i, parser->Tok[0]);
191
+ if (errcode) return setError(parser, 0, errcode);
192
+ net->Ntanks++;
193
+ net->Nnodes++;
194
+
195
+ // Check for valid data
196
+ n = parser->Ntokens;
197
+ if (n < 2) errcode = 201;
198
+ if (!errcode && !getfloat(parser->Tok[1], &x))
199
+ {
200
+ errcode = 202;
201
+ errtok = 1;
202
+ }
203
+ else el = x;
204
+
205
+ // Node is a reservoir
206
+ if (n <= 3)
207
+ {
208
+ // Head pattern supplied
209
+ if (n == 3 && !errcode)
210
+ {
211
+ pattern = findpattern(net, parser->Tok[2]);
212
+ if (pattern < 0)
213
+ {
214
+ errcode = 205;
215
+ errtok = 2;
216
+ }
217
+ }
218
+ }
219
+
220
+ // Node is a storage tank
221
+ else if (!errcode)
222
+ {
223
+ if (n < 6) errcode = 201;
224
+ else
225
+ {
226
+ // Read required data
227
+ initlevel = gettokvalue(pr, initlevel, 2, &errcode, &errtok);
228
+ minlevel = gettokvalue(pr, minlevel, 3, &errcode, &errtok);
229
+ maxlevel = gettokvalue(pr, maxlevel, 4, &errcode, &errtok);
230
+ diam = gettokvalue(pr, diam, 5, &errcode, &errtok);
231
+ if (n >= 7) minvol = gettokvalue(pr, minvol, 6, &errcode, &errtok);
232
+
233
+ // If volume curve supplied check it exists
234
+ if (!errcode && n >= 8)
235
+ {
236
+ if (strlen(parser->Tok[7]) > 0 && *(parser->Tok[7]) != '*')
237
+ {
238
+ curve = findcurve(net, parser->Tok[7]);
239
+ if (curve == 0)
240
+ {
241
+ errcode = 206;
242
+ errtok = 7;
243
+ }
244
+ else net->Curve[curve].Type = VOLUME_CURVE;
245
+ }
246
+ }
247
+
248
+ // Read overflow indicator if present
249
+ if (!errcode && n >= 9)
250
+ {
251
+ if (match(parser->Tok[8], w_YES)) overflow = TRUE;
252
+ else if (match(parser->Tok[8], w_NO)) overflow = FALSE;
253
+ else
254
+ {
255
+ errcode = 213;
256
+ errtok = 8;
257
+ }
258
+ }
259
+ }
260
+ }
261
+ node = &net->Node[i];
262
+ tank = &net->Tank[net->Ntanks];
263
+
264
+ node->X = MISSING;
265
+ node->Y = MISSING;
266
+ node->Rpt = 0;
267
+ node->ResultIndex = 0;
268
+ node->El = el;
269
+ node->C0 = 0.0;
270
+ node->S = NULL;
271
+ node->Ke = 0.0;
272
+ node->Type = (diam == 0) ? RESERVOIR : TANK;
273
+ node->Comment = xstrcpy(&node->Comment, parser->Comment, MAXMSG);
274
+ tank->Node = i;
275
+ tank->H0 = initlevel;
276
+ tank->Hmin = minlevel;
277
+ tank->Hmax = maxlevel;
278
+ tank->A = diam;
279
+ tank->Pat = pattern;
280
+ tank->Kb = MISSING;
281
+ tank->CanOverflow = overflow;
282
+
283
+ //*******************************************************************
284
+ // NOTE: The min, max, & initial volumes set here are based on a
285
+ // nominal tank diameter. They will be modified in INPUT1.C if
286
+ // a volume curve is supplied for this tank.
287
+ //*******************************************************************
288
+ area = PI * SQR(diam) / 4.0;
289
+ tank->Vmin = area * minlevel;
290
+ if (minvol > 0.0) tank->Vmin = minvol;
291
+ tank->V0 = tank->Vmin + area * (initlevel - minlevel);
292
+ tank->Vmax = tank->Vmin + area * (maxlevel - minlevel);
293
+
294
+ tank->Vcurve = curve;
295
+ tank->MixModel = MIX1; // Completely mixed
296
+ tank->V1frac = 1.0; // Mixing compartment size fraction
297
+
298
+ // Return error code
299
+ if (errcode > 0) return setError(parser, errtok, errcode);
300
+ return 0;
301
+ }
302
+
303
+ double gettokvalue(Project *pr, double x, int itok, int *errcode, int *errtok)
304
+ /*
305
+ **--------------------------------------------------------------
306
+ ** Input: x = default numerical value
307
+ ** itok = index into an array of string tokens
308
+ ** Output: errcode = an error code or 0 if successful
309
+ ** errtok = itok if an error occurs
310
+ ** returns a numerical data value
311
+ ** Purpose: converts a string token into a numerical value.
312
+ **--------------------------------------------------------------
313
+ */
314
+ {
315
+ Parser *parser = &pr->parser;
316
+ double result;
317
+
318
+ if (*errcode) return x;
319
+ if (!getfloat(parser->Tok[itok], &result)) *errcode = 202;
320
+ else if (result < 0.0) *errcode = 209;
321
+ if (*errcode > 0)
322
+ {
323
+ result = x;
324
+ *errtok = itok;
325
+ }
326
+ return result;
327
+ }
328
+
329
+ int pipedata(Project *pr)
330
+ /*
331
+ **--------------------------------------------------------------
332
+ ** Input: none
333
+ ** Output: returns error code
334
+ ** Purpose: processes pipe data
335
+ ** Format:
336
+ ** [PIPE]
337
+ ** id node1 node2 length diam rcoeff (lcoeff) (status)
338
+ **--------------------------------------------------------------
339
+ */
340
+ {
341
+ Network *net = &pr->network;
342
+ Parser *parser = &pr->parser;
343
+
344
+ int j1, // Start-node index
345
+ j2, // End-node index
346
+ n; // # data items
347
+ double x;
348
+ Slink *link;
349
+ int errcode = 0;
350
+
351
+ // Check that end nodes exist
352
+ if (net->Nlinks == parser->MaxLinks) return 200;
353
+ n = parser->Ntokens;
354
+ if (n < 3) return setError(parser, -1, errcode);
355
+ if ((j1 = findnode(net, parser->Tok[1])) == 0) return setError(parser, 1, 203);
356
+ if ((j2 = findnode(net, parser->Tok[2])) == 0) return setError(parser, 2, 203);
357
+ if (j1 == j2) return setError(parser, 0, 222);
358
+
359
+ // Add new pipe to data base
360
+ errcode = addlinkID(net, net->Nlinks+1, parser->Tok[0]);
361
+ if (errcode) return setError(parser, 0, errcode);
362
+ net->Npipes++;
363
+ net->Nlinks++;
364
+
365
+ // Assign default data to pipe
366
+ link = &net->Link[net->Nlinks];
367
+ link->N1 = j1;
368
+ link->N2 = j2;
369
+
370
+ if (parser->Unitsflag == SI)
371
+ {
372
+ link->Len = 100.0;
373
+ link->Diam = 254.0;
374
+ }
375
+ else
376
+ {
377
+ link->Len = 330.0;
378
+ link->Diam = 10.0;
379
+ }
380
+ switch (pr->hydraul.Formflag)
381
+ {
382
+ case HW: link->Kc = 130; break;
383
+ case DW: link->Kc = 0.0005; break;
384
+ case CM: link->Kc = 0.01; break;
385
+ default: link->Kc = 1.0;
386
+ }
387
+
388
+ link->Km = 0.0;
389
+ link->Kb = MISSING;
390
+ link->Kw = MISSING;
391
+ link->LeakArea = 0.0;
392
+ link->LeakExpan = 0.0;
393
+ link->Type = PIPE;
394
+ link->InitStatus = OPEN;
395
+ link->InitSetting = link->Kc;
396
+ link->Rpt = 0;
397
+ link->ResultIndex = 0;
398
+ link->Comment = xstrcpy(&link->Comment, parser->Comment, MAXMSG);
399
+
400
+ // Parse data values from input tokens
401
+ if (n > 3)
402
+ {
403
+ if (!getfloat(parser->Tok[3], &x) || x <= 0.0)
404
+ return setError(parser, 3, 202);
405
+ link->Len = x;
406
+ }
407
+ if (n > 4)
408
+ {
409
+ if (!getfloat(parser->Tok[4], &x) || x <= 0.0)
410
+ return setError(parser, 4, 202);
411
+ link->Diam = x;
412
+ }
413
+ if (n > 5)
414
+ {
415
+ if (!getfloat(parser->Tok[5], &x) || x <= 0.0)
416
+ return setError(parser, 5, 202);
417
+ link->Kc = x;
418
+ }
419
+
420
+ // Either a loss coeff. or a status is supplied
421
+ if (n > 6)
422
+ {
423
+ if (match(parser->Tok[6], w_CV)) link->Type = CVPIPE;
424
+ else if (match(parser->Tok[6], w_CLOSED)) link->InitStatus = CLOSED;
425
+ else if (match(parser->Tok[6], w_OPEN)) link->InitStatus = OPEN;
426
+ else
427
+ {
428
+ if (!getfloat(parser->Tok[6], &x) || x < 0.0)
429
+ return setError(parser, 6, 202);
430
+ link->Km = x;
431
+ }
432
+ }
433
+
434
+ // Both a loss coeff. and a status is supplied
435
+ if (n > 7)
436
+ {
437
+ if (!getfloat(parser->Tok[6], &x) || x < 0.0)
438
+ return setError(parser, 6, 202);
439
+ link->Km = x;
440
+ if (match(parser->Tok[7], w_CV)) link->Type = CVPIPE;
441
+ else if (match(parser->Tok[7], w_CLOSED)) link->InitStatus = CLOSED;
442
+ else if (match(parser->Tok[7], w_OPEN)) link->InitStatus = OPEN;
443
+ else return setError(parser, 7, 213);
444
+ }
445
+ return 0;
446
+ }
447
+
448
+ int pumpdata(Project *pr)
449
+ /*
450
+ **--------------------------------------------------------------
451
+ ** Input: none
452
+ ** Output: returns error code
453
+ ** Purpose: processes pump data
454
+ ** Formats:
455
+ ** [PUMP]
456
+ ** id node1 node2 KEYWORD value {KEYWORD value ...}
457
+ ** where KEYWORD = [POWER,HEAD,PATTERN,SPEED]
458
+ **--------------------------------------------------------------
459
+ */
460
+ {
461
+ Network *net = &pr->network;
462
+ Parser *parser = &pr->parser;
463
+
464
+ int m, // Token array indexes
465
+ j1, // Start-node index
466
+ j2, // End-node index
467
+ n, // # data items
468
+ c, p; // Curve & Pattern indexes
469
+ double y;
470
+ Slink *link;
471
+ Spump *pump;
472
+ int errcode = 0;
473
+
474
+ // Check that end nodes exist
475
+ if (net->Nlinks == parser->MaxLinks ||
476
+ net->Npumps == parser->MaxPumps) return 200;
477
+ n = parser->Ntokens;
478
+ if (n < 3) return setError(parser, -1, errcode);
479
+ if ((j1 = findnode(net, parser->Tok[1])) == 0) return setError(parser, 1, 203);
480
+ if ((j2 = findnode(net, parser->Tok[2])) == 0) return setError(parser, 2, 203);
481
+ if (j1 == j2) return setError(parser, 0, 222);
482
+
483
+ // Add new pump to data base
484
+ errcode = addlinkID(net, net->Nlinks+1, parser->Tok[0]);
485
+ if (errcode) return setError(parser, 0, errcode);
486
+ net->Nlinks++;
487
+ net->Npumps++;
488
+
489
+ // Assign default data to pump
490
+ link = &net->Link[net->Nlinks];
491
+ pump = &net->Pump[net->Npumps];
492
+
493
+ link->N1 = j1;
494
+ link->N2 = j2;
495
+ link->Diam = 0;
496
+ link->Len = 0.0;
497
+ link->Kc = 1.0;
498
+ link->Km = 0.0;
499
+ link->Kb = 0.0;
500
+ link->Kw = 0.0;
501
+ link->LeakArea = 0.0;
502
+ link->LeakExpan = 0.0;
503
+ link->Type = PUMP;
504
+ link->InitStatus = OPEN;
505
+ link->InitSetting = 1.0;
506
+ link->Rpt = 0;
507
+ link->ResultIndex = 0;
508
+ link->Comment = xstrcpy(&link->Comment, parser->Comment, MAXMSG);
509
+ pump->Link = net->Nlinks;
510
+ pump->Ptype = NOCURVE; // NOCURVE is a placeholder
511
+ pump->Hcurve = 0;
512
+ pump->Ecurve = 0;
513
+ pump->Upat = 0;
514
+ pump->Ecost = 0.0;
515
+ pump->Epat = 0;
516
+ if (n < 4) return 0;
517
+
518
+ // Retrieve keyword/value pairs
519
+ m = 4;
520
+ while (m < n)
521
+ {
522
+ if (match(parser->Tok[m - 1], w_POWER)) // Const. HP curve
523
+ {
524
+ if (!getfloat(parser->Tok[m], &y) || y <= 0.0)
525
+ return setError(parser, m, 202);
526
+ pump->Ptype = CONST_HP;
527
+ link->Km = y;
528
+ }
529
+ else if (match(parser->Tok[m - 1], w_HEAD)) // Custom pump curve
530
+ {
531
+ c = findcurve(net, parser->Tok[m]);
532
+ if (c == 0) return setError(parser, m, 206);
533
+ pump->Ptype = CUSTOM;
534
+ pump->Hcurve = c;
535
+ }
536
+ else if (match(parser->Tok[m - 1], w_PATTERN)) // Speed/status pattern
537
+ {
538
+ p = findpattern(net, parser->Tok[m]);
539
+ if (p < 0) return setError(parser, m, 205);
540
+ pump->Upat = p;
541
+ }
542
+ else if (match(parser->Tok[m - 1], w_SPEED)) // Speed setting
543
+ {
544
+ if (!getfloat(parser->Tok[m], &y) || y < 0.0)
545
+ return setError(parser, m, 202);
546
+ link->Kc = y;
547
+ }
548
+ else return setError(parser, m-1, 201);;
549
+ m = m + 2; // Move to next keyword token
550
+ }
551
+ link->InitSetting = link->Kc;
552
+ return 0;
553
+ }
554
+
555
+ int valvedata(Project *pr)
556
+ /*
557
+ **--------------------------------------------------------------
558
+ ** Input: none
559
+ ** Output: returns error code
560
+ ** Purpose: processes valve data
561
+ ** Format:
562
+ ** [VALVE]
563
+ ** id node1 node2 diam type setting (lcoeff lcurve)
564
+ **--------------------------------------------------------------
565
+ */
566
+ {
567
+ Network *net = &pr->network;
568
+ Parser *parser = &pr->parser;
569
+
570
+ int c, // Curve index
571
+ j1, // Start-node index
572
+ j2, // End-node index
573
+ n; // # data items
574
+ char status = ACTIVE, // Valve status
575
+ type; // Valve type
576
+ double x;
577
+ Slink *link;
578
+ int errcode = 0,
579
+ losscurve = 0; // Loss coeff. curve
580
+
581
+ // Check that end nodes exist
582
+ if (net->Nlinks == parser->MaxLinks ||
583
+ net->Nvalves == parser->MaxValves) return 200;
584
+ n = parser->Ntokens;
585
+ if (n < 5) return setError(parser, -1, errcode);
586
+ if ((j1 = findnode(net, parser->Tok[1])) == 0) return setError(parser, 1, 203);
587
+ if ((j2 = findnode(net, parser->Tok[2])) == 0) return setError(parser, 2, 203);
588
+ if (j1 == j2) return setError(parser, 0, 222);
589
+
590
+ // Parse valve type
591
+ if (match(parser->Tok[4], w_PRV)) type = PRV;
592
+ else if (match(parser->Tok[4], w_PSV)) type = PSV;
593
+ else if (match(parser->Tok[4], w_PBV)) type = PBV;
594
+ else if (match(parser->Tok[4], w_FCV)) type = FCV;
595
+ else if (match(parser->Tok[4], w_TCV)) type = TCV;
596
+ else if (match(parser->Tok[4], w_GPV)) type = GPV;
597
+ else if (match(parser->Tok[4], w_PCV)) type = PCV;
598
+ else return setError(parser, 4, 213);
599
+
600
+ // Check for illegal connections
601
+ if (valvecheck(pr, net->Nlinks, type, j1, j2))
602
+ {
603
+ if (j1 > net->Njuncs) return setError(parser, 1, 219);
604
+ else if (j2 > net->Njuncs) return setError(parser, 2, 219);
605
+ else return setError(parser, -1, 220);
606
+ }
607
+
608
+ // Add new valve to data base
609
+ errcode = addlinkID(net, net->Nlinks+1, parser->Tok[0]);
610
+ if (errcode) return setError(parser, 0, errcode);
611
+ net->Nvalves++;
612
+ net->Nlinks++;
613
+
614
+ // Assign default data to valve
615
+ link = &net->Link[net->Nlinks];
616
+ link->N1 = j1;
617
+ link->N2 = j2;
618
+ if (parser->Unitsflag == SI) link->Diam = 254.0;
619
+ else link->Diam = 10.0;
620
+ link->Len = 0.0;
621
+ link->Kc = 0.0;
622
+ link->Km = 0.0;
623
+ link->Kb = 0.0;
624
+ link->Kw = 0.0;
625
+ link->LeakArea = 0.0;
626
+ link->LeakExpan = 0.0;
627
+ link->Type = type;
628
+ link->InitStatus = ACTIVE;
629
+ link->InitSetting = 0.0;
630
+ link->Rpt = 0;
631
+ link->ResultIndex = 0;
632
+ link->Comment = xstrcpy(&link->Comment, parser->Comment, MAXMSG);
633
+ net->Valve[net->Nvalves].Link = net->Nlinks;
634
+ net->Valve[net->Nvalves].Curve = 0;
635
+
636
+ // Parse data values
637
+ if (!getfloat(parser->Tok[3], &x) || x <= 0.0)
638
+ return setError(parser, 3, 202);
639
+ link->Diam = x;
640
+ if (n > 5)
641
+ {
642
+ // Find headloss curve for GPV
643
+ if (type == GPV)
644
+ {
645
+ c = findcurve(net, parser->Tok[5]);
646
+ if (c == 0) return setError(parser, 5, 206);
647
+ link->Kc = c;
648
+ net->Curve[c].Type = HLOSS_CURVE;
649
+ link->InitStatus = OPEN;
650
+ }
651
+ else
652
+ {
653
+ if (!getfloat(parser->Tok[5], &x)) return setError(parser, 5, 202);
654
+ link->Kc = x;
655
+ }
656
+ }
657
+ if (n > 6)
658
+ {
659
+ if (!getfloat(parser->Tok[6], &x) || x < 0.0)
660
+ return setError(parser, 6, 202);
661
+ link->Km = x;
662
+ }
663
+ if (n > 7 && type == PCV)
664
+ {
665
+ // Find loss coeff. curve for PCV
666
+ c = findcurve(net, parser->Tok[7]);
667
+ if (c == 0) return setError(parser, 7, 206);
668
+ net->Valve[net->Nvalves].Curve = c;
669
+ net->Curve[c].Type = VALVE_CURVE;
670
+ if (link->Kc > 100.0) link->Kc = 100.0;
671
+ }
672
+ link->InitSetting = link->Kc;
673
+ return 0;
674
+ }
675
+
676
+ int patterndata(Project *pr)
677
+ /*
678
+ **--------------------------------------------------------------
679
+ ** Input: none
680
+ ** Output: returns error code
681
+ ** Purpose: processes time pattern data
682
+ ** Format:
683
+ ** [PATTERNS]
684
+ ** id mult1 mult2 .....
685
+ **--------------------------------------------------------------
686
+ */
687
+ {
688
+ Network *net = &pr->network;
689
+ Parser *parser = &pr->parser;
690
+
691
+ int i, j, n, n1;
692
+ double x;
693
+ Spattern *pattern;
694
+
695
+ // "n" is the number of pattern factors contained in the line
696
+ n = parser->Ntokens - 1;
697
+ if (n < 1) return 201;
698
+
699
+ // Check if previous input line was for the same pattern
700
+ if (parser->PrevPat && strcmp(parser->Tok[0], parser->PrevPat->ID) == 0)
701
+ {
702
+ pattern = parser->PrevPat;
703
+ }
704
+
705
+ // Otherwise retrieve pattern from the network's Pattern array
706
+ else
707
+ {
708
+ i = findpattern(net, parser->Tok[0]);
709
+ if (i <= 0) return setError(parser, 0, 205);
710
+ pattern = &(net->Pattern[i]);
711
+ if (pattern->Comment == NULL && parser->Comment[0])
712
+ {
713
+ pattern->Comment = xstrcpy(&pattern->Comment, parser->Comment, MAXMSG);
714
+ }
715
+ }
716
+
717
+ // Expand size of the pattern's factors array
718
+ n1 = pattern->Length;
719
+ pattern->Length += n;
720
+ pattern->F = realloc(pattern->F, pattern->Length * sizeof(double));
721
+
722
+ // Add parsed multipliers to the pattern
723
+ for (j = 1; j <= n; j++) pattern->F[n1 + j - 1] = 1.0;
724
+ for (j = 1; j <= n; j++)
725
+ {
726
+ if (!getfloat(parser->Tok[j], &x)) return setError(parser, j, 202);
727
+ pattern->F[n1 + j - 1] = x;
728
+ }
729
+
730
+ // Save a reference to this pattern for processing additional pattern data
731
+ parser->PrevPat = pattern;
732
+ return 0;
733
+ }
734
+
735
+ int curvedata(Project *pr)
736
+ /*
737
+ **------------------------------------------------------
738
+ ** Input: none
739
+ ** Output: returns error code
740
+ ** Purpose: processes curve data
741
+ ** Format:
742
+ ** [CURVES]
743
+ ** CurveID x-value y-value
744
+ **------------------------------------------------------
745
+ */
746
+ {
747
+ Network *net = &pr->network;
748
+ Parser *parser = &pr->parser;
749
+
750
+ int i, ctype;
751
+ double x, y;
752
+ Scurve *curve;
753
+
754
+ // Check for valid data
755
+ if (parser->Ntokens < 3) return 201;
756
+ if (!getfloat(parser->Tok[1], &x)) return setError(parser, 1, 202);
757
+ if (!getfloat(parser->Tok[2], &y)) return setError(parser, 2, 202);
758
+ ctype = -1;
759
+ if (parser->Ntokens > 3)
760
+ {
761
+ ctype = findmatch(parser->Tok[3], CurveTypeTxt);
762
+ }
763
+
764
+ // Check if previous input line was for the same curve
765
+ if (parser->PrevCurve && strcmp(parser->Tok[0], parser->PrevCurve->ID) == 0)
766
+ {
767
+ curve = parser->PrevCurve;
768
+ }
769
+
770
+ // Otherwise retrieve curve from the network's Curve array
771
+ else
772
+ {
773
+ i = findcurve(net, parser->Tok[0]);
774
+ if (i == 0) return setError(parser, 0, 206);
775
+ curve = &(net->Curve[i]);
776
+ if (curve->Comment == NULL && parser->Comment[0])
777
+ {
778
+ curve->Comment = xstrcpy(&curve->Comment, parser->Comment, MAXMSG);
779
+ }
780
+ }
781
+
782
+ // Expand size of data arrays if need be
783
+ if (curve->Capacity == curve->Npts)
784
+ {
785
+ if (resizecurve(curve, curve->Capacity + 10) > 0) return 101;
786
+ }
787
+
788
+ // Add new data point to curve
789
+ curve->X[curve->Npts] = x;
790
+ curve->Y[curve->Npts] = y;
791
+ curve->Npts++;
792
+ if (ctype >= 0) curve->Type = (CurveType)ctype;
793
+
794
+ // Save a reference to this curve for processing additional curve data
795
+ parser->PrevCurve = curve;
796
+ return 0;
797
+ }
798
+
799
+ int coordata(Project *pr)
800
+ /*
801
+ **--------------------------------------------------------------
802
+ ** Input: none
803
+ ** Output: returns 0
804
+ ** Purpose: processes node coordinate data
805
+ ** Format:
806
+ ** [COORD]
807
+ ** id x y
808
+ **
809
+ ** Note: since node coords. are not used in any computations,
810
+ ** invalid data are simply ignored.
811
+ **--------------------------------------------------------------
812
+ */
813
+ {
814
+ Network *net = &pr->network;
815
+ Parser *parser = &pr->parser;
816
+
817
+ int j;
818
+ double x, y;
819
+ Snode *node;
820
+
821
+ // Check for valid node ID
822
+ if (parser->Ntokens < 3) return 0;
823
+ if ((j = findnode(net, parser->Tok[0])) == 0) return 0;
824
+
825
+ // Check for valid data
826
+ if (!getfloat(parser->Tok[1], &x)) return 0;
827
+ if (!getfloat(parser->Tok[2], &y)) return 0;
828
+
829
+ // Save coord data
830
+ node = &net->Node[j];
831
+ node->X = x;
832
+ node->Y = y;
833
+ return 0;
834
+ }
835
+
836
+ int vertexdata(Project *pr)
837
+ /*
838
+ **--------------------------------------------------------------
839
+ ** Input: none
840
+ ** Output: returns 0
841
+ ** Purpose: processes link vertex data
842
+ ** Format:
843
+ ** [VERTICES]
844
+ ** id x y
845
+ **
846
+ ** Note: since vertex coords. are not used in any computations,
847
+ ** invalid data are simply ignored.
848
+ **--------------------------------------------------------------
849
+ */
850
+ {
851
+ Network *net = &pr->network;
852
+ Parser *parser = &pr->parser;
853
+
854
+ int j;
855
+ double x, y;
856
+
857
+ // Check for valid link ID
858
+ if (parser->Ntokens < 3) return 0;
859
+ if ((j = findlink(net, parser->Tok[0])) == 0) return 0;
860
+
861
+ // Check for valid coordinate data
862
+ if (!getfloat(parser->Tok[1], &x)) return 0;
863
+ if (!getfloat(parser->Tok[2], &y)) return 0;
864
+
865
+ // Add to link's list of vertex points
866
+ return addlinkvertex(&net->Link[j], x, y);
867
+ }
868
+
869
+
870
+ int demanddata(Project *pr)
871
+ /*
872
+ **--------------------------------------------------------------
873
+ ** Input: none
874
+ ** Output: returns error code
875
+ ** Purpose: processes node demand data
876
+ ** Format:
877
+ ** [DEMANDS]
878
+ ** node base_demand (pattern)
879
+ **
880
+ ** NOTE: Demands entered in this section replace those
881
+ ** entered in the [JUNCTIONS] section
882
+ **--------------------------------------------------------------
883
+ */
884
+ {
885
+ Network *net = &pr->network;
886
+ Hydraul *hyd = &pr->hydraul;
887
+ Parser *parser = &pr->parser;
888
+
889
+ int j, n, p = 0;
890
+ double y;
891
+
892
+ Pdemand demand;
893
+
894
+ // Extract data from tokens
895
+ n = parser->Ntokens;
896
+ if (n < 2) return 201;
897
+ if (!getfloat(parser->Tok[1], &y)) return setError(parser, 1, 202);
898
+
899
+ // Find node (and pattern) being referenced
900
+ if ((j = findnode(net, parser->Tok[0])) == 0) return setError(parser, 0, 203);
901
+ if (j > net->Njuncs) return 0;
902
+ if (n >= 3)
903
+ {
904
+ p = findpattern(net, parser->Tok[2]);
905
+ if (p < 0) return setError(parser, 2, 205);
906
+ }
907
+
908
+ // Replace any demand entered in [JUNCTIONS] section
909
+ demand = net->Node[j].D;
910
+ if (demand && hyd->NodeDemand[j] != MISSING)
911
+ {
912
+ // First category encountered will overwrite demand category
913
+ // created when junction was read from [JUNCTIONS] section
914
+ demand->Base = y;
915
+ demand->Pat = p;
916
+ if (parser->Comment[0])
917
+ {
918
+ demand->Name = xstrcpy(&demand->Name, parser->Comment, MAXID);
919
+ }
920
+ hyd->NodeDemand[j] = MISSING; // marker - next iteration will append a new category.
921
+ }
922
+
923
+ // Otherwise add new demand to junction
924
+ else if (!adddemand(&net->Node[j], y, p, parser->Comment) > 0) return 101;
925
+ return 0;
926
+ }
927
+
928
+ int controldata(Project *pr)
929
+ /*
930
+ **--------------------------------------------------------------
931
+ ** Input: none
932
+ ** Output: returns error code
933
+ ** Purpose: processes simple controls
934
+ ** Formats:
935
+ ** [CONTROLS]
936
+ ** LINK linkID setting IF NODE nodeID {BELOW/ABOVE} level (DISABLED)
937
+ ** LINK linkID setting AT TIME value (units) (DISABLED)
938
+ ** LINK linkID setting AT CLOCKTIME value (units) (DISABLED)
939
+ ** (0) (1) (2) (3) (4) (5) (6) (7) (8)
940
+ **--------------------------------------------------------------
941
+ */
942
+ {
943
+ Network *net = &pr->network;
944
+ Parser *parser = &pr->parser;
945
+
946
+ int i = 0, // Node index
947
+ k, // Link index
948
+ n, // # data items
949
+ isEnabled = TRUE; // Control enabled
950
+ double setting = MISSING, // Link setting
951
+ time = 0.0, // Simulation time
952
+ level = 0.0; // Pressure or tank level
953
+ StatusType status = ACTIVE; // Link status
954
+ ControlType ctltype; // Control type
955
+ LinkType linktype; // Link type
956
+ Scontrol *control;
957
+
958
+ // Check for sufficient number of input tokens
959
+ n = parser->Ntokens;
960
+ if (n < 6) return 201;
961
+
962
+ // Check if last token is "DISABLED"
963
+ if (match(parser->Tok[n-1], w_DISABLED))
964
+ {
965
+ isEnabled = FALSE;
966
+ n = n - 1;
967
+ }
968
+
969
+ // Check that controlled link exists
970
+ k = findlink(net, parser->Tok[1]);
971
+ if (k == 0) return setError(parser, 1, 204);
972
+
973
+ // Cannot control a check valve
974
+ linktype = net->Link[k].Type;
975
+ if (linktype == CVPIPE) return setError(parser, 1, 207);
976
+
977
+ // Parse control setting into a status level or numerical setting
978
+ if (match(parser->Tok[2], w_OPEN))
979
+ {
980
+ status = OPEN;
981
+ if (linktype == PUMP) setting = 1.0;
982
+ if (linktype == GPV) setting = net->Link[k].Kc;
983
+ }
984
+ else if (match(parser->Tok[2], w_CLOSED))
985
+ {
986
+ status = CLOSED;
987
+ if (linktype == PUMP) setting = 0.0;
988
+ if (linktype == GPV) setting = net->Link[k].Kc;
989
+ }
990
+ else if (linktype == GPV) return setError(parser, 1, 207);
991
+ else if (!getfloat(parser->Tok[2], &setting)) return setError(parser, 2, 202);
992
+
993
+ // Set status for pump in case speed setting was supplied
994
+ // or for pipe if numerical setting was supplied
995
+ if (linktype == PUMP || linktype == PIPE)
996
+ {
997
+ if (setting != MISSING)
998
+ {
999
+ if (setting < 0.0) return setError(parser, 2, 211);
1000
+ else if (setting == 0.0) status = CLOSED;
1001
+ else status = OPEN;
1002
+ }
1003
+ }
1004
+
1005
+ // Determine type of control
1006
+ if (match(parser->Tok[4], w_TIME)) ctltype = TIMER;
1007
+ else if (match(parser->Tok[4], w_CLOCKTIME)) ctltype = TIMEOFDAY;
1008
+ else
1009
+ {
1010
+ if (n < 8) return 201;
1011
+ if ((i = findnode(net, parser->Tok[5])) == 0) return setError(parser, 5, 203);
1012
+ if (match(parser->Tok[6], w_BELOW)) ctltype = LOWLEVEL;
1013
+ else if (match(parser->Tok[6], w_ABOVE)) ctltype = HILEVEL;
1014
+ else return setError(parser, 6, 213);
1015
+ }
1016
+
1017
+ // Parse control level or time
1018
+ switch (ctltype)
1019
+ {
1020
+ case TIMER:
1021
+ case TIMEOFDAY:
1022
+ if (n == 6) time = hour(parser->Tok[5], "");
1023
+ if (n >= 7) time = hour(parser->Tok[5], parser->Tok[6]);
1024
+ if (time < 0.0) return setError(parser, 5, 213);
1025
+ break;
1026
+ case LOWLEVEL:
1027
+ case HILEVEL:
1028
+ if (!getfloat(parser->Tok[7], &level)) return setError(parser, 7, 202);
1029
+ break;
1030
+ }
1031
+
1032
+ // Fill in fields of control data structure
1033
+ net->Ncontrols++;
1034
+ if (net->Ncontrols > parser->MaxControls) return 200;
1035
+ control = &net->Control[net->Ncontrols];
1036
+ control->Link = k;
1037
+ control->Node = i;
1038
+ control->Type = ctltype;
1039
+ control->Status = status;
1040
+ control->Setting = setting;
1041
+ control->Time = (long)(3600.0 * time);
1042
+ if (ctltype == TIMEOFDAY) control->Time %= SECperDAY;
1043
+ control->Grade = level;
1044
+ control->isEnabled = isEnabled;
1045
+ return 0;
1046
+ }
1047
+
1048
+ int sourcedata(Project *pr)
1049
+ /*
1050
+ **--------------------------------------------------------------
1051
+ ** Input: none
1052
+ ** Output: returns error code
1053
+ ** Purpose: processes water quality source data
1054
+ ** Formats:
1055
+ ** [SOURCE]
1056
+ ** node sourcetype quality (pattern)
1057
+ **
1058
+ ** NOTE: units of mass-based source are mass/min
1059
+ **--------------------------------------------------------------
1060
+ */
1061
+ {
1062
+ Network *net = &pr->network;
1063
+ Parser *parser = &pr->parser;
1064
+
1065
+ int i, // Token with quality value
1066
+ j, // Node index
1067
+ n, // # data items
1068
+ p = 0; // Time pattern index
1069
+ char type = CONCEN; // Source type
1070
+ double c0 = 0; // Initial quality
1071
+ Psource source;
1072
+
1073
+ // Check for enough tokens & that source node exists
1074
+ n = parser->Ntokens;
1075
+ if (n < 2) return 201;
1076
+ if ((j = findnode(net, parser->Tok[0])) == 0) return setError(parser, 0, 203);
1077
+
1078
+ // Parse source type
1079
+ // NOTE: Under old 1.1 format, SourceType not supplied so
1080
+ // let i = index of token that contains quality value
1081
+ i = 2;
1082
+ if (match(parser->Tok[1], w_CONCEN)) type = CONCEN;
1083
+ else if (match(parser->Tok[1], w_MASS)) type = MASS;
1084
+ else if (match(parser->Tok[1], w_SETPOINT)) type = SETPOINT;
1085
+ else if (match(parser->Tok[1], w_FLOWPACED)) type = FLOWPACED;
1086
+ else i = 1;
1087
+
1088
+ // Parse source quality
1089
+ if (!getfloat(parser->Tok[i], &c0))
1090
+ {
1091
+ if (i == 1) return setError(parser, i, 213);
1092
+ else return setError(parser, i, 202);
1093
+ }
1094
+
1095
+ // Parse optional source time pattern
1096
+ if (n > i + 1 && strlen(parser->Tok[i + 1]) > 0 &&
1097
+ strcmp(parser->Tok[i + 1], "*") != 0)
1098
+ {
1099
+ p = findpattern(net, parser->Tok[i + 1]);
1100
+ if (p < 0) return setError(parser, i + 1, 205);
1101
+ }
1102
+
1103
+ // Destroy any existing source assigned to node
1104
+ if (net->Node[j].S != NULL) free(net->Node[j].S);
1105
+
1106
+ // Create a new source & assign it to the node
1107
+ source = (struct Ssource *)malloc(sizeof(struct Ssource));
1108
+ if (source == NULL) return 101;
1109
+ source->C0 = c0;
1110
+ source->Pat = p;
1111
+ source->Type = type;
1112
+ net->Node[j].S = source;
1113
+ return 0;
1114
+ }
1115
+
1116
+ int emitterdata(Project *pr)
1117
+ /*
1118
+ **--------------------------------------------------------------
1119
+ ** Input: none
1120
+ ** Output: returns error code
1121
+ ** Purpose: processes junction emitter data
1122
+ ** Format:
1123
+ ** [EMITTER]
1124
+ ** node Ke
1125
+ **--------------------------------------------------------------
1126
+ */
1127
+ {
1128
+ Network *net = &pr->network;
1129
+ Parser *parser = &pr->parser;
1130
+
1131
+ int j, // Node index
1132
+ n; // # data items
1133
+ double k; // Flow coeff.
1134
+
1135
+ // Check that node exists & is a junction
1136
+ n = parser->Ntokens;
1137
+ if (n < 2) return 201;
1138
+ if ((j = findnode(net, parser->Tok[0])) == 0) return setError(parser, 0, 203);
1139
+ if (j > net->Njuncs) return 0;
1140
+
1141
+ // Parse emitter flow coeff.
1142
+ if (!getfloat(parser->Tok[1], &k)) return setError(parser, 1, 202);
1143
+ if (k < 0.0) return setError(parser, 1, 209);
1144
+ net->Node[j].Ke = k;
1145
+ return 0;
1146
+ }
1147
+
1148
+ int leakagedata(Project *pr)
1149
+ /*
1150
+ **--------------------------------------------------------------
1151
+ ** Input: none
1152
+ ** Output: returns error code
1153
+ ** Purpose: processes link leakage data
1154
+ ** Format:
1155
+ ** [LEAKAGE]
1156
+ ** link C1 C2
1157
+ **--------------------------------------------------------------
1158
+ */
1159
+ {
1160
+ Network *net = &pr->network;
1161
+ Parser *parser = &pr->parser;
1162
+
1163
+ int j, // Link index
1164
+ n; // # data items
1165
+ double c1, c2; // Flow coeff.
1166
+
1167
+ // Check that link exists & is a pipe
1168
+ n = parser->Ntokens;
1169
+ if (n < 3) return 201;
1170
+ if ((j = findlink(net, parser->Tok[0])) == 0) return setError(parser, 0, 203);
1171
+ if (net->Link[j].Type > PIPE) return 0;
1172
+
1173
+ // Parse leakage coeffs.
1174
+ if (!getfloat(parser->Tok[1], &c1)) return setError(parser, 1, 202);
1175
+ if (c1 < 0.0) return setError(parser, 1, 209);
1176
+ if (!getfloat(parser->Tok[2], &c2)) return setError(parser, 2, 202);
1177
+ if (c2 < 0.0) return setError(parser, 1, 209);
1178
+ net->Link[j].LeakArea = c1;
1179
+ net->Link[j].LeakExpan = c2;
1180
+ return 0;
1181
+ }
1182
+
1183
+ int qualdata(Project *pr)
1184
+ /*
1185
+ **--------------------------------------------------------------
1186
+ ** Input: none
1187
+ ** Output: returns error code
1188
+ ** Purpose: processes initial water quality data
1189
+ ** Formats:
1190
+ ** [QUALITY]
1191
+ ** node initqual
1192
+ ** node1 node2 initqual
1193
+ **--------------------------------------------------------------
1194
+ */
1195
+ {
1196
+ Network *net = &pr->network;
1197
+ Parser *parser = &pr->parser;
1198
+
1199
+ int j, n;
1200
+ long i, i1, i2;
1201
+ double c0;
1202
+ Snode *Node = net->Node;
1203
+
1204
+ if (net->Nnodes == 0) return setError(parser, 0, 203); // No nodes defined yet
1205
+ n = parser->Ntokens;
1206
+ if (n < 2) return 0;
1207
+
1208
+ // Single node name supplied
1209
+ if (n == 2)
1210
+ {
1211
+ if ((j = findnode(net,parser->Tok[0])) == 0) return setError(parser, 0, 203);
1212
+ if (!getfloat(parser->Tok[1], &c0)) return setError(parser, 1, 202);
1213
+ if (c0 < 0.0) return setError(parser, 1, 209);
1214
+ Node[j].C0 = c0;
1215
+ }
1216
+
1217
+ // Range of node names supplied
1218
+ else
1219
+ {
1220
+ // Parse quality value
1221
+ if (!getfloat(parser->Tok[2], &c0)) return setError(parser, 2, 202);
1222
+ if (c0 < 0.0) return setError(parser, 2, 209);
1223
+
1224
+ // If numerical node names supplied, then use numerical comparison
1225
+ // to find which nodes are assigned the quality value
1226
+ if ((i1 = atol(parser->Tok[0])) > 0 &&
1227
+ (i2 = atol(parser->Tok[1])) > 0)
1228
+ {
1229
+ for (j = 1; j <= net->Nnodes; j++)
1230
+ {
1231
+ i = atol(Node[j].ID);
1232
+ if (i >= i1 && i <= i2) Node[j].C0 = c0;
1233
+ }
1234
+ }
1235
+
1236
+ // Otherwise use lexicographic comparison
1237
+ else
1238
+ {
1239
+ for (j = 1; j <= net->Nnodes; j++)
1240
+ {
1241
+ if ((strcmp(parser->Tok[0], Node[j].ID) <= 0) &&
1242
+ (strcmp(parser->Tok[1], Node[j].ID) >= 0)
1243
+ ) Node[j].C0 = c0;
1244
+ }
1245
+ }
1246
+ }
1247
+ return 0;
1248
+ }
1249
+
1250
+ int reactdata(Project *pr)
1251
+ /*
1252
+ **--------------------------------------------------------------
1253
+ ** Input: none
1254
+ ** Output: returns error code
1255
+ ** Purpose: processes reaction coeff. data
1256
+ ** Formats:
1257
+ ** [REACTIONS]
1258
+ ** ORDER {BULK/WALL/TANK} value
1259
+ ** GLOBAL BULK coeff
1260
+ ** GLOBAL WALL coeff
1261
+ ** BULK link1 (link2) coeff
1262
+ ** WALL link1 (link2) coeff
1263
+ ** TANK node1 (node2) coeff
1264
+ ** LIMITING POTENTIAL value
1265
+ ** ROUGHNESS CORRELATION value
1266
+ **--------------------------------------------------------------
1267
+ */
1268
+ {
1269
+ Network *net = &pr->network;
1270
+ Quality *qual = &pr->quality;
1271
+ Parser *parser = &pr->parser;
1272
+
1273
+ int item, j, n;
1274
+ long i, i1, i2;
1275
+ double y;
1276
+
1277
+ // Skip line if insufficient data
1278
+ n = parser->Ntokens;
1279
+ if (n < 3) return 0;
1280
+
1281
+ // Keyword is ORDER
1282
+ if (match(parser->Tok[0], w_ORDER))
1283
+ {
1284
+ if (!getfloat(parser->Tok[n - 1], &y)) return setError(parser, n-1, 202);
1285
+ if (match(parser->Tok[1], w_BULK)) qual->BulkOrder = y;
1286
+ else if (match(parser->Tok[1], w_TANK)) qual->TankOrder = y;
1287
+ else if (match(parser->Tok[1], w_WALL))
1288
+ {
1289
+ if (y == 0.0) qual->WallOrder = 0.0;
1290
+ else if (y == 1.0) qual->WallOrder = 1.0;
1291
+ else return setError(parser, n-1, 213);
1292
+ }
1293
+ else return setError(parser, 1, 213);
1294
+ return 0;
1295
+ }
1296
+
1297
+ // Keyword is ROUGHNESS
1298
+ if (match(parser->Tok[0], w_ROUGHNESS))
1299
+ {
1300
+ if (!getfloat(parser->Tok[n - 1], &y)) return setError(parser, n-1, 202);
1301
+ qual->Rfactor = y;
1302
+ return 0;
1303
+ }
1304
+
1305
+ // Keyword is LIMITING
1306
+ if (match(parser->Tok[0], w_LIMITING))
1307
+ {
1308
+ if (!getfloat(parser->Tok[n - 1], &y)) return setError(parser, n-1, 202);
1309
+ qual->Climit = y;
1310
+ return 0;
1311
+ }
1312
+
1313
+ // Keyword is GLOBAL
1314
+ if (match(parser->Tok[0], w_GLOBAL))
1315
+ {
1316
+ if (!getfloat(parser->Tok[n - 1], &y)) return setError(parser, n-1, 202);
1317
+ if (match(parser->Tok[1], w_BULK)) qual->Kbulk = y;
1318
+ else if (match(parser->Tok[1], w_WALL)) qual->Kwall = y;
1319
+ else return setError(parser, 1, 213);
1320
+ return 0;
1321
+ }
1322
+
1323
+ // Keyword is BULK, WALL or TANK
1324
+ if (match(parser->Tok[0], w_BULK)) item = 1;
1325
+ else if (match(parser->Tok[0], w_WALL)) item = 2;
1326
+ else if (match(parser->Tok[0], w_TANK)) item = 3;
1327
+ else return setError(parser, 0, 213);
1328
+
1329
+ // Case where tank rate coeffs. are being set
1330
+ if (item == 3)
1331
+ {
1332
+ // Get the rate coeff. value
1333
+ if (!getfloat(parser->Tok[n - 1], &y)) return setError(parser, n-1, 202);
1334
+
1335
+ // Case where just a single tank is specified
1336
+ if (n == 3)
1337
+ {
1338
+ if ((j = findnode(net,parser->Tok[1])) <= net->Njuncs) return 0;
1339
+ net->Tank[j - net->Njuncs].Kb = y;
1340
+ }
1341
+
1342
+ // Case where a numerical range of tank IDs is specified
1343
+ else if ((i1 = atol(parser->Tok[1])) > 0 &&
1344
+ (i2 = atol(parser->Tok[2])) > 0)
1345
+ {
1346
+ for (j = net->Njuncs + 1; j <= net->Nnodes; j++)
1347
+ {
1348
+ i = atol(net->Node[j].ID);
1349
+ if (i >= i1 && i <= i2) net->Tank[j - net->Njuncs].Kb = y;
1350
+ }
1351
+ }
1352
+
1353
+ // Case where a general range of tank IDs is specified
1354
+ else for (j = net->Njuncs + 1; j <= net->Nnodes; j++)
1355
+ {
1356
+ if ((strcmp(parser->Tok[1], net->Node[j].ID) <= 0) &&
1357
+ (strcmp(parser->Tok[2], net->Node[j].ID) >= 0)
1358
+ ) net->Tank[j - net->Njuncs].Kb = y;
1359
+ }
1360
+ }
1361
+
1362
+ // Case where pipe rate coeffs. are being set
1363
+ else
1364
+ {
1365
+ // Get the rate coeff. value
1366
+ if (!getfloat(parser->Tok[n - 1], &y)) return setError(parser, n-1, 202);
1367
+ if (net->Nlinks == 0) return 0;
1368
+
1369
+ // Case where just a single link is specified
1370
+ if (n == 3)
1371
+ {
1372
+ if ((j = findlink(net, parser->Tok[1])) == 0) return 0;
1373
+ if (item == 1) net->Link[j].Kb = y;
1374
+ else net->Link[j].Kw = y;
1375
+ }
1376
+
1377
+ // Case where a numerical range of link IDs is specified
1378
+ else if ((i1 = atol(parser->Tok[1])) > 0 &&
1379
+ (i2 = atol(parser->Tok[2])) > 0)
1380
+ {
1381
+ for (j = 1; j <= net->Nlinks; j++)
1382
+ {
1383
+ i = atol(net->Link[j].ID);
1384
+ if (i >= i1 && i <= i2)
1385
+ {
1386
+ if (item == 1) net->Link[j].Kb = y;
1387
+ else net->Link[j].Kw = y;
1388
+ }
1389
+ }
1390
+ }
1391
+
1392
+ // Case where a general range of link IDs is specified
1393
+ else for (j = 1; j <= net->Nlinks; j++)
1394
+ {
1395
+ if ((strcmp(parser->Tok[1], net->Link[j].ID) <= 0) &&
1396
+ (strcmp(parser->Tok[2], net->Link[j].ID) >= 0))
1397
+ {
1398
+ if (item == 1) net->Link[j].Kb = y;
1399
+ else net->Link[j].Kw = y;
1400
+ }
1401
+ }
1402
+ }
1403
+ return 0;
1404
+ }
1405
+
1406
+ int mixingdata(Project *pr)
1407
+ /*
1408
+ **-------------------------------------------------------------
1409
+ ** Input: none
1410
+ ** Output: returns error code
1411
+ ** Purpose: processes tank mixing data
1412
+ ** Format:
1413
+ ** [MIXING]
1414
+ ** TankID MixModel FractVolume
1415
+ **-------------------------------------------------------------
1416
+ */
1417
+ {
1418
+ Network *net = &pr->network;
1419
+ Parser *parser = &pr->parser;
1420
+
1421
+ int i, // Tank index
1422
+ j, // Node index
1423
+ m, // Type of mixing model
1424
+ n; // Number of data items
1425
+ double v; // Mixing zone volume fraction
1426
+
1427
+ // Check for valid data
1428
+ if (net->Nnodes == 0) return setError(parser, 0, 203);
1429
+ n = parser->Ntokens;
1430
+ if (n < 2) return 0;
1431
+ j = findnode(net, parser->Tok[0]);
1432
+ if (j == 0) return setError(parser, 0, 203);
1433
+ if (j <= net->Njuncs) return 0;
1434
+ if ((m = findmatch(parser->Tok[1], MixTxt)) < 0) return setError(parser, 1, 213);
1435
+
1436
+ // Find mixing zone volume fraction (which can't be 0)
1437
+ v = 1.0;
1438
+ if ((m == MIX2) && (n == 3) &&
1439
+ (!getfloat(parser->Tok[2], &v))) return setError(parser, 2, 202);
1440
+ if (v == 0.0) v = 1.0;
1441
+
1442
+ // Assign mixing data to tank (return if tank is a reservoir)
1443
+ i = j - net->Njuncs;
1444
+ if (net->Tank[i].A == 0.0) return 0;
1445
+ net->Tank[i].MixModel = (char)m;
1446
+ net->Tank[i].V1frac = v;
1447
+ return 0;
1448
+ }
1449
+
1450
+ int statusdata(Project *pr)
1451
+ /*
1452
+ **--------------------------------------------------------------
1453
+ ** Input: none
1454
+ ** Output: returns error code
1455
+ ** Purpose: processes link initial status data
1456
+ ** Formats:
1457
+ ** [STATUS]
1458
+ ** link value
1459
+ ** link1 (link2) value
1460
+ **--------------------------------------------------------------
1461
+ */
1462
+ {
1463
+ Network *net = &pr->network;
1464
+ Parser *parser = &pr->parser;
1465
+
1466
+ int j, n;
1467
+ long i, i1, i2;
1468
+ double y = 0.0;
1469
+ char status = ACTIVE;
1470
+
1471
+ if (net->Nlinks == 0) return setError(parser, 0, 204);
1472
+ n = parser->Ntokens - 1;
1473
+ if (n < 1) return 201;
1474
+
1475
+ // Check for legal status setting
1476
+ if (match(parser->Tok[n], w_OPEN)) status = OPEN;
1477
+ else if (match(parser->Tok[n], w_CLOSED)) status = CLOSED;
1478
+ else
1479
+ {
1480
+ if (!getfloat(parser->Tok[n], &y)) return setError(parser, n, 202);
1481
+ if (y < 0.0) return setError(parser, n, 211);
1482
+ }
1483
+
1484
+ // A single link ID was supplied
1485
+ if (n == 1)
1486
+ {
1487
+ if ((j = findlink(net, parser->Tok[0])) == 0) return setError(parser, 0, 204);
1488
+
1489
+ // Cannot change status of a Check Valve
1490
+ if (net->Link[j].Type == CVPIPE) return setError(parser, 0, 207);
1491
+
1492
+ // Cannot change setting for a GPV
1493
+ if (net->Link[j].Type == GPV && status == ACTIVE) return setError(parser, 0, 207);
1494
+ changestatus(net, j, status, y);
1495
+ }
1496
+
1497
+ // A range of numerical link ID's was supplied
1498
+ else if ((i1 = atol(parser->Tok[0])) > 0 &&
1499
+ (i2 = atol(parser->Tok[1])) > 0)
1500
+ {
1501
+ for (j = 1; j <= net->Nlinks; j++)
1502
+ {
1503
+ i = atol(net->Link[j].ID);
1504
+ if (i >= i1 && i <= i2) changestatus(net, j, status, y);
1505
+ }
1506
+ }
1507
+
1508
+ // A range of general link ID's was supplied
1509
+ else for (j = 1; j <= net->Nlinks; j++)
1510
+ {
1511
+ if ((strcmp(parser->Tok[0], net->Link[j].ID) <= 0) &&
1512
+ (strcmp(parser->Tok[1], net->Link[j].ID) >= 0)
1513
+ ) changestatus(net, j, status, y);
1514
+ }
1515
+ return 0;
1516
+ }
1517
+
1518
+ int energydata(Project *pr)
1519
+ /*
1520
+ **--------------------------------------------------------------
1521
+ ** Input: none
1522
+ ** Output: returns error code
1523
+ ** Purpose: processes pump energy data
1524
+ ** Formats:
1525
+ ** [ENERGY]
1526
+ ** GLOBAL {PRICE/PATTERN/EFFIC} value
1527
+ ** PUMP id {PRICE/PATTERN/EFFIC} value
1528
+ ** DEMAND CHARGE value
1529
+ **--------------------------------------------------------------
1530
+ */
1531
+ {
1532
+ Network *net = &pr->network;
1533
+ Hydraul *hyd = &pr->hydraul;
1534
+ Parser *parser = &pr->parser;
1535
+
1536
+ int j, k, n, p, c;
1537
+ double y;
1538
+
1539
+ Slink *Link = net->Link;
1540
+ Spump *Pump = net->Pump;
1541
+
1542
+ // Check for sufficient data
1543
+ n = parser->Ntokens;
1544
+ if (n < 3) return 201;
1545
+
1546
+ // First keyword is DEMAND
1547
+ if (match(parser->Tok[0], w_DMNDCHARGE))
1548
+ {
1549
+ if (!getfloat(parser->Tok[2], &y)) return setError(parser, 2, 202);
1550
+ if (y < 0.0) return setError(parser, 2, 213);
1551
+ hyd->Dcost = y;
1552
+ return 0;
1553
+ }
1554
+
1555
+ // First keyword is GLOBAL (remaining data refer to global options)
1556
+ if (match(parser->Tok[0], w_GLOBAL))
1557
+ {
1558
+ j = 0;
1559
+ }
1560
+
1561
+ // First keyword is PUMP (remaining data refer to a specific pump)
1562
+ else if (match(parser->Tok[0], w_PUMP))
1563
+ {
1564
+ if (n < 4) return 201;
1565
+ k = findlink(net,parser->Tok[1]);
1566
+ if (k == 0) return setError(parser, 1, 216);
1567
+ if (Link[k].Type != PUMP) return setError(parser, 1, 216);
1568
+ j = findpump(net, k);
1569
+ }
1570
+ else return setError(parser, 0, 213);
1571
+
1572
+ // PRICE parameter being set
1573
+ if (match(parser->Tok[n - 2], w_PRICE))
1574
+ {
1575
+ if (!getfloat(parser->Tok[n - 1], &y)) return setError(parser, n-1, 202);
1576
+ if (y < 0.0) return setError(parser, n-1, 217);
1577
+ if (j == 0) hyd->Ecost = y;
1578
+ else Pump[j].Ecost = y;
1579
+ return 0;
1580
+ }
1581
+
1582
+ // Price PATTERN being set
1583
+ else if (match(parser->Tok[n - 2], w_PATTERN))
1584
+ {
1585
+ p = findpattern(net, parser->Tok[n - 1]);
1586
+ if (p < 0) return setError(parser, n - 1, 205);
1587
+ if (j == 0) hyd->Epat = p;
1588
+ else Pump[j].Epat = p;
1589
+ return 0;
1590
+ }
1591
+
1592
+ // Pump EFFIC being set
1593
+ else if (match(parser->Tok[n - 2], w_EFFIC))
1594
+ {
1595
+ if (j == 0)
1596
+ {
1597
+ if (!getfloat(parser->Tok[n - 1], &y)) return setError(parser, n - 1, 202);
1598
+ if (y <= 0.0) return setError(parser, n - 1, 217);
1599
+ hyd->Epump = y;
1600
+ }
1601
+ else
1602
+ {
1603
+ c = findcurve(net, parser->Tok[n - 1]);
1604
+ if (c == 0) return setError(parser, n - 1, 206);
1605
+ Pump[j].Ecurve = c;
1606
+ net->Curve[c].Type = EFFIC_CURVE;
1607
+ }
1608
+ return 0;
1609
+ }
1610
+ return 201;
1611
+ }
1612
+
1613
+ int reportdata(Project *pr)
1614
+ /*
1615
+ **--------------------------------------------------------------
1616
+ ** Input: none
1617
+ ** Output: returns error code
1618
+ ** Purpose: processes report options data
1619
+ ** Formats:
1620
+ ** PAGE linesperpage
1621
+ ** STATUS {NONE/YES/FULL}
1622
+ ** SUMMARY {YES/NO}
1623
+ ** MESSAGES {YES/NO}
1624
+ ** ENERGY {NO/YES}
1625
+ ** NODES {NONE/ALL}
1626
+ ** NODES node1 node2 ...
1627
+ ** LINKS {NONE/ALL}
1628
+ ** LINKS link1 link2 ...
1629
+ ** FILE filename
1630
+ ** variable {YES/NO}
1631
+ ** variable {BELOW/ABOVE/PRECISION} value
1632
+ **--------------------------------------------------------------
1633
+ */
1634
+ {
1635
+ Network *net = &pr->network;
1636
+ Report *rpt = &pr->report;
1637
+ Parser *parser = &pr->parser;
1638
+
1639
+ int i, j, n;
1640
+ double y;
1641
+
1642
+ n = parser->Ntokens - 1;
1643
+ if (n < 1) return 201;
1644
+
1645
+ // Value for page size
1646
+ if (match(parser->Tok[0], w_PAGE))
1647
+ {
1648
+ if (!getfloat(parser->Tok[n], &y)) return setError(parser, n, 202);
1649
+ if (y < 0.0 || y > 255.0) return setError(parser, n, 213);
1650
+ rpt->PageSize = (int)y;
1651
+ return 0;
1652
+ }
1653
+
1654
+ // Request that status reports be written
1655
+ if (match(parser->Tok[0], w_STATUS))
1656
+ {
1657
+ if (match(parser->Tok[n], w_NO)) rpt->Statflag = FALSE;
1658
+ if (match(parser->Tok[n], w_YES)) rpt->Statflag = TRUE;
1659
+ if (match(parser->Tok[n], w_FULL)) rpt->Statflag = FULL;
1660
+ return 0;
1661
+ }
1662
+
1663
+ // Request summary report
1664
+ if (match(parser->Tok[0], w_SUMMARY))
1665
+ {
1666
+ if (match(parser->Tok[n], w_NO)) rpt->Summaryflag = FALSE;
1667
+ if (match(parser->Tok[n], w_YES)) rpt->Summaryflag = TRUE;
1668
+ return 0;
1669
+ }
1670
+
1671
+ // Request error/warning message reporting
1672
+ if (match(parser->Tok[0], w_MESSAGES))
1673
+ {
1674
+ if (match(parser->Tok[n], w_NO)) rpt->Messageflag = FALSE;
1675
+ if (match(parser->Tok[n], w_YES)) rpt->Messageflag = TRUE;
1676
+ return 0;
1677
+ }
1678
+
1679
+ // Request an energy usage report
1680
+ if (match(parser->Tok[0], w_ENERGY))
1681
+ {
1682
+ if (match(parser->Tok[n], w_NO)) rpt->Energyflag = FALSE;
1683
+ if (match(parser->Tok[n], w_YES)) rpt->Energyflag = TRUE;
1684
+ return 0;
1685
+ }
1686
+
1687
+ // Particular reporting nodes specified
1688
+ if (match(parser->Tok[0], w_NODE))
1689
+ {
1690
+ if (match(parser->Tok[n], w_NONE)) rpt->Nodeflag = 0; // No nodes
1691
+ else if (match(parser->Tok[n], w_ALL)) rpt->Nodeflag = 1; // All nodes
1692
+ else
1693
+ {
1694
+ if (net->Nnodes == 0) return setError(parser, 1, 203);
1695
+ for (i = 1; i <= n; i++)
1696
+ {
1697
+ if ((j = findnode(net, parser->Tok[i])) == 0) return setError(parser, i, 203);
1698
+ net->Node[j].Rpt = 1;
1699
+ }
1700
+ rpt->Nodeflag = 2;
1701
+ }
1702
+ return 0;
1703
+ }
1704
+
1705
+ // Particular reporting links specified
1706
+ if (match(parser->Tok[0], w_LINK))
1707
+ {
1708
+ if (match(parser->Tok[n], w_NONE)) rpt->Linkflag = 0;
1709
+ else if (match(parser->Tok[n], w_ALL)) rpt->Linkflag = 1;
1710
+ else
1711
+ {
1712
+ if (net->Nlinks == 0) return setError(parser, 1, 204);
1713
+ for (i = 1; i <= n; i++)
1714
+ {
1715
+ if ((j = findlink(net, parser->Tok[i])) == 0) return setError(parser, i, 204);
1716
+ net->Link[j].Rpt = 1;
1717
+ }
1718
+ rpt->Linkflag = 2;
1719
+ }
1720
+ return 0;
1721
+ }
1722
+
1723
+ // Report fields specified
1724
+ // Special case needed to distinguish "HEAD" from "HEADLOSS"
1725
+ if (strcomp(parser->Tok[0], t_HEADLOSS)) i = HEADLOSS;
1726
+ else i = findmatch(parser->Tok[0], Fldname);
1727
+ if (i >= 0)
1728
+ {
1729
+ if (i > FRICTION) return setError(parser, 0, 213);
1730
+ if (parser->Ntokens == 1 || match(parser->Tok[1], w_YES))
1731
+ {
1732
+ rpt->Field[i].Enabled = TRUE;
1733
+ return 0;
1734
+ }
1735
+
1736
+ if (match(parser->Tok[1], w_NO))
1737
+ {
1738
+ rpt->Field[i].Enabled = FALSE;
1739
+ return 0;
1740
+ }
1741
+
1742
+ // Get field qualifier type
1743
+ if (parser->Ntokens < 3) return 201;
1744
+ if (match(parser->Tok[1], w_BELOW)) j = LOW;
1745
+ else if (match(parser->Tok[1], w_ABOVE)) j = HI;
1746
+ else if (match(parser->Tok[1], w_PRECISION)) j = PREC;
1747
+ else return setError(parser, 1, 213);
1748
+
1749
+ // Get field qualifier value
1750
+ if (!getfloat(parser->Tok[2], &y)) return setError(parser, 2, 202);
1751
+ if (j == PREC)
1752
+ {
1753
+ rpt->Field[i].Enabled = TRUE;
1754
+ rpt->Field[i].Precision = ROUND(y);
1755
+ }
1756
+ else rpt->Field[i].RptLim[j] = y;
1757
+ return (0);
1758
+ }
1759
+
1760
+ // Name of external report file
1761
+ if (match(parser->Tok[0], w_FILE))
1762
+ {
1763
+ strncpy(rpt->Rpt2Fname, parser->Tok[1], MAXFNAME);
1764
+ return 0;
1765
+ }
1766
+
1767
+ // If get to here then return error condition
1768
+ return 201;
1769
+ }
1770
+
1771
+ int timedata(Project *pr)
1772
+ /*
1773
+ **--------------------------------------------------------------
1774
+ ** Input: none
1775
+ ** Output: returns error code
1776
+ ** Purpose: processes time options data
1777
+ ** Formats:
1778
+ ** STATISTIC {NONE/AVERAGE/MIN/MAX/RANGE}
1779
+ ** DURATION value (units)
1780
+ ** HYDRAULIC TIMESTEP value (units)
1781
+ ** QUALITY TIMESTEP value (units)
1782
+ ** MINIMUM TRAVELTIME value (units)
1783
+ ** RULE TIMESTEP value (units)
1784
+ ** PATTERN TIMESTEP value (units)
1785
+ ** PATTERN START value (units)
1786
+ ** REPORT TIMESTEP value (units)
1787
+ ** REPORT START value (units)
1788
+ ** START CLOCKTIME value (AM PM)
1789
+ **-------------------------------------------------------------
1790
+ */
1791
+ {
1792
+ Report *rpt = &pr->report;
1793
+ Parser *parser = &pr->parser;
1794
+ Times *time = &pr->times;
1795
+
1796
+ int n;
1797
+ long t;
1798
+ double y;
1799
+
1800
+ n = parser->Ntokens - 1;
1801
+ if (n < 1) return 201;
1802
+
1803
+ // Check if setting report time statistic flag
1804
+ if (match(parser->Tok[0], w_STATISTIC))
1805
+ {
1806
+ if (match(parser->Tok[n], w_NONE)) rpt->Tstatflag = SERIES;
1807
+ else if (match(parser->Tok[n], w_NO)) rpt->Tstatflag = SERIES;
1808
+ else if (match(parser->Tok[n], w_AVG)) rpt->Tstatflag = AVG;
1809
+ else if (match(parser->Tok[n], w_MIN)) rpt->Tstatflag = MIN;
1810
+ else if (match(parser->Tok[n], w_MAX)) rpt->Tstatflag = MAX;
1811
+ else if (match(parser->Tok[n], w_RANGE)) rpt->Tstatflag = RANGE;
1812
+ else return setError(parser, n, 213);
1813
+ return 0;
1814
+ }
1815
+
1816
+ // Convert text time value to numerical value in seconds
1817
+ // Examples:
1818
+ // 5 = 5 * 3600 sec
1819
+ // 5 MINUTES = 5 * 60 sec
1820
+ // 13:50 = 13*3600 + 50*60 sec
1821
+ // 1:50 pm = (12+1)*3600 + 50*60 sec
1822
+
1823
+ if (!getfloat(parser->Tok[n], &y))
1824
+ {
1825
+ if ((y = hour(parser->Tok[n], "")) < 0.0)
1826
+ {
1827
+ if ((y = hour(parser->Tok[n - 1], parser->Tok[n])) < 0.0)
1828
+ {
1829
+ return setError(parser, n-1, 213);
1830
+ }
1831
+ }
1832
+ }
1833
+ t = (long)(3600.0 * y + 0.5);
1834
+
1835
+ /// Process the value assigned to the matched parameter
1836
+ if (match(parser->Tok[0], w_DURATION)) time->Dur = t;
1837
+ else if (match(parser->Tok[0], w_HYDRAULIC)) time->Hstep = t;
1838
+ else if (match(parser->Tok[0], w_QUALITY) ) time->Qstep = t;
1839
+ else if (match(parser->Tok[0], w_RULE)) time->Rulestep = t;
1840
+ else if (match(parser->Tok[0], w_MINIMUM)) return 0; // Not used anymore
1841
+ else if (match(parser->Tok[0], w_PATTERN))
1842
+ {
1843
+ if (match(parser->Tok[1], w_TIME)) time->Pstep = t;
1844
+ else if (match(parser->Tok[1], w_START)) time->Pstart = t;
1845
+ else return setError(parser, 1, 213);
1846
+ }
1847
+ else if (match(parser->Tok[0], w_REPORT))
1848
+ {
1849
+ if (match(parser->Tok[1], w_TIME)) time->Rstep = t;
1850
+ else if (match(parser->Tok[1], w_START)) time->Rstart = t;
1851
+ else return setError(parser, 1, 213);
1852
+ }
1853
+ else if (match(parser->Tok[0], w_START)) time->Tstart = t % SECperDAY;
1854
+ else return setError(parser, 0, 213);
1855
+ return 0;
1856
+ }
1857
+
1858
+ int optiondata(Project *pr)
1859
+ /*
1860
+ **--------------------------------------------------------------
1861
+ ** Input: none
1862
+ ** Output: returns error code
1863
+ ** Purpose: processes [OPTIONS] data
1864
+ **--------------------------------------------------------------
1865
+ */
1866
+ {
1867
+ int i, n;
1868
+ Parser *parser = &pr->parser;
1869
+
1870
+ // Option is a named choice
1871
+ n = parser->Ntokens - 1;
1872
+ i = optionchoice(pr, n);
1873
+ if (i >= 0) return i;
1874
+
1875
+ // Option is a numerical value
1876
+ return (optionvalue(pr, n));
1877
+ }
1878
+
1879
+ int optionchoice(Project *pr, int n)
1880
+ /*
1881
+ **--------------------------------------------------------------
1882
+ ** Input: n = index of last input token
1883
+ ** Output: returns error code or 0 if option belongs to
1884
+ ** those listed below, or -1 otherwise
1885
+ ** Purpose: processes fixed choice [OPTIONS] data
1886
+ ** Formats:
1887
+ ** UNITS CFS/GPM/MGD/IMGD/AFD/LPS/LPM/MLD/CMH/CMD/CMS
1888
+ ** PRESSURE PSI/KPA/METERS/BAR/FEET
1889
+ ** HEADLOSS H-W/D-W/C-M
1890
+ ** HYDRAULICS USE/SAVE filename
1891
+ ** QUALITY NONE/AGE/TRACE/CHEMICAL (TraceNode)
1892
+ ** MAP filename
1893
+ ** VERIFY filename
1894
+ ** UNBALANCED STOP/CONTINUE {Niter}
1895
+ ** PATTERN id
1896
+ ** DEMAND MODEL DDA/PDA
1897
+ ** BACKFLOW ALLOWED YES/NO
1898
+ **--------------------------------------------------------------
1899
+ */
1900
+ {
1901
+ Network *net = &pr->network;
1902
+ Hydraul *hyd = &pr->hydraul;
1903
+ Quality *qual = &pr->quality;
1904
+ Parser *parser = &pr->parser;
1905
+ Outfile *out = &pr->outfile;
1906
+
1907
+ int choice;
1908
+
1909
+ // Check if 1st token matches a parameter name and
1910
+ // process the input for the matched parameter
1911
+ if (n < 0) return 201;
1912
+
1913
+ // Flow UNITS
1914
+ if (match(parser->Tok[0], w_UNITS))
1915
+ {
1916
+ if (n < 1) return 0;
1917
+ if (!getunitsoption(pr, parser->Tok[1]))
1918
+ return setError(parser, 1, 213);
1919
+ }
1920
+
1921
+ // PRESSURE units
1922
+ else if (match(parser->Tok[0], w_PRESSURE))
1923
+ {
1924
+ if (n < 1) return 0;
1925
+ else if (match(parser->Tok[1], w_EXPONENT)) return -1;
1926
+ else if (match(parser->Tok[1], w_PSI)) parser->Pressflag = PSI;
1927
+ else if (match(parser->Tok[1], w_KPA)) parser->Pressflag = KPA;
1928
+ else if (match(parser->Tok[1], w_METERS)) parser->Pressflag = METERS;
1929
+ else if (match(parser->Tok[1], w_BAR)) parser->Pressflag = BAR;
1930
+ else if (match(parser->Tok[1], w_FEET)) parser->Pressflag = FEET;
1931
+ else return setError(parser, 1, 213);
1932
+ }
1933
+
1934
+ // HEADLOSS formula
1935
+ else if (match(parser->Tok[0], w_HEADLOSS))
1936
+ {
1937
+ if (n < 1) return 0;
1938
+ if (!getheadlossoption(pr, parser->Tok[1]))
1939
+ return setError(parser, 1, 213);
1940
+ }
1941
+
1942
+ // HYDRUALICS USE/SAVE file option
1943
+ else if (match(parser->Tok[0], w_HYDRAULIC))
1944
+ {
1945
+ if (n < 2) return 0;
1946
+ else if (match(parser->Tok[1], w_USE)) out->Hydflag = USE;
1947
+ else if (match(parser->Tok[1], w_SAVE)) out->Hydflag = SAVE;
1948
+ else return setError(parser, 1, 213);
1949
+ strncpy(out->HydFname, parser->Tok[2], MAXFNAME);
1950
+ }
1951
+
1952
+ // Water QUALITY option
1953
+ else if (match(parser->Tok[0], w_QUALITY))
1954
+ {
1955
+ if (n < 1) return 0;
1956
+ else if (match(parser->Tok[1], w_NONE)) qual->Qualflag = NONE;
1957
+ else if (match(parser->Tok[1], w_CHEM)) qual->Qualflag = CHEM;
1958
+ else if (match(parser->Tok[1], w_AGE)) qual->Qualflag = AGE;
1959
+ else if (match(parser->Tok[1], w_TRACE)) qual->Qualflag = TRACE;
1960
+ else
1961
+ {
1962
+ qual->Qualflag = CHEM;
1963
+ strncpy(qual->ChemName, parser->Tok[1], MAXID);
1964
+ if (n >= 2) strncpy(qual->ChemUnits, parser->Tok[2], MAXID);
1965
+ }
1966
+ if (qual->Qualflag == TRACE)
1967
+ {
1968
+ if (n < 2) return 201;
1969
+ qual->TraceNode = findnode(net, parser->Tok[2]);
1970
+ if (qual->TraceNode == 0) return setError(parser, 2, 212);
1971
+ strncpy(qual->ChemName, u_PERCENT, MAXID);
1972
+ strncpy(qual->ChemUnits, parser->Tok[2], MAXID);
1973
+ }
1974
+ if (qual->Qualflag == AGE)
1975
+ {
1976
+ strncpy(qual->ChemName, w_AGE, MAXID);
1977
+ strncpy(qual->ChemUnits, u_HOURS, MAXID);
1978
+ }
1979
+ }
1980
+
1981
+ // MAP file name
1982
+ else if (match(parser->Tok[0], w_MAP))
1983
+ {
1984
+ if (n < 1) return 0;
1985
+ strncpy(pr->MapFname, parser->Tok[1], MAXFNAME);
1986
+ }
1987
+
1988
+ else if (match(parser->Tok[0], w_VERIFY))
1989
+ {
1990
+ // Deprecated
1991
+ }
1992
+
1993
+ // Hydraulics UNBALANCED option
1994
+ else if (match(parser->Tok[0], w_UNBALANCED))
1995
+ {
1996
+ if (n < 1) return 0;
1997
+ if (match(parser->Tok[1], w_STOP)) hyd->ExtraIter = -1;
1998
+ else if (match(parser->Tok[1], w_CONTINUE))
1999
+ {
2000
+ if (n >= 2) hyd->ExtraIter = atoi(parser->Tok[2]);
2001
+ else hyd->ExtraIter = 0;
2002
+ }
2003
+ else return setError(parser, 1, 213);
2004
+ }
2005
+
2006
+ // Default demand PATTERN
2007
+ else if (match(parser->Tok[0], w_PATTERN))
2008
+ {
2009
+ if (n < 1) return 0;
2010
+ strncpy(parser->DefPatID, parser->Tok[1], MAXID);
2011
+ }
2012
+
2013
+ // DEMAND model
2014
+ else if (match(parser->Tok[0], w_DEMAND))
2015
+ {
2016
+ if (n < 2) return 0;
2017
+ if (!match(parser->Tok[1], w_MODEL)) return -1;
2018
+ choice = findmatch(parser->Tok[2], DemandModelTxt);
2019
+ if (choice < 0) return setError(parser, 2, 213);
2020
+ hyd->DemandModel = choice;
2021
+ }
2022
+
2023
+ // Emitter BACKFLOW ALLOWED
2024
+ else if (match(parser->Tok[0], w_BACKFLOW))
2025
+ {
2026
+ if (n < 2) return 0;
2027
+ if (!match(parser->Tok[1], w_ALLOWED)) return -1;
2028
+ choice = findmatch(parser->Tok[2], BackflowTxt);
2029
+ if (choice < 0) return setError(parser, 2, 213);
2030
+ hyd->EmitBackFlag = choice;
2031
+ }
2032
+
2033
+ // Return -1 if keyword did not match any option
2034
+ else return -1;
2035
+ return 0;
2036
+ }
2037
+
2038
+ int optionvalue(Project *pr, int n)
2039
+ /*
2040
+ **-------------------------------------------------------------
2041
+ ** Input: n = index of last input token
2042
+ ** Output: returns error code
2043
+ ** Purpose: processes numerical value [OPTIONS] data
2044
+ ** Formats:
2045
+ ** DEMAND MULTIPLIER value
2046
+ ** EMITTER EXPONENT value
2047
+ ** VISCOSITY value
2048
+ ** DIFFUSIVITY value
2049
+ ** SPECIFIC GRAVITY value
2050
+ ** TRIALS value
2051
+ ** ACCURACY value
2052
+
2053
+ ** HEADERROR value
2054
+ ** FLOWCHANGE value
2055
+ ** MINIMUM PRESSURE value
2056
+ ** REQUIRED PRESSURE value
2057
+ ** PRESSURE EXPONENT value
2058
+
2059
+ ** TOLERANCE value
2060
+ ** SEGMENTS value (not used)
2061
+ ** ------ Undocumented Options -----
2062
+ ** HTOL value
2063
+ ** QTOL value
2064
+ ** RQTOL value
2065
+ ** CHECKFREQ value
2066
+ ** MAXCHECK value
2067
+ ** DAMPLIMIT value
2068
+ **--------------------------------------------------------------
2069
+ */
2070
+ {
2071
+ Hydraul *hyd = &pr->hydraul;
2072
+ Quality *qual = &pr->quality;
2073
+ Parser *parser = &pr->parser;
2074
+
2075
+ int nvalue = 1; // Index of token with numerical value
2076
+ double y;
2077
+ char* tok0 = parser->Tok[0];
2078
+
2079
+ // Check for deprecated SEGMENTS keyword
2080
+ if (match(tok0, w_SEGMENTS)) return 0;
2081
+
2082
+ // Check for missing value (which is permissible)
2083
+ if (match(tok0, w_SPECGRAV) || match(tok0, w_EMITTER) ||
2084
+ match(tok0, w_DEMAND) || match(tok0, w_MINIMUM) ||
2085
+ match(tok0, w_REQUIRED) || match(tok0, w_PRESSURE) ||
2086
+ match(tok0, w_PRECISION)
2087
+ ) nvalue = 2;
2088
+ if (n < nvalue) return 0;
2089
+
2090
+ // Check for valid numerical input
2091
+ if (!getfloat(parser->Tok[nvalue], &y)) return setError(parser, nvalue, 202);
2092
+
2093
+ // Quality tolerance option (which can be 0)
2094
+ if (match(tok0, w_TOLERANCE))
2095
+ {
2096
+ if (y < 0.0) return setError(parser, nvalue, 213);
2097
+ qual->Ctol = y;
2098
+ return 0;
2099
+ }
2100
+
2101
+ // Diffusivity
2102
+ if (match(tok0, w_DIFFUSIVITY))
2103
+ {
2104
+ if (y < 0.0) return setError(parser, nvalue, 213);
2105
+ qual->Diffus = y;
2106
+ return 0;
2107
+ }
2108
+
2109
+ // Hydraulic damping limit option */
2110
+ if (match(tok0, w_DAMPLIMIT))
2111
+ {
2112
+ hyd->DampLimit = y;
2113
+ return 0;
2114
+ }
2115
+
2116
+ // Flow change limit
2117
+ else if (match(tok0, w_FLOWCHANGE))
2118
+ {
2119
+ if (y < 0.0) return setError(parser, nvalue, 213);
2120
+ hyd->FlowChangeLimit = y;
2121
+ return 0;
2122
+ }
2123
+
2124
+ // Head loss error limit
2125
+ else if (match(tok0, w_HEADERROR))
2126
+ {
2127
+ if (y < 0.0) return setError(parser, nvalue, 213);
2128
+ hyd->HeadErrorLimit = y;
2129
+ return 0;
2130
+ }
2131
+
2132
+ // Pressure dependent demand parameters
2133
+ else if (match(tok0, w_MINIMUM))
2134
+ {
2135
+ if (y < 0.0) return setError(parser, nvalue, 213);
2136
+ // Required pressure still at default value
2137
+ if (hyd->Preq == MINPDIFF)
2138
+ hyd->Preq = y + MINPDIFF;
2139
+ // Required pressure already entered
2140
+ else if (hyd->Preq - y < MINPDIFF)
2141
+ return setError(parser, nvalue, 208);
2142
+ hyd->Pmin = y;
2143
+ return 0;
2144
+ }
2145
+ else if (match(tok0, w_REQUIRED))
2146
+ {
2147
+ if (y < 0.0) return setError(parser, nvalue, 213);
2148
+ if (y - hyd->Pmin < MINPDIFF)
2149
+ return setError(parser, nvalue, 208);
2150
+ hyd->Preq = y;
2151
+ return 0;
2152
+ }
2153
+ else if (match(tok0, w_PRESSURE))
2154
+ {
2155
+ if (y < 0.0) return setError(parser, nvalue, 213);
2156
+ hyd->Pexp = y;
2157
+ return 0;
2158
+ }
2159
+
2160
+ // All other options must be > 0
2161
+ if (y <= 0.0) return setError(parser, nvalue, 213);
2162
+
2163
+ // Assign value to all other options
2164
+ if (match(tok0, w_VISCOSITY)) hyd->Viscos = y;
2165
+ else if (match(tok0, w_SPECGRAV)) hyd->SpGrav = y;
2166
+ else if (match(tok0, w_TRIALS)) hyd->MaxIter = (int)y;
2167
+ else if (match(tok0, w_ACCURACY))
2168
+ {
2169
+ y = MAX(y, 1.e-5);
2170
+ y = MIN(y, 1.e-1);
2171
+ hyd->Hacc = y;
2172
+ }
2173
+ else if (match(tok0, w_HTOL)) hyd->Htol = y;
2174
+ else if (match(tok0, w_QTOL)) hyd->Qtol = y;
2175
+ else if (match(tok0, w_RQTOL))
2176
+ {
2177
+ if (y >= 1.0) return 213;
2178
+ hyd->RQtol = y;
2179
+ }
2180
+ else if (match(tok0, w_CHECKFREQ)) hyd->CheckFreq = (int)y;
2181
+ else if (match(tok0, w_MAXCHECK)) hyd->MaxCheck = (int)y;
2182
+ else if (match(tok0, w_EMITTER)) hyd->Qexp = 1.0 / y;
2183
+ else if (match(tok0, w_DEMAND)) hyd->Dmult = y;
2184
+ else return 201;
2185
+ return 0;
2186
+ }
2187
+
2188
+ int tagdata(Project *pr)
2189
+ /*
2190
+ **-------------------------------------------------------------
2191
+ ** Input: none
2192
+ ** Output: returns error code
2193
+ ** Purpose: processes [TAGS] data
2194
+ ** Formats:
2195
+ ** NODE id tag
2196
+ ** LINK id tag
2197
+ **--------------------------------------------------------------
2198
+ */
2199
+ {
2200
+ Network *net = &pr->network;
2201
+ Parser *parser = &pr->parser;
2202
+
2203
+ int j, n;
2204
+
2205
+ // Check for sufficient data
2206
+ n = parser->Ntokens;
2207
+ if (n < 3) return 201;
2208
+
2209
+ // First keyword is NODE
2210
+ if (match(parser->Tok[0], w_NODE))
2211
+ {
2212
+ if ((j = findnode(net, parser->Tok[1])) == 0) return setError(parser, 0, 203);
2213
+ xstrcpy(&net->Node[j].Tag, parser->Tok[2], MAXMSG);
2214
+ }
2215
+
2216
+ // First keyword is LINK
2217
+ else if (match(parser->Tok[0], w_LINK))
2218
+ {
2219
+ if ((j = findlink(net, parser->Tok[1])) == 0) return setError(parser, 0, 203);
2220
+ xstrcpy(&net->Link[j].Tag, parser->Tok[2], MAXMSG);
2221
+ }
2222
+ return 0;
2223
+ }
2224
+
2225
+ void changestatus(Network *net, int j, StatusType status, double y)
2226
+ /*
2227
+ **--------------------------------------------------------------
2228
+ ** Input: j = link index
2229
+ ** status = status setting (OPEN, CLOSED)
2230
+ ** y = numerical setting (pump speed, valve
2231
+ ** setting)
2232
+ ** Output: none
2233
+ ** Purpose: changes initial status or setting of a link
2234
+ **
2235
+ ** NOTE: If status = ACTIVE, then a numerical setting (y) was
2236
+ ** supplied.
2237
+ **--------------------------------------------------------------
2238
+ */
2239
+ {
2240
+ Slink *link = &net->Link[j];
2241
+
2242
+ if (link->Type == PIPE || link->Type == GPV)
2243
+ {
2244
+ if (status != ACTIVE) link->InitStatus = status;
2245
+ }
2246
+ else if (link->Type == PUMP)
2247
+ {
2248
+ if (status == ACTIVE)
2249
+ {
2250
+ link->Kc = y;
2251
+ status = OPEN;
2252
+ if (y == 0.0) status = CLOSED;
2253
+ }
2254
+ else if (status == OPEN) link->Kc = 1.0;
2255
+ else if (status == CLOSED) link->Kc = 0.0;
2256
+ link->InitStatus = status;
2257
+ link->InitSetting = link->Kc;
2258
+ }
2259
+ else if (link->Type >= PRV)
2260
+ {
2261
+ if (status == ACTIVE) link->Kc = y;
2262
+ link->InitStatus = status;
2263
+ link->InitSetting = link->Kc;
2264
+ }
2265
+ }