epanet-plus 0.1.0__cp314-cp314-macosx_10_15_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 (106) 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-314-darwin.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 +2429 -0
  86. epanet_plus/include/epanet_plus.h +9 -0
  87. epanet_plus-0.1.0.dist-info/METADATA +153 -0
  88. epanet_plus-0.1.0.dist-info/RECORD +106 -0
  89. epanet_plus-0.1.0.dist-info/WHEEL +6 -0
  90. epanet_plus-0.1.0.dist-info/licenses/LICENSE +21 -0
  91. epanet_plus-0.1.0.dist-info/top_level.txt +11 -0
  92. examples/basic_usage.py +35 -0
  93. examples/epanet_msx.py +35 -0
  94. python-extension/ext.c +344 -0
  95. python-extension/pyepanet.c +2150 -0
  96. python-extension/pyepanet.h +144 -0
  97. python-extension/pyepanet2.c +1835 -0
  98. python-extension/pyepanet2.h +142 -0
  99. python-extension/pyepanet_plus.c +37 -0
  100. python-extension/pyepanet_plus.h +4 -0
  101. python-extension/pyepanetmsx.c +388 -0
  102. python-extension/pyepanetmsx.h +35 -0
  103. tests/test_epanet.py +16 -0
  104. tests/test_epanetmsx.py +36 -0
  105. tests/test_epyt.py +114 -0
  106. tests/test_load_inp_from_buffer.py +18 -0
epanet-src/input2.c ADDED
@@ -0,0 +1,970 @@
1
+ /*
2
+ ******************************************************************************
3
+ Project: OWA EPANET
4
+ Version: 2.3
5
+ Module: input2.c
6
+ Description: reads and interprets network data from an EPANET input file
7
+ Authors: see AUTHORS
8
+ Copyright: see AUTHORS
9
+ License: see LICENSE
10
+ Last Updated: 02/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
+ extern char *SectTxt[]; // Input section keywords (see ENUMSTXT.H)
25
+
26
+ // Exported functions
27
+ int addnodeID(Network *, int, char *);
28
+ int addlinkID(Network *, int, char *);
29
+ int getunitsoption(Project *, char *);
30
+ int getheadlossoption(Project *, char *);
31
+
32
+ // Local functions
33
+ static int newline(Project *, int, char *);
34
+ static int addpattern(Network *, char *);
35
+ static int addcurve(Network *, char *);
36
+ static void inperrmsg(Project *, int, int, char *);
37
+
38
+
39
+ int netsize(Project *pr)
40
+ /*
41
+ **--------------------------------------------------------------
42
+ ** Input: none
43
+ ** Output: returns error code
44
+ ** Purpose: determines number of network objects
45
+ **--------------------------------------------------------------
46
+ */
47
+ {
48
+ Parser *parser = &pr->parser;
49
+
50
+ char line[MAXLINE + 1]; // Line from input data file
51
+ char *tok; // First token of line
52
+ int sect, newsect; // Input data sections
53
+ int errcode = 0; // Error code
54
+ Spattern *pattern;
55
+
56
+ // Initialize object counts
57
+ parser->MaxJuncs = 0;
58
+ parser->MaxTanks = 0;
59
+ parser->MaxPipes = 0;
60
+ parser->MaxPumps = 0;
61
+ parser->MaxValves = 0;
62
+ parser->MaxControls = 0;
63
+ parser->MaxRules = 0;
64
+ parser->MaxCurves = 0;
65
+ sect = -1;
66
+
67
+
68
+ // Add a "dummy" time pattern with index of 0 and a single multiplier
69
+ // of 1.0 to be used by all demands not assigned a pattern
70
+ pr->network.Npats = -1;
71
+ errcode = addpattern(&pr->network, "");
72
+ if (errcode) return errcode;
73
+ pattern = &pr->network.Pattern[0];
74
+ pattern->Length = 1;
75
+ pattern[0].F = (double *)calloc(1, sizeof(double));
76
+ pattern[0].F[0] = 1.0;
77
+ parser->MaxPats = pr->network.Npats;
78
+
79
+ // Make a pass through input file counting number of each object
80
+ if (parser->InFile == NULL) return 0;
81
+ while (fgets(line, MAXLINE, parser->InFile) != NULL)
82
+ {
83
+ // Skip blank lines & those beginning with a comment
84
+ tok = strtok(line, SEPSTR);
85
+ if (tok == NULL) continue;
86
+ if (*tok == ';') continue;
87
+
88
+ // Check if line begins with a new section heading
89
+ if (tok[0] == '[')
90
+ {
91
+ newsect = findmatch(tok, SectTxt);
92
+ if (newsect >= 0)
93
+ {
94
+ sect = newsect;
95
+ if (sect == _END) break;
96
+ continue;
97
+ }
98
+ else
99
+ {
100
+ sect = -1;
101
+ continue;
102
+ }
103
+ }
104
+
105
+ // Add to count of current object
106
+ switch (sect)
107
+ {
108
+ case _JUNCTIONS: parser->MaxJuncs++; break;
109
+ case _RESERVOIRS:
110
+ case _TANKS: parser->MaxTanks++; break;
111
+ case _PIPES: parser->MaxPipes++; break;
112
+ case _PUMPS: parser->MaxPumps++; break;
113
+ case _VALVES: parser->MaxValves++; break;
114
+ case _CONTROLS: parser->MaxControls++; break;
115
+ case _RULES: addrule(parser,tok); break;
116
+ case _PATTERNS:
117
+ errcode = addpattern(&pr->network, tok);
118
+ parser->MaxPats = pr->network.Npats;
119
+ break;
120
+ case _CURVES:
121
+ errcode = addcurve(&pr->network, tok);
122
+ parser->MaxCurves = pr->network.Ncurves;
123
+ break;
124
+ case _OPTIONS:
125
+ if (match(tok, w_UNITS))
126
+ getunitsoption(pr, strtok(line, SEPSTR));
127
+ else if (match(tok, w_HEADLOSS))
128
+ getheadlossoption(pr, strtok(line, SEPSTR));
129
+ break;
130
+ }
131
+ if (errcode) break;
132
+ }
133
+
134
+ parser->MaxNodes = parser->MaxJuncs + parser->MaxTanks;
135
+ parser->MaxLinks = parser->MaxPipes + parser->MaxPumps + parser->MaxValves;
136
+ if (parser->MaxPats < 1) parser->MaxPats = 1;
137
+ return errcode;
138
+ }
139
+
140
+ int readdata(Project *pr)
141
+ /*
142
+ **--------------------------------------------------------------
143
+ ** Input: none
144
+ ** Output: returns error code
145
+ ** Purpose: reads contents of input data file
146
+ **--------------------------------------------------------------
147
+ */
148
+ {
149
+ Network *net = &pr->network;
150
+ Parser *parser = &pr->parser;
151
+
152
+ char line[MAXLINE + 1], // Line from input data file
153
+ wline[MAXLINE + 1]; // Working copy of input line
154
+ char errmsg[MAXMSG + 1] = "";
155
+ int sect, newsect, // Data sections
156
+ errcode = 0, // Error code
157
+ inperr, errsum; // Error code & total error count
158
+
159
+ // Allocate input buffer
160
+ parser->X = (double *)calloc(MAXTOKS, sizeof(double));
161
+ ERRCODE(MEMCHECK(parser->X));
162
+ if (errcode) return errcode;
163
+
164
+ // Initialize actual number of network components
165
+ parser->Ntitle = 0;
166
+ net->Nnodes = 0;
167
+ net->Njuncs = 0;
168
+ net->Ntanks = 0;
169
+ net->Nlinks = 0;
170
+ net->Npipes = 0;
171
+ net->Npumps = 0;
172
+ net->Nvalves = 0;
173
+ net->Ncontrols = 0;
174
+ net->Nrules = 0;
175
+
176
+ // Patterns & Curves were created previously in netsize()
177
+ parser->MaxPats = net->Npats;
178
+ parser->MaxCurves = net->Ncurves;
179
+ parser->PrevPat = NULL;
180
+ parser->PrevCurve = NULL;
181
+
182
+ // Initialize full line comment, input data section and error count
183
+ parser->LineComment[0] = '\0';
184
+ sect = -1;
185
+ errsum = 0;
186
+
187
+ // Read each line from input file
188
+ while (fgets(line, MAXLINE, parser->InFile) != NULL)
189
+ {
190
+ // Make copy of line and scan for tokens
191
+ strcpy(wline, line);
192
+ parser->Ntokens = gettokens(wline, parser->Tok, MAXTOKS, parser->Comment);
193
+
194
+ // Skip blank lines and those filled with a comment
195
+ parser->ErrTok = -1;
196
+ if (parser->Ntokens == 0)
197
+ {
198
+ // Store full line comment for Patterns and Curves
199
+ if (sect == _PATTERNS || sect == _CURVES)
200
+ {
201
+ strncpy(parser->LineComment, parser->Comment, MAXMSG);
202
+ }
203
+ continue;
204
+ }
205
+
206
+ // Apply full line comment for Patterns and Curves
207
+ if (sect == _PATTERNS || sect == _CURVES)
208
+ {
209
+ strcpy(parser->Comment, parser->LineComment);
210
+ }
211
+ parser->LineComment[0] = '\0';
212
+
213
+ // Check if max. line length exceeded
214
+ if (strlen(line) >= MAXLINE)
215
+ {
216
+ sprintf(pr->Msg, "%s section: %s", geterrmsg(214, errmsg), SectTxt[sect]);
217
+ writeline(pr, pr->Msg);
218
+ writeline(pr, line);
219
+ errsum++;
220
+ }
221
+
222
+ // Check if at start of a new input section
223
+ if (parser->Tok[0][0] == '[')
224
+ {
225
+ newsect = findmatch(parser->Tok[0], SectTxt);
226
+ if (newsect >= 0)
227
+ {
228
+ sect = newsect;
229
+ if (sect == _END) break;
230
+ continue;
231
+ }
232
+ else
233
+ {
234
+ sect = -1;
235
+ parser->ErrTok = 0;
236
+ errsum++;
237
+ inperrmsg(pr, 299, sect, line);
238
+ continue;
239
+ }
240
+ }
241
+
242
+ // Otherwise process next line of input in current section
243
+ else
244
+ {
245
+ if (sect >= 0)
246
+ {
247
+ inperr = newline(pr, sect, line);
248
+ if (inperr > 0)
249
+ {
250
+ inperrmsg(pr, inperr, sect, line);
251
+ errsum++;
252
+ }
253
+ }
254
+ else continue;
255
+ }
256
+ }
257
+
258
+ // Check for errors
259
+ if (errsum > 0) errcode = 200;
260
+
261
+ // Free input buffer
262
+ free(parser->X);
263
+ return errcode;
264
+ }
265
+
266
+ int newline(Project *pr, int sect, char *line)
267
+ /*
268
+ **--------------------------------------------------------------
269
+ ** Input: sect = current section of input file
270
+ ** *line = line read from input file
271
+ ** Output: returns error code or 0 if no error found
272
+ ** Purpose: processes a new line of data from input file
273
+ **
274
+ ** Note: The xxxdata() functions appear in INPUT3.c.
275
+ **--------------------------------------------------------------
276
+ */
277
+ {
278
+ Parser *parser = &pr->parser;
279
+ int n;
280
+
281
+ switch (sect)
282
+ {
283
+ case _TITLE:
284
+ if (parser->Ntitle < 3)
285
+ {
286
+ n = (int)strlen(line);
287
+ if (line[n - 1] == 10)
288
+ line[n - 1] = '\0';
289
+ strncpy(pr->Title[parser->Ntitle], line, TITLELEN);
290
+ parser->Ntitle++;
291
+ }
292
+ return 0;
293
+ case _JUNCTIONS: return (juncdata(pr));
294
+ case _RESERVOIRS:
295
+ case _TANKS: return (tankdata(pr));
296
+ case _PIPES: return (pipedata(pr));
297
+ case _PUMPS: return (pumpdata(pr));
298
+ case _VALVES: return (valvedata(pr));
299
+ case _PATTERNS: return (patterndata(pr));
300
+ case _CURVES: return (curvedata(pr));
301
+ case _DEMANDS: return (demanddata(pr));
302
+ case _CONTROLS: return (controldata(pr));
303
+ case _RULES:
304
+ if (ruledata(pr) > 0)
305
+ {
306
+ ruleerrmsg(pr);
307
+ deleterule(pr, pr->network.Nrules);
308
+ return 200;
309
+ }
310
+ else return 0;
311
+ case _SOURCES: return (sourcedata(pr));
312
+ case _EMITTERS: return (emitterdata(pr));
313
+ case _LEAKAGE: return (leakagedata(pr));
314
+ case _QUALITY: return (qualdata(pr));
315
+ case _STATUS: return (statusdata(pr));
316
+ case _ROUGHNESS: return (0);
317
+ case _ENERGY: return (energydata(pr));
318
+ case _REACTIONS: return (reactdata(pr));
319
+ case _MIXING: return (mixingdata(pr));
320
+ case _REPORT: return (reportdata(pr));
321
+ case _TIMES: return (timedata(pr));
322
+ case _OPTIONS: return (optiondata(pr));
323
+ case _TAGS: return (tagdata(pr));
324
+ case _COORDS: return (coordata(pr));
325
+ case _VERTICES: return (vertexdata(pr));
326
+
327
+ // Data in these sections are not used for any computations
328
+ case _LABELS:
329
+ case _BACKDROP:
330
+ return (0);
331
+ }
332
+ return 201;
333
+ }
334
+
335
+ int addnodeID(Network *net, int n, char *id)
336
+ /*
337
+ **-------------------------------------------------------------
338
+ ** Input: n = node index
339
+ ** id = ID label
340
+ ** Output: returns 0 if ID already in use, 1 if not
341
+ ** Purpose: adds a node ID to the Node Hash Table
342
+ **--------------------------------------------------------------
343
+ */
344
+ {
345
+ if (findnode(net,id))
346
+ return 215; // duplicate id
347
+ if (strlen(id) > MAXID)
348
+ return 252; // invalid format (too long)
349
+ strncpy(net->Node[n].ID, id, MAXID);
350
+ hashtable_insert(net->NodeHashTable, net->Node[n].ID, n);
351
+ return 0;
352
+ }
353
+
354
+ int addlinkID(Network *net, int n, char *id)
355
+ /*
356
+ **-------------------------------------------------------------
357
+ ** Input: n = link index
358
+ ** id = ID label
359
+ ** Output: returns 0 if ID already in use, 1 if not
360
+ ** Purpose: adds a link ID to the Link Hash Table
361
+ **--------------------------------------------------------------
362
+ */
363
+ {
364
+ if (findlink(net,id))
365
+ return 215; // duplicate id
366
+ if (strlen(id) > MAXID)
367
+ return 252; // invalid formt (too long);
368
+ strncpy(net->Link[n].ID, id, MAXID);
369
+ hashtable_insert(net->LinkHashTable, net->Link[n].ID, n);
370
+ return 0;
371
+ }
372
+
373
+ int addpattern(Network *network, char *id)
374
+ /*
375
+ **-------------------------------------------------------------
376
+ ** Input: id = pattern ID label
377
+ ** Output: returns error code
378
+ ** Purpose: adds a new pattern to the database
379
+ **--------------------------------------------------------------
380
+ */
381
+ {
382
+ int n = network->Npats;
383
+ Spattern *pattern;
384
+
385
+ // Check if pattern was already created
386
+ if (n > 0)
387
+ {
388
+ if (strcmp(id, network->Pattern[n].ID) == 0) return 0;
389
+ if (findpattern(network, id) > 0) return 0;
390
+ }
391
+ if (strlen(id) > MAXID) return 252;
392
+
393
+ // Update pattern count & add a new pattern to the database
394
+ n = n + 2;
395
+ network->Pattern = (Spattern *)realloc(network->Pattern, n * sizeof(Spattern));
396
+ if (network->Pattern == NULL) return 101;
397
+ (network->Npats)++;
398
+
399
+ // Initialize the pattern
400
+ pattern = &network->Pattern[network->Npats];
401
+ strncpy(pattern->ID, id, MAXID);
402
+ pattern->Comment = NULL;
403
+ pattern->Length = 0;
404
+ pattern->F = NULL;
405
+ return 0;
406
+ }
407
+
408
+ int addcurve(Network *network, char *id)
409
+ /*
410
+ **-------------------------------------------------------------
411
+ ** Input: id = curve ID label
412
+ ** Output: returns error code
413
+ ** Purpose: adds a new curve to the database
414
+ **--------------------------------------------------------------
415
+ */
416
+ {
417
+ int n = network->Ncurves;
418
+ Scurve *curve;
419
+
420
+ // Check if was already created
421
+ if (n > 0)
422
+ {
423
+ if (strcmp(id, network->Curve[n].ID) == 0) return 0;
424
+ if (findcurve(network, id) > 0) return 0;
425
+ }
426
+ if (strlen(id) > MAXID) return 252;
427
+
428
+ n = n + 2;
429
+ network->Curve = (Scurve *)realloc(network->Curve, n * sizeof(Scurve));
430
+ if (network->Curve == NULL) return 101;
431
+ (network->Ncurves)++;
432
+
433
+ // Initialize the curve
434
+ curve = &network->Curve[network->Ncurves];
435
+ strncpy(curve->ID, id, MAXID);
436
+ curve->Type = GENERIC_CURVE;
437
+ curve->Comment = NULL;
438
+ curve->Capacity = 0;
439
+ curve->Npts = 0;
440
+ curve->X = NULL;
441
+ curve->Y = NULL;
442
+ return 0;
443
+ }
444
+
445
+ int getunitsoption(Project *pr, char *units)
446
+ /*
447
+ **-------------------------------------------------------------
448
+ ** Input: units = name of flow units to be used
449
+ ** Output: returns 1 if successful, 0 if not
450
+ ** Purpose: sets the flows units to be used by a project.
451
+ **--------------------------------------------------------------
452
+ */
453
+ {
454
+ Parser *parser = &pr->parser;
455
+ if (match(units, w_CFS)) parser->Flowflag = CFS;
456
+ else if (match(units, w_GPM)) parser->Flowflag = GPM;
457
+ else if (match(units, w_AFD)) parser->Flowflag = AFD;
458
+ else if (match(units, w_MGD)) parser->Flowflag = MGD;
459
+ else if (match(units, w_IMGD)) parser->Flowflag = IMGD;
460
+ else if (match(units, w_LPS)) parser->Flowflag = LPS;
461
+ else if (match(units, w_LPM)) parser->Flowflag = LPM;
462
+ else if (match(units, w_CMH)) parser->Flowflag = CMH;
463
+ else if (match(units, w_CMD)) parser->Flowflag = CMD;
464
+ else if (match(units, w_MLD)) parser->Flowflag = MLD;
465
+ else if (match(units, w_CMS)) parser->Flowflag = CMS;
466
+ else if (match(units, w_SI)) parser->Flowflag = LPS;
467
+ else return 0;
468
+ if (parser->Flowflag >= LPS) parser->Unitsflag = SI;
469
+ else parser->Unitsflag = US;
470
+ return 1;
471
+ }
472
+
473
+ int getheadlossoption(Project *pr, char *formula)
474
+ /*
475
+ **-------------------------------------------------------------
476
+ ** Input: formula = name of head loss formula to be used
477
+ ** Output: returns 1 if successful, 0 if not
478
+ ** Purpose: sets the head loss formula to be used by a project.
479
+ **--------------------------------------------------------------
480
+ */
481
+ {
482
+ Hydraul *hyd = &pr->hydraul;
483
+ if (match(formula, w_HW)) hyd->Formflag = HW;
484
+ else if (match(formula, w_DW)) hyd->Formflag = DW;
485
+ else if (match(formula, w_CM)) hyd->Formflag = CM;
486
+ else return 0;
487
+ return 1;
488
+ }
489
+
490
+ int findmatch(char *line, char *keyword[])
491
+ /*
492
+ **--------------------------------------------------------------
493
+ ** Input: *line = line from input file
494
+ ** *keyword[] = list of NULL terminated keywords
495
+ ** Output: returns index of matching keyword or
496
+ ** -1 if no match found
497
+ ** Purpose: determines which keyword appears on input line
498
+ **--------------------------------------------------------------
499
+ */
500
+ {
501
+ int i = 0;
502
+ while (keyword[i] != NULL)
503
+ {
504
+ if (match(line, keyword[i])) return i;
505
+ i++;
506
+ }
507
+ return -1;
508
+ }
509
+
510
+ int match(const char *str, const char *substr)
511
+ /*
512
+ **--------------------------------------------------------------
513
+ ** Input: *str = string being searched
514
+ ** *substr = substring being searched for
515
+ ** Output: returns 1 if substr found in str, 0 if not
516
+ ** Purpose: sees if substr matches any part of str
517
+ **
518
+ ** (Not case sensitive)
519
+ **--------------------------------------------------------------
520
+ */
521
+ {
522
+ int i, j;
523
+
524
+ // Fail if substring is empty
525
+ if (!substr[0]) return 0;
526
+
527
+ // Skip leading blanks of str
528
+ for (i = 0; str[i]; i++)
529
+ {
530
+ if (str[i] != ' ') break;
531
+ }
532
+
533
+ // Check if substr matches remainder of str
534
+ for (j = 0; substr[j]; i++, j++)
535
+ {
536
+ if (!str[i] || UCHAR(str[i]) != UCHAR(substr[j])) return 0;
537
+ }
538
+ return 1;
539
+ }
540
+
541
+ int gettokens(char *s, char** Tok, int maxToks, char *comment)
542
+ /*
543
+ **--------------------------------------------------------------
544
+ ** Input: *s = string to be tokenized
545
+ ** Output: returns number of tokens in s
546
+ ** Purpose: scans string for tokens, saving pointers to them
547
+ ** in module global variable Tok[]
548
+ **
549
+ ** Tokens can be separated by the characters listed in SEPSTR
550
+ ** (spaces, tabs, newline, carriage return) which is defined
551
+ ** in TYPES.H. Text between quotes is treated as a single token.
552
+ **--------------------------------------------------------------
553
+ */
554
+ {
555
+ int n;
556
+ int len, m;
557
+ char *c, *c2;
558
+
559
+ // clear comment
560
+ comment[0] = '\0';
561
+
562
+ // Begin with no tokens
563
+ for (n=0; n<maxToks; n++) Tok[n] = NULL;
564
+ n = 0;
565
+
566
+ // Truncate s at start of comment
567
+ c = strchr(s,';');
568
+ if (c)
569
+ {
570
+ c2 = c+1;
571
+ if (c2)
572
+ {
573
+ // there is a comment here, after the semi-colon.
574
+ len = (int)strlen(c2);
575
+ if (len > 0)
576
+ {
577
+ len = (int)strcspn(c2, "\n\r");
578
+ len = MIN(len, MAXMSG);
579
+ strncpy(comment, c2, len);
580
+ comment[MIN(len,MAXMSG)] = '\0';
581
+ }
582
+ }
583
+ *c = '\0';
584
+ }
585
+ len = (int)strlen(s);
586
+
587
+ // Scan s for tokens until nothing left
588
+ while (len > 0 && n < MAXTOKS)
589
+ {
590
+ m = (int)strcspn(s,SEPSTR); // Find token length
591
+ if (m == len) // s is last token
592
+ {
593
+ Tok[n] = s;
594
+ n++;
595
+ break;
596
+ }
597
+ len -= m+1; // Update length of s
598
+ if (m == 0) s++; // No token found
599
+ else
600
+ {
601
+ if (*s == '"') // Token begins with quote
602
+ {
603
+ s++; // Start token after quote
604
+ m = (int)strcspn(s,"\"\n\r"); // Find end quote (or EOL)
605
+ }
606
+ s[m] = '\0'; // Null-terminate the token
607
+ Tok[n] = s; // Save pointer to token
608
+ n++; // Update token count
609
+ s += m+1; // Begin next token
610
+ }
611
+ }
612
+ return n;
613
+ }
614
+
615
+ double hour(char *time, char *units)
616
+ /*
617
+ **---------------------------------------------------------
618
+ ** Input: *time = string containing a time value
619
+ ** *units = string containing time units
620
+ ** Output: returns numerical value of time in hours,
621
+ ** or -1 if an error occurs
622
+ ** Purpose: converts time from units to hours
623
+ **---------------------------------------------------------
624
+ */
625
+ {
626
+ int n;
627
+ double y[3];
628
+ char *s;
629
+
630
+ // Separate clock time into hrs, min, sec
631
+ for (n = 0; n < 3; n++) y[n] = 0.0;
632
+ n = 0;
633
+ s = strtok(time, ":");
634
+ while (s != NULL && n <= 3)
635
+ {
636
+ if (!getfloat(s, &y[n])) return -1.0;
637
+ s = strtok(NULL, ":");
638
+ n++;
639
+ }
640
+
641
+ // If decimal time with units attached then convert to hours
642
+ if (n == 1)
643
+ {
644
+ if (strlen(units) == 0) return (y[0]);
645
+ if (match(units, w_SECONDS)) return (y[0] / 3600.0);
646
+ if (match(units, w_MINUTES)) return (y[0] / 60.0);
647
+ if (match(units, w_HOURS)) return (y[0]);
648
+ if (match(units, w_DAYS)) return (y[0] * 24.0);
649
+ }
650
+
651
+ // Convert hh:mm:ss format to decimal hours
652
+ if (n > 1) y[0] = y[0] + y[1] / 60.0 + y[2] / 3600.0;
653
+
654
+ // If am/pm attached then adjust hour accordingly
655
+ // (12 am is midnight, 12 pm is noon)
656
+ if (units[0] == '\0') return y[0];
657
+ if (match(units, w_AM))
658
+ {
659
+ if (y[0] >= 13.0) return -1.0;
660
+ if (y[0] >= 12.0) return (y[0] - 12.0);
661
+ else return (y[0]);
662
+ }
663
+ if (match(units, w_PM))
664
+ {
665
+ if (y[0] >= 13.0) return -1.0;
666
+ if (y[0] >= 12.0) return y[0];
667
+ else return (y[0] + 12.0);
668
+ }
669
+ return -1.0;
670
+ }
671
+
672
+ int getfloat(char *s, double *y)
673
+ /*
674
+ **-----------------------------------------------------------
675
+ ** Input: *s = character string
676
+ ** Output: *y = floating point number
677
+ ** returns 1 if conversion successful, 0 if not
678
+ ** Purpose: converts string to floating point number
679
+ **-----------------------------------------------------------
680
+ */
681
+ {
682
+ char *endptr;
683
+ *y = (double)strtod(s, &endptr);
684
+ if (*endptr > 0) return 0;
685
+ return 1;
686
+ }
687
+
688
+ int setreport(Project *pr, char *s)
689
+ /*
690
+ **-----------------------------------------------------------
691
+ ** Input: *s = report format command
692
+ ** Output: none
693
+ ** Returns: error code
694
+ ** Purpose: processes a report formatting command
695
+ ** issued by the ENsetreport function
696
+ **-----------------------------------------------------------
697
+ */
698
+ {
699
+ Parser *parser = &pr->parser;
700
+ parser->Ntokens = gettokens(s, parser->Tok, MAXTOKS, parser->Comment);
701
+ return reportdata(pr);
702
+ }
703
+
704
+ void inperrmsg(Project *pr, int err, int sect, char *line)
705
+ /*
706
+ **-------------------------------------------------------------
707
+ ** Input: err = error code
708
+ ** sect = input data section
709
+ ** *line = line from input file
710
+ ** Output: none
711
+ ** Purpose: displays input reader error message
712
+ **-------------------------------------------------------------
713
+ */
714
+ {
715
+ Parser *parser = &pr->parser;
716
+
717
+ char errStr[MAXMSG + 1] = "";
718
+ char tok[MAXMSG + 1];
719
+
720
+ // Get token associated with input error
721
+ if (parser->ErrTok >= 0) strcpy(tok, parser->Tok[parser->ErrTok]);
722
+ else strcpy(tok, "");
723
+
724
+ // write error message to report file
725
+ if (err == 299)
726
+ sprintf(pr->Msg, "Error %d: %s %s: section contents ignored.",
727
+ err, geterrmsg(err, errStr), tok);
728
+ else
729
+ sprintf(pr->Msg, "Error %d: %s %s in %s section:",
730
+ err, geterrmsg(err, errStr), tok, SectTxt[sect]);
731
+ writeline(pr, pr->Msg);
732
+
733
+ // Echo input line
734
+ writeline(pr, line);
735
+ }
736
+
737
+ char *read_line_from_buffer(char *line, const int maxLen, const char **buf)
738
+ {
739
+ if(**buf == '\0')
740
+ return NULL;
741
+
742
+ int i;
743
+ for(i=0; i <= maxLen - 1; ++(*buf))
744
+ {
745
+ if(**buf == '\0')
746
+ break;
747
+ if(**buf == '\n')
748
+ {
749
+ *(line++)='\0';
750
+ ++(*buf);
751
+ break;
752
+ }
753
+
754
+ *(line++) = **buf;
755
+ }
756
+
757
+ if(i == maxLen - 1)
758
+ *(line++) = '\0';
759
+
760
+ return line;
761
+ }
762
+
763
+ int netsize_from_buffer(Project *pr, const char* inpBuffer)
764
+ {
765
+ Parser *parser = &pr->parser;
766
+
767
+ char line[MAXLINE + 1]; // Line from input data file
768
+ char *tok; // First token of line
769
+ int sect, newsect; // Input data sections
770
+ int errcode = 0; // Error code
771
+ Spattern *pattern;
772
+
773
+ // Initialize object counts
774
+ parser->MaxJuncs = 0;
775
+ parser->MaxTanks = 0;
776
+ parser->MaxPipes = 0;
777
+ parser->MaxPumps = 0;
778
+ parser->MaxValves = 0;
779
+ parser->MaxControls = 0;
780
+ parser->MaxRules = 0;
781
+ parser->MaxCurves = 0;
782
+ sect = -1;
783
+
784
+
785
+ // Add a "dummy" time pattern with index of 0 and a single multiplier
786
+ // of 1.0 to be used by all demands not assigned a pattern
787
+ pr->network.Npats = -1;
788
+ errcode = addpattern(&pr->network, "");
789
+ if (errcode) return errcode;
790
+ pattern = &pr->network.Pattern[0];
791
+ pattern->Length = 1;
792
+ pattern[0].F = (double *)calloc(1, sizeof(double));
793
+ pattern[0].F[0] = 1.0;
794
+ parser->MaxPats = pr->network.Npats;
795
+
796
+ // Make a pass through input buffer counting number of each object
797
+ if (inpBuffer == NULL) return 0;
798
+ while(read_line_from_buffer(&line[0], MAXLINE, &inpBuffer) != NULL)
799
+ {
800
+ // Skip blank lines & those beginning with a comment
801
+ tok = strtok(line, SEPSTR);
802
+ if (tok == NULL) continue;
803
+ if (*tok == ';') continue;
804
+
805
+ // Check if line begins with a new section heading
806
+ if (tok[0] == '[')
807
+ {
808
+ newsect = findmatch(tok, SectTxt);
809
+ if (newsect >= 0)
810
+ {
811
+ sect = newsect;
812
+ if (sect == _END) break;
813
+ continue;
814
+ }
815
+ else continue;
816
+ }
817
+
818
+ // Add to count of current object
819
+ switch (sect)
820
+ {
821
+ case _JUNCTIONS: parser->MaxJuncs++; break;
822
+ case _RESERVOIRS:
823
+ case _TANKS: parser->MaxTanks++; break;
824
+ case _PIPES: parser->MaxPipes++; break;
825
+ case _PUMPS: parser->MaxPumps++; break;
826
+ case _VALVES: parser->MaxValves++; break;
827
+ case _CONTROLS: parser->MaxControls++; break;
828
+ case _RULES: addrule(parser,tok); break;
829
+ case _PATTERNS:
830
+ errcode = addpattern(&pr->network, tok);
831
+ parser->MaxPats = pr->network.Npats;
832
+ break;
833
+ case _CURVES:
834
+ errcode = addcurve(&pr->network, tok);
835
+ parser->MaxCurves = pr->network.Ncurves;
836
+ break;
837
+ }
838
+ if (errcode) break;
839
+ }
840
+
841
+ parser->MaxNodes = parser->MaxJuncs + parser->MaxTanks;
842
+ parser->MaxLinks = parser->MaxPipes + parser->MaxPumps + parser->MaxValves;
843
+ if (parser->MaxPats < 1) parser->MaxPats = 1;
844
+ if (!errcode)
845
+ {
846
+ if (parser->MaxJuncs < 1) errcode = 223; // Not enough nodes
847
+ else if (parser->MaxTanks == 0) errcode = 224; // No tanks
848
+ }
849
+ return errcode;
850
+ }
851
+
852
+ int read_data_from_buffer(Project *pr, const char *inpBuffer)
853
+ {
854
+ Network *net = &pr->network;
855
+ Parser *parser = &pr->parser;
856
+
857
+ char line[MAXLINE + 1], // Line from input data file
858
+ wline[MAXLINE + 1]; // Working copy of input line
859
+ int sect, newsect, // Data sections
860
+ errcode = 0, // Error code
861
+ inperr, errsum; // Error code & total error count
862
+
863
+ // Allocate input buffer
864
+ parser->X = (double *)calloc(MAXTOKS, sizeof(double));
865
+ ERRCODE(MEMCHECK(parser->X));
866
+ if (errcode) return errcode;
867
+
868
+ // Initialize actual number of network components
869
+ parser->Ntitle = 0;
870
+ net->Nnodes = 0;
871
+ net->Njuncs = 0;
872
+ net->Ntanks = 0;
873
+ net->Nlinks = 0;
874
+ net->Npipes = 0;
875
+ net->Npumps = 0;
876
+ net->Nvalves = 0;
877
+ net->Ncontrols = 0;
878
+ net->Nrules = 0;
879
+
880
+ // Patterns & Curves were created previously in netsize()
881
+ parser->MaxPats = net->Npats;
882
+ parser->MaxCurves = net->Ncurves;
883
+ parser->PrevPat = NULL;
884
+ parser->PrevCurve = NULL;
885
+
886
+ // Initialize full line comment, input data section and error count
887
+ parser->LineComment[0] = '\0';
888
+ sect = -1;
889
+ errsum = 0;
890
+
891
+ // Read each line from input file
892
+ const char **pData = &inpBuffer;
893
+ while (read_line_from_buffer(&line[0], MAXLINE, pData) != NULL)
894
+ {
895
+ // Make copy of line and scan for tokens
896
+ strcpy(wline, line);
897
+ parser->Ntokens = gettokens(wline, parser->Tok, MAXTOKS, parser->Comment);
898
+
899
+ // Skip blank lines and those filled with a comment
900
+ parser->ErrTok = -1;
901
+ if (parser->Ntokens == 0)
902
+ {
903
+ // Store full line comment for Patterns and Curves
904
+ if (sect == _PATTERNS || sect == _CURVES)
905
+ {
906
+ strncpy(parser->LineComment, parser->Comment, MAXMSG);
907
+ }
908
+ continue;
909
+ }
910
+
911
+ // Apply full line comment for Patterns and Curves
912
+ if (sect == _PATTERNS || sect == _CURVES)
913
+ {
914
+ strcpy(parser->Comment, parser->LineComment);
915
+ }
916
+ parser->LineComment[0] = '\0';
917
+
918
+ // Check if max. line length exceeded
919
+ if (strlen(line) >= MAXLINE)
920
+ {
921
+ char errStr[MAXMSG + 1] = "";
922
+ sprintf(pr->Msg, "%s section: %s", geterrmsg(214, errStr), SectTxt[sect]);
923
+ writeline(pr, pr->Msg);
924
+ writeline(pr, line);
925
+ errsum++;
926
+ }
927
+
928
+ // Check if at start of a new input section
929
+ if (parser->Tok[0][0] == '[')
930
+ {
931
+ newsect = findmatch(parser->Tok[0], SectTxt);
932
+ if (newsect >= 0)
933
+ {
934
+ sect = newsect;
935
+ if (sect == _END) break;
936
+ continue;
937
+ }
938
+ else
939
+ {
940
+ sect = -1;
941
+ parser->ErrTok = 0;
942
+ errsum++;
943
+ inperrmsg(pr, 299, sect, line);
944
+ continue;
945
+ }
946
+ }
947
+
948
+ // Otherwise process next line of input in current section
949
+ else
950
+ {
951
+ if (sect >= 0)
952
+ {
953
+ inperr = newline(pr, sect, line);
954
+ if (inperr > 0)
955
+ {
956
+ inperrmsg(pr, inperr, sect, line);
957
+ errsum++;
958
+ }
959
+ }
960
+ else continue;
961
+ }
962
+ }
963
+
964
+ // Check for errors
965
+ if (errsum > 0) errcode = 200;
966
+
967
+ // Free input buffer
968
+ free(parser->X);
969
+ return errcode;
970
+ }