epanet-plus 0.0.1__cp312-cp312-macosx_10_13_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-312-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 +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 +6 -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/project.c ADDED
@@ -0,0 +1,1691 @@
1
+ /*
2
+ ******************************************************************************
3
+ Project: OWA EPANET
4
+ Version: 2.3
5
+ Module: project.c
6
+ Description: project data management routines
7
+ Authors: see AUTHORS
8
+ Copyright: see AUTHORS
9
+ License: see LICENSE
10
+ Last Updated: 04/23/2025
11
+ ******************************************************************************
12
+ */
13
+
14
+ #include <stdlib.h>
15
+ #include <stdio.h>
16
+ #include <string.h>
17
+ #include <math.h>
18
+
19
+ //*** For the Windows SDK _tempnam function ***//
20
+ #ifdef _WIN32
21
+ #include <windows.h>
22
+ #endif
23
+
24
+ #include "types.h"
25
+ #include "funcs.h"
26
+
27
+ int openproject(Project *pr, const char *inpFile, const char *rptFile,
28
+ const char *outFile, int allowerrors)
29
+ /*----------------------------------------------------------------
30
+ ** Input: inpFile = name of input file
31
+ ** rptFile = name of report file
32
+ ** outFile = name of binary output file
33
+ ** allowerrors = TRUE if project can be opened with errors
34
+ ** Output: none
35
+ ** Returns: error code
36
+ ** Purpose: opens an EPANET input file & reads in network data
37
+ **----------------------------------------------------------------
38
+ */
39
+ {
40
+ int errcode = 0;
41
+ int hyderrcode = 0;
42
+ int projectopened;
43
+
44
+ // Set system flags
45
+ pr->Openflag = FALSE;
46
+ pr->hydraul.OpenHflag = FALSE;
47
+ pr->quality.OpenQflag = FALSE;
48
+ pr->outfile.SaveHflag = FALSE;
49
+ pr->outfile.SaveQflag = FALSE;
50
+ pr->Warnflag = FALSE;
51
+ pr->report.Messageflag = TRUE;
52
+ pr->report.Rptflag = 1;
53
+
54
+ // Initialize data arrays to NULL
55
+ initpointers(pr);
56
+
57
+ // Open input & report files
58
+ ERRCODE(openfiles(pr, inpFile, rptFile, outFile));
59
+ if (errcode > 0)
60
+ {
61
+ errmsg(pr, errcode);
62
+ return errcode;
63
+ }
64
+
65
+ // Allocate memory for project's data arrays
66
+ ERRCODE(netsize(pr));
67
+ ERRCODE(allocdata(pr));
68
+
69
+ // Read input data
70
+ ERRCODE(getdata(pr));
71
+
72
+ // Close input file
73
+ if (pr->parser.InFile != NULL)
74
+ {
75
+ fclose(pr->parser.InFile);
76
+ pr->parser.InFile = NULL;
77
+ }
78
+
79
+ // Input file read with no fatal errors
80
+ if (allowerrors) projectopened = (errcode == 0 || errcode == 200);
81
+ else projectopened = (errcode == 0);
82
+ if (projectopened)
83
+ {
84
+ // If using previously saved hydraulics file then open it
85
+ if (pr->outfile.Hydflag == USE)
86
+ {
87
+ hyderrcode = openhydfile(pr);
88
+ if (hyderrcode > 0)
89
+ {
90
+ errmsg(pr, hyderrcode);
91
+ pr->outfile.Hydflag = SCRATCH;
92
+ }
93
+ }
94
+
95
+ // Write input summary to report file
96
+ if (pr->report.Summaryflag) writesummary(pr);
97
+ pr->Openflag = TRUE;
98
+ }
99
+ errmsg(pr, errcode);
100
+ return errcode;
101
+ }
102
+
103
+ int openfiles(Project *pr, const char *f1, const char *f2, const char *f3)
104
+ /*----------------------------------------------------------------
105
+ ** Input: f1 = pointer to name of input file
106
+ ** f2 = pointer to name of report file
107
+ ** f3 = pointer to name of binary output file
108
+ ** Output: none
109
+ ** Returns: error code
110
+ ** Purpose: opens input & report files
111
+ **----------------------------------------------------------------
112
+ */
113
+ {
114
+ // Initialize file pointers to NULL
115
+ pr->parser.InFile = NULL;
116
+ pr->report.RptFile = NULL;
117
+ pr->outfile.OutFile = NULL;
118
+ pr->outfile.HydFile = NULL;
119
+ pr->outfile.TmpOutFile = NULL;
120
+
121
+ // Save file names
122
+ strncpy(pr->parser.InpFname, f1, MAXFNAME);
123
+ strncpy(pr->report.Rpt1Fname, f2, MAXFNAME);
124
+ strncpy(pr->outfile.OutFname, f3, MAXFNAME);
125
+ if (strlen(f3) > 0) pr->outfile.Outflag = SAVE;
126
+ else
127
+ {
128
+ pr->outfile.Outflag = SCRATCH;
129
+ strcpy(pr->outfile.OutFname, pr->TmpOutFname);
130
+ }
131
+
132
+ // Check that file names are not identical
133
+ if (strlen(f1) > 0 && (strcomp(f1, f2) || strcomp(f1, f3))) return 301;
134
+ if (strlen(f3) > 0 && strcomp(f2, f3)) return 301;
135
+
136
+ // Attempt to open input and report files
137
+ if (strlen(f1) > 0)
138
+ {
139
+ if ((pr->parser.InFile = fopen(f1, "rt")) == NULL) return 302;
140
+ }
141
+ if (strlen(f2) == 0) pr->report.RptFile = stdout;
142
+ else
143
+ {
144
+ pr->report.RptFile = fopen(f2, "wt");
145
+ if (pr->report.RptFile == NULL) return 303;
146
+ }
147
+ writelogo(pr);
148
+ return 0;
149
+ }
150
+
151
+ int openhydfile(Project *pr)
152
+ /*----------------------------------------------------------------
153
+ ** Input: none
154
+ ** Output: none
155
+ ** Returns: error code
156
+ ** Purpose: opens file that saves hydraulics solution
157
+ **----------------------------------------------------------------
158
+ */
159
+ {
160
+ const int Nnodes = pr->network.Nnodes;
161
+ const int Ntanks = pr->network.Ntanks;
162
+ const int Nlinks = pr->network.Nlinks;
163
+ const int Nvalves = pr->network.Nvalves;
164
+ const int Npumps = pr->network.Npumps;
165
+
166
+ INT4 nsize[6]; // Temporary array
167
+ INT4 magic;
168
+ INT4 version;
169
+ int errcode = 0;
170
+
171
+ // If HydFile currently open, then close it
172
+ if (pr->outfile.HydFile != NULL)
173
+ {
174
+ fclose(pr->outfile.HydFile);
175
+ pr->outfile.HydFile = NULL;
176
+ }
177
+
178
+ // Use Hydflag to determine the type of hydraulics file to use.
179
+ // Write error message if the file cannot be opened.
180
+ pr->outfile.HydFile = NULL;
181
+ switch (pr->outfile.Hydflag)
182
+ {
183
+ case SCRATCH:
184
+ strcpy(pr->outfile.HydFname, pr->TmpHydFname);
185
+ pr->outfile.HydFile = fopen(pr->outfile.HydFname, "w+b");
186
+ break;
187
+ case SAVE:
188
+ pr->outfile.HydFile = fopen(pr->outfile.HydFname, "w+b");
189
+ break;
190
+ case USE:
191
+ pr->outfile.HydFile = fopen(pr->outfile.HydFname, "rb");
192
+ break;
193
+ }
194
+ if (pr->outfile.HydFile == NULL) return 305;
195
+
196
+ // If a previous hydraulics solution is not being used, then
197
+ // save the current network size parameters to the file.
198
+ if (pr->outfile.Hydflag != USE)
199
+ {
200
+ magic = MAGICNUMBER;
201
+ version = ENGINE_VERSION;
202
+ nsize[0] = Nnodes;
203
+ nsize[1] = Nlinks;
204
+ nsize[2] = Ntanks;
205
+ nsize[3] = Npumps;
206
+ nsize[4] = Nvalves;
207
+ nsize[5] = (int)pr->times.Dur;
208
+ fwrite(&magic, sizeof(INT4), 1, pr->outfile.HydFile);
209
+ fwrite(&version, sizeof(INT4), 1, pr->outfile.HydFile);
210
+ fwrite(nsize, sizeof(INT4), 6, pr->outfile.HydFile);
211
+ }
212
+
213
+ // If a previous hydraulics solution is being used, then
214
+ // make sure its network size parameters match those of
215
+ // the current network
216
+ if (pr->outfile.Hydflag == USE)
217
+ {
218
+ fread(&magic, sizeof(INT4), 1, pr->outfile.HydFile);
219
+ if (magic != MAGICNUMBER) return 306;
220
+ fread(&version, sizeof(INT4), 1, pr->outfile.HydFile);
221
+ if (version != ENGINE_VERSION) return 306;
222
+ if (fread(nsize, sizeof(INT4), 6, pr->outfile.HydFile) < 6) return 306;
223
+ if (nsize[0] != Nnodes || nsize[1] != Nlinks || nsize[2] != Ntanks ||
224
+ nsize[3] != Npumps || nsize[4] != Nvalves ||
225
+ nsize[5] != pr->times.Dur
226
+ ) return 306;
227
+ pr->outfile.SaveHflag = TRUE;
228
+ }
229
+
230
+ // Save current position in hydraulics file
231
+ // where storage of hydraulic results begins
232
+ pr->outfile.HydOffset = ftell(pr->outfile.HydFile);
233
+ return errcode;
234
+ }
235
+
236
+ int openoutfile(Project *pr)
237
+ /*----------------------------------------------------------------
238
+ ** Input: none
239
+ ** Output: none
240
+ ** Returns: error code
241
+ ** Purpose: opens binary output file.
242
+ **----------------------------------------------------------------
243
+ */
244
+ {
245
+ int errcode = 0;
246
+
247
+ // Close output file if already opened
248
+ closeoutfile(pr);
249
+
250
+ // Try to open binary output file
251
+ pr->outfile.OutFile = fopen(pr->outfile.OutFname, "w+b");
252
+ if (pr->outfile.OutFile == NULL) return 304;
253
+
254
+ // Save basic network data & energy usage results
255
+ ERRCODE(savenetdata(pr));
256
+ pr->outfile.OutOffset1 = ftell(pr->outfile.OutFile);
257
+ ERRCODE(saveenergy(pr));
258
+ pr->outfile.OutOffset2 = ftell(pr->outfile.OutFile);
259
+
260
+ // Open temporary file if computing time series statistic
261
+ if (!errcode)
262
+ {
263
+ if (pr->report.Tstatflag != SERIES)
264
+ {
265
+ pr->outfile.TmpOutFile = fopen(pr->TmpStatFname, "w+b");
266
+ if (pr->outfile.TmpOutFile == NULL) errcode = 304;
267
+ }
268
+ else pr->outfile.TmpOutFile = pr->outfile.OutFile;
269
+ }
270
+ return errcode;
271
+ }
272
+
273
+ void closeoutfile(Project *pr)
274
+ /*----------------------------------------------------------------
275
+ ** Input: none
276
+ ** Output: none
277
+ ** Purpose: closes binary output file.
278
+ **----------------------------------------------------------------
279
+ */
280
+ {
281
+ if (pr->outfile.TmpOutFile != pr->outfile.OutFile)
282
+ {
283
+ if (pr->outfile.TmpOutFile != NULL)
284
+ {
285
+ fclose(pr->outfile.TmpOutFile);
286
+ pr->outfile.TmpOutFile = NULL;
287
+ }
288
+ }
289
+ if (pr->outfile.OutFile != NULL)
290
+ {
291
+ if (pr->outfile.OutFile == pr->outfile.TmpOutFile)
292
+ {
293
+ pr->outfile.TmpOutFile = NULL;
294
+ }
295
+ fclose(pr->outfile.OutFile);
296
+ pr->outfile.OutFile = NULL;
297
+ }
298
+ }
299
+
300
+ void initpointers(Project *pr)
301
+ /*----------------------------------------------------------------
302
+ ** Input: none
303
+ ** Output: none
304
+ ** Purpose: initializes data array pointers to NULL
305
+ **----------------------------------------------------------------
306
+ */
307
+ {
308
+ Network* nw = &pr->network;
309
+ nw->Nnodes = 0;
310
+ nw->Ntanks = 0;
311
+ nw->Njuncs = 0;
312
+ nw->Nlinks = 0;
313
+ nw->Npipes = 0;
314
+ nw->Npumps = 0;
315
+ nw->Nvalves = 0;
316
+ nw->Ncontrols = 0;
317
+ nw->Nrules = 0;
318
+ nw->Npats = 0;
319
+ nw->Ncurves = 0;
320
+
321
+ pr->hydraul.NodeDemand = NULL;
322
+ pr->hydraul.NodeHead = NULL;
323
+ pr->hydraul.LinkFlow = NULL;
324
+ pr->hydraul.LinkStatus = NULL;
325
+ pr->hydraul.LinkSetting = NULL;
326
+ pr->hydraul.OldStatus = NULL;
327
+ pr->hydraul.P = NULL;
328
+ pr->hydraul.Y = NULL;
329
+ pr->hydraul.Xflow = NULL;
330
+ pr->hydraul.FullDemand = NULL;
331
+ pr->hydraul.DemandFlow = NULL;
332
+ pr->hydraul.EmitterFlow = NULL;
333
+ pr->hydraul.LeakageFlow = NULL;
334
+ pr->hydraul.Leakage = NULL;
335
+
336
+ pr->quality.NodeQual = NULL;
337
+ pr->quality.PipeRateCoeff = NULL;
338
+
339
+ pr->network.Node = NULL;
340
+ pr->network.Link = NULL;
341
+ pr->network.Tank = NULL;
342
+ pr->network.Pump = NULL;
343
+ pr->network.Valve = NULL;
344
+ pr->network.Pattern = NULL;
345
+ pr->network.Curve = NULL;
346
+ pr->network.Control = NULL;
347
+ pr->network.Adjlist = NULL;
348
+ pr->network.NodeHashTable = NULL;
349
+ pr->network.LinkHashTable = NULL;
350
+
351
+ pr->hydraul.smatrix.Aii = NULL;
352
+ pr->hydraul.smatrix.Aij = NULL;
353
+ pr->hydraul.smatrix.F = NULL;
354
+ pr->hydraul.smatrix.Order = NULL;
355
+ pr->hydraul.smatrix.Row = NULL;
356
+ pr->hydraul.smatrix.Ndx = NULL;
357
+ pr->hydraul.smatrix.XLNZ = NULL;
358
+ pr->hydraul.smatrix.NZSUB = NULL;
359
+ pr->hydraul.smatrix.LNZ = NULL;
360
+
361
+ pr->report.reportCallback = NULL;
362
+
363
+ initrules(pr);
364
+ }
365
+
366
+ int allocdata(Project *pr)
367
+ /*----------------------------------------------------------------
368
+ ** Input: none
369
+ ** Output: none
370
+ ** Returns: error code
371
+ ** Purpose: allocates memory for network data structures
372
+ **----------------------------------------------------------------
373
+ */
374
+ {
375
+ int n;
376
+ int errcode = 0;
377
+
378
+ // Allocate node & link ID hash tables
379
+ pr->network.NodeHashTable = hashtable_create();
380
+ pr->network.LinkHashTable = hashtable_create();
381
+ ERRCODE(MEMCHECK(pr->network.NodeHashTable));
382
+ ERRCODE(MEMCHECK(pr->network.LinkHashTable));
383
+
384
+ // Allocate memory for network nodes
385
+ //*************************************************************
386
+ // NOTE: Because network components of a given type are indexed
387
+ // starting from 1, their arrays must be sized 1
388
+ // element larger than the number of components.
389
+ //*************************************************************
390
+ if (!errcode)
391
+ {
392
+ n = pr->parser.MaxNodes + 1;
393
+ pr->network.Node = (Snode *)calloc(n, sizeof(Snode));
394
+ pr->hydraul.NodeDemand = (double *)calloc(n, sizeof(double));
395
+ pr->hydraul.NodeHead = (double *)calloc(n, sizeof(double));
396
+ pr->quality.NodeQual = (double *)calloc(n, sizeof(double));
397
+ pr->hydraul.FullDemand = (double *)calloc(n, sizeof(double));
398
+ pr->hydraul.DemandFlow = (double *)calloc(n, sizeof(double));
399
+ pr->hydraul.EmitterFlow = (double *)calloc(n, sizeof(double));
400
+ pr->hydraul.LeakageFlow = (double *)calloc(n, sizeof(double));
401
+ ERRCODE(MEMCHECK(pr->network.Node));
402
+ ERRCODE(MEMCHECK(pr->hydraul.NodeDemand));
403
+ ERRCODE(MEMCHECK(pr->hydraul.NodeHead));
404
+ ERRCODE(MEMCHECK(pr->quality.NodeQual));
405
+ ERRCODE(MEMCHECK(pr->hydraul.FullDemand));
406
+ ERRCODE(MEMCHECK(pr->hydraul.DemandFlow));
407
+ ERRCODE(MEMCHECK(pr->hydraul.EmitterFlow));
408
+ ERRCODE(MEMCHECK(pr->hydraul.LeakageFlow));
409
+ }
410
+
411
+ // Allocate memory for network links
412
+ if (!errcode)
413
+ {
414
+ n = pr->parser.MaxLinks + 1;
415
+ pr->network.Link = (Slink *)calloc(n, sizeof(Slink));
416
+ pr->hydraul.LinkFlow = (double *)calloc(n, sizeof(double));
417
+ pr->hydraul.LinkSetting = (double *)calloc(n, sizeof(double));
418
+ pr->hydraul.LinkStatus = (StatusType *)calloc(n, sizeof(StatusType));
419
+ ERRCODE(MEMCHECK(pr->network.Link));
420
+ ERRCODE(MEMCHECK(pr->hydraul.LinkFlow));
421
+ ERRCODE(MEMCHECK(pr->hydraul.LinkSetting));
422
+ ERRCODE(MEMCHECK(pr->hydraul.LinkStatus));
423
+ }
424
+
425
+ // Allocate memory for tanks, sources, pumps, valves, and controls
426
+ // (memory for Patterns and Curves arrays expanded as each is added)
427
+ if (!errcode)
428
+ {
429
+ pr->network.Tank =
430
+ (Stank *)calloc(pr->parser.MaxTanks + 1, sizeof(Stank));
431
+ pr->network.Pump =
432
+ (Spump *)calloc(pr->parser.MaxPumps + 1, sizeof(Spump));
433
+ pr->network.Valve =
434
+ (Svalve *)calloc(pr->parser.MaxValves + 1, sizeof(Svalve));
435
+ pr->network.Control =
436
+ (Scontrol *)calloc(pr->parser.MaxControls + 1, sizeof(Scontrol));
437
+ ERRCODE(MEMCHECK(pr->network.Tank));
438
+ ERRCODE(MEMCHECK(pr->network.Pump));
439
+ ERRCODE(MEMCHECK(pr->network.Valve));
440
+ ERRCODE(MEMCHECK(pr->network.Control));
441
+ }
442
+
443
+ // Initialize pointers used in nodes and links
444
+ if (!errcode)
445
+ {
446
+ for (n = 0; n <= pr->parser.MaxNodes; n++)
447
+ {
448
+ pr->network.Node[n].D = NULL; // node demand
449
+ pr->network.Node[n].S = NULL; // node source
450
+ pr->network.Node[n].Comment = NULL;
451
+ pr->network.Node[n].Tag = NULL;
452
+ }
453
+ for (n = 0; n <= pr->parser.MaxLinks; n++)
454
+ {
455
+ pr->network.Link[n].Vertices = NULL;
456
+ pr->network.Link[n].Comment = NULL;
457
+ pr->network.Link[n].Tag = NULL;
458
+ }
459
+ }
460
+
461
+ // Allocate memory for rule base (see RULES.C)
462
+ if (!errcode) errcode = allocrules(pr);
463
+ return errcode;
464
+ }
465
+
466
+ void freedata(Project *pr)
467
+ /*----------------------------------------------------------------
468
+ ** Input: none
469
+ ** Output: none
470
+ ** Purpose: frees memory allocated for network data structures.
471
+ **----------------------------------------------------------------
472
+ */
473
+ {
474
+ int j;
475
+
476
+ // Free memory for computed results
477
+ free(pr->hydraul.NodeDemand);
478
+ free(pr->hydraul.NodeHead);
479
+ free(pr->hydraul.LinkFlow);
480
+ free(pr->hydraul.LinkSetting);
481
+ free(pr->hydraul.LinkStatus);
482
+ free(pr->hydraul.FullDemand);
483
+ free(pr->hydraul.DemandFlow);
484
+ free(pr->hydraul.EmitterFlow);
485
+ free(pr->hydraul.LeakageFlow);
486
+ free(pr->quality.NodeQual);
487
+
488
+ // Free memory used for nodal adjacency lists
489
+ freeadjlists(&pr->network);
490
+
491
+ // Free memory for node data
492
+ if (pr->network.Node != NULL)
493
+ {
494
+ for (j = 1; j <= pr->network.Nnodes; j++)
495
+ {
496
+ // Free memory used for demands and WQ source data
497
+ freedemands(&(pr->network.Node[j]));
498
+ free(pr->network.Node[j].S);
499
+ free(pr->network.Node[j].Comment);
500
+ free(pr->network.Node[j].Tag);
501
+ }
502
+ free(pr->network.Node);
503
+ }
504
+
505
+ // Free memory for link data
506
+ if (pr->network.Link != NULL)
507
+ {
508
+ for (j = 1; j <= pr->network.Nlinks; j++)
509
+ {
510
+ freelinkvertices(&pr->network.Link[j]);
511
+ free(pr->network.Link[j].Comment);
512
+ free(pr->network.Link[j].Tag);
513
+ }
514
+ }
515
+ free(pr->network.Link);
516
+
517
+ // Free memory for other network objects
518
+ free(pr->network.Tank);
519
+ free(pr->network.Pump);
520
+ free(pr->network.Valve);
521
+ free(pr->network.Control);
522
+
523
+ // Free memory for time patterns
524
+ if (pr->network.Pattern != NULL)
525
+ {
526
+ for (j = 0; j <= pr->network.Npats; j++)
527
+ {
528
+ free(pr->network.Pattern[j].F);
529
+ free(pr->network.Pattern[j].Comment);
530
+ }
531
+ free(pr->network.Pattern);
532
+ }
533
+
534
+ // Free memory for curves
535
+ if (pr->network.Curve != NULL)
536
+ {
537
+ // There is no Curve[0]
538
+ for (j = 1; j <= pr->network.Ncurves; j++)
539
+ {
540
+ free(pr->network.Curve[j].X);
541
+ free(pr->network.Curve[j].Y);
542
+ free(pr->network.Curve[j].Comment);
543
+ }
544
+ free(pr->network.Curve);
545
+ }
546
+
547
+ // Free memory for rule base (see RULES.C)
548
+ freerules(pr);
549
+
550
+ // Free hash table memory
551
+ if (pr->network.NodeHashTable != NULL)
552
+ {
553
+ hashtable_free(pr->network.NodeHashTable);
554
+ }
555
+ if (pr->network.LinkHashTable != NULL)
556
+ {
557
+ hashtable_free(pr->network.LinkHashTable);
558
+ }
559
+ }
560
+
561
+ Pdemand finddemand(Pdemand d, int index)
562
+ /*----------------------------------------------------------------
563
+ ** Input: d = pointer to start of a list of demands
564
+ ** index = the position of the demand to retrieve
565
+ ** Output: none
566
+ ** Returns: the demand at the requested position
567
+ ** Purpose: finds the demand at a given position in a demand list
568
+ **----------------------------------------------------------------
569
+ */
570
+ {
571
+ int n = 1;
572
+ if (index <= 0)return NULL;
573
+ while (d)
574
+ {
575
+ if (n == index) break;
576
+ n++;
577
+ d = d->next;
578
+ }
579
+ return d;
580
+ }
581
+
582
+ int adddemand(Snode *node, double dbase, int dpat, const char *dname)
583
+ /*----------------------------------------------------------------
584
+ ** Input: node = a network junction node
585
+ ** dbase = base demand value
586
+ ** dpat = demand pattern index
587
+ ** dname = name of demand category
588
+ ** Output: returns TRUE if successful, FALSE if not
589
+ ** Purpose: adds a new demand category to a node.
590
+ **----------------------------------------------------------------
591
+ */
592
+ {
593
+ Pdemand demand, lastdemand;
594
+
595
+ // Create a new demand struct
596
+ demand = (struct Sdemand *)malloc(sizeof(struct Sdemand));
597
+ if (demand == NULL) return FALSE;
598
+
599
+ // Assign it the designated properties
600
+ demand->Base = dbase;
601
+ demand->Pat = dpat;
602
+ demand->Name = NULL;
603
+ if (dname && strlen(dname) > 0) xstrcpy(&demand->Name, dname, MAXID);
604
+ demand->next = NULL;
605
+
606
+ // If node has no demands make this its first demand category
607
+ if (node->D == NULL) node->D = demand;
608
+
609
+ // Otherwise append this demand to the end of the node's demands list
610
+ else
611
+ {
612
+ lastdemand = node->D;
613
+ while (lastdemand->next) lastdemand = lastdemand->next;
614
+ lastdemand->next = demand;
615
+ }
616
+ return TRUE;
617
+ }
618
+
619
+ void freedemands(Snode *node)
620
+ /*----------------------------------------------------------------
621
+ ** Input: node = a network junction node
622
+ ** Output: node
623
+ ** Purpose: frees the memory used for a node's list of demands.
624
+ **----------------------------------------------------------------
625
+ */
626
+ {
627
+ Pdemand nextdemand;
628
+ Pdemand demand = node->D;
629
+ while (demand != NULL)
630
+ {
631
+ nextdemand = demand->next;
632
+ free(demand->Name);
633
+ free(demand);
634
+ demand = nextdemand;
635
+ }
636
+ node->D = NULL;
637
+ }
638
+
639
+ int addlinkvertex(Slink *link, double x, double y)
640
+ /*----------------------------------------------------------------
641
+ ** Input: link = pointer to a network link
642
+ ** x = x-coordinate of a new vertex
643
+ ** y = y-coordiante of a new vertex
644
+ ** Returns: an error code
645
+ ** Purpose: adds to a link's collection of vertex points.
646
+ **----------------------------------------------------------------
647
+ */
648
+ {
649
+ static int CHUNKSIZE = 5;
650
+ int n, newCapacity;
651
+ Pvertices vertices;
652
+ double *newX, *newY;
653
+
654
+ vertices = link->Vertices;
655
+ if (vertices == NULL)
656
+ {
657
+ vertices = (struct Svertices *) malloc(sizeof(struct Svertices));
658
+ if (vertices == NULL) return 101;
659
+ vertices->Npts = 0;
660
+ vertices->Capacity = 0;
661
+ vertices->X = NULL;
662
+ vertices->Y = NULL;
663
+ link->Vertices = vertices;
664
+ }
665
+ if (vertices->Npts >= vertices->Capacity)
666
+ {
667
+ newCapacity = vertices->Capacity + CHUNKSIZE;
668
+ newX = realloc(vertices->X, newCapacity * sizeof(double));
669
+ newY = realloc(vertices->Y, newCapacity * sizeof(double));
670
+ if (newX == NULL || newY == NULL)
671
+ {
672
+ free(newX);
673
+ free(newY);
674
+ return 101;
675
+ }
676
+ vertices->Capacity = newCapacity;
677
+ vertices->X = newX;
678
+ vertices->Y = newY;
679
+ }
680
+ n = vertices->Npts;
681
+ vertices->X[n] = x;
682
+ vertices->Y[n] = y;
683
+ vertices->Npts++;
684
+ return 0;
685
+ }
686
+
687
+ void freelinkvertices(Slink *link)
688
+ /*----------------------------------------------------------------
689
+ ** Input: vertices = list of link vertex points
690
+ ** Output: none
691
+ ** Purpose: frees the memory used for a link's list of vertices.
692
+ **----------------------------------------------------------------
693
+ */
694
+ {
695
+ if (link->Vertices)
696
+ {
697
+ free(link->Vertices->X);
698
+ free(link->Vertices->Y);
699
+ free(link->Vertices);
700
+ link->Vertices = NULL;
701
+ }
702
+ }
703
+
704
+ int buildadjlists(Network *net)
705
+ /*
706
+ **--------------------------------------------------------------
707
+ ** Input: none
708
+ ** Output: returns error code
709
+ ** Purpose: builds linked list of links adjacent to each node
710
+ **--------------------------------------------------------------
711
+ */
712
+ {
713
+ int i, j, k;
714
+ int errcode = 0;
715
+ Padjlist alink;
716
+
717
+ // Create an array of adjacency lists
718
+ freeadjlists(net);
719
+ net->Adjlist = (Padjlist *)calloc(net->Nnodes + 1, sizeof(Padjlist));
720
+ if (net->Adjlist == NULL) return 101;
721
+ for (i = 0; i <= net->Nnodes; i++) net->Adjlist[i] = NULL;
722
+
723
+ // For each link, update adjacency lists of its end nodes
724
+ for (k = 1; k <= net->Nlinks; k++)
725
+ {
726
+ i = net->Link[k].N1;
727
+ j = net->Link[k].N2;
728
+
729
+ // Include link in start node i's list
730
+ alink = (struct Sadjlist *) malloc(sizeof(struct Sadjlist));
731
+ if (alink == NULL)
732
+ {
733
+ errcode = 101;
734
+ break;
735
+ }
736
+ alink->node = j;
737
+ alink->link = k;
738
+ alink->next = net->Adjlist[i];
739
+ net->Adjlist[i] = alink;
740
+
741
+ // Include link in end node j's list
742
+ alink = (struct Sadjlist *) malloc(sizeof(struct Sadjlist));
743
+ if (alink == NULL)
744
+ {
745
+ errcode = 101;
746
+ break;
747
+ }
748
+ alink->node = i;
749
+ alink->link = k;
750
+ alink->next = net->Adjlist[j];
751
+ net->Adjlist[j] = alink;
752
+ }
753
+ if (errcode) freeadjlists(net);
754
+ return errcode;
755
+ }
756
+
757
+ void freeadjlists(Network *net)
758
+ /*
759
+ **--------------------------------------------------------------
760
+ ** Input: none
761
+ ** Output: none
762
+ ** Purpose: frees memory used for nodal adjacency lists
763
+ **--------------------------------------------------------------
764
+ */
765
+ {
766
+ int i;
767
+ Padjlist alink;
768
+
769
+ if (net->Adjlist == NULL) return;
770
+ for (i = 0; i <= net->Nnodes; i++)
771
+ {
772
+ for (alink = net->Adjlist[i]; alink != NULL; alink = net->Adjlist[i])
773
+ {
774
+ net->Adjlist[i] = alink->next;
775
+ free(alink);
776
+ }
777
+ }
778
+ FREE(net->Adjlist);
779
+ }
780
+
781
+ int incontrols(Project *pr, int objType, int index)
782
+ /*----------------------------------------------------------------
783
+ ** Input: objType = type of object (either NODE or LINK)
784
+ ** index = the object's index
785
+ ** Output: none
786
+ ** Returns: 1 if any controls contain the object; 0 if not
787
+ ** Purpose: determines if any simple or rule-based controls
788
+ ** contain a particular node or link.
789
+ **----------------------------------------------------------------
790
+ */
791
+ {
792
+ Network *net = &pr->network;
793
+
794
+ int i, ruleObject;
795
+ Spremise *premise;
796
+ Saction *action;
797
+
798
+ // Check simple controls
799
+ for (i = 1; i <= net->Ncontrols; i++)
800
+ {
801
+ if (objType == NODE && net->Control[i].Node == index) return 1;
802
+ if (objType == LINK && net->Control[i].Link == index) return 1;
803
+ }
804
+
805
+ // Check rule-based controls
806
+ for (i = 1; i <= net->Nrules; i++)
807
+ {
808
+ // Convert objType to a rule object type
809
+ if (objType == NODE) ruleObject = 6;
810
+ else ruleObject = 7;
811
+
812
+ // Check rule's premises
813
+ premise = net->Rule[i].Premises;
814
+ while (premise != NULL)
815
+ {
816
+ if (ruleObject == premise->object && premise->index == index) return 1;
817
+ premise = premise->next;
818
+ }
819
+
820
+ // Rule actions only need to be checked for link objects
821
+ if (objType == LINK)
822
+ {
823
+ // Check rule's THEN actions
824
+ action = net->Rule[i].ThenActions;
825
+ while (action != NULL)
826
+ {
827
+ if (action->link == index) return 1;
828
+ action = action->next;
829
+ }
830
+
831
+ // Check rule's ELSE actions
832
+ action = net->Rule[i].ElseActions;
833
+ while (action != NULL)
834
+ {
835
+ if (action->link == index) return 1;
836
+ action = action->next;
837
+ }
838
+ }
839
+ }
840
+ return 0;
841
+ }
842
+
843
+ int changevalvetype(Project *pr, int index, int type)
844
+ /*
845
+ **--------------------------------------------------------------
846
+ ** Input: index = link index
847
+ ** type = new valve type
848
+ ** Output: returns an error code
849
+ ** Purpose: changes a valve's type
850
+ **--------------------------------------------------------------
851
+ */
852
+ {
853
+ Network *net = &pr->network;
854
+ Slink *link;
855
+ int errcode;
856
+ double setting;
857
+
858
+ // Check that new valve type has legal connections
859
+ link = &net->Link[index];
860
+ if (link->Type <= PUMP) return 264;
861
+ errcode = valvecheck(pr, index, type, link->N1, link->N2);
862
+ if (errcode) return errcode;
863
+
864
+ // Preserve new type's setting in solver units
865
+ setting = link->InitSetting;
866
+ switch (link->Type)
867
+ {
868
+ case FCV:
869
+ setting *= pr->Ucf[FLOW];
870
+ break;
871
+ case PRV:
872
+ case PSV:
873
+ case PBV:
874
+ setting *= pr->Ucf[PRESSURE];
875
+ break;
876
+ case GPV:
877
+ setting = 0.0;
878
+ break;
879
+ }
880
+ switch (type)
881
+ {
882
+ case FCV:
883
+ setting /= pr->Ucf[FLOW];
884
+ break;
885
+ case PRV:
886
+ case PSV:
887
+ case PBV:
888
+ setting /= pr->Ucf[PRESSURE];
889
+ break;
890
+ }
891
+
892
+ // Save setting
893
+ if (type == GPV) setting = 0.0;
894
+ if (type == PCV) setting = MIN(setting, 100.0);
895
+ link->Kc = setting;
896
+ link->InitSetting = setting;
897
+
898
+ // Change valve link's type
899
+ link->Type = type;
900
+ return 0;
901
+ }
902
+
903
+ int valvecheck(Project *pr, int index, int type, int j1, int j2)
904
+ /*
905
+ **--------------------------------------------------------------
906
+ ** Input: index = link index
907
+ ** type = valve type
908
+ ** j1 = index of upstream node
909
+ ** j2 = index of downstream node
910
+ ** Output: returns an error code
911
+ ** Purpose: checks for illegal connections between valves
912
+ **--------------------------------------------------------------
913
+ */
914
+ {
915
+ Network *net = &pr->network;
916
+
917
+ int k, vj1, vj2;
918
+ LinkType vtype;
919
+ Slink *link;
920
+ Svalve *valve;
921
+
922
+ if (type == PRV || type == PSV || type == FCV)
923
+ {
924
+ // Can't be connected to a fixed grade node
925
+ if (j1 > net->Njuncs || j2 > net->Njuncs) return 219;
926
+
927
+ // Examine each existing valve
928
+ for (k = 1; k <= net->Nvalves; k++)
929
+ {
930
+ valve = &net->Valve[k];
931
+ if (valve->Link == index) continue;
932
+ link = &net->Link[valve->Link];
933
+ vj1 = link->N1;
934
+ vj2 = link->N2;
935
+ vtype = link->Type;
936
+
937
+ // Cannot have two PRVs sharing downstream nodes or in series
938
+ if (vtype == PRV && type == PRV)
939
+ {
940
+ if (vj2 == j2 || vj2 == j1 || vj1 == j2) return 220;
941
+ }
942
+
943
+ // Cannot have two PSVs sharing upstream nodes or in series
944
+ if (vtype == PSV && type == PSV)
945
+ {
946
+ if (vj1 == j1 || vj1 == j2 || vj2 == j1) return 220;
947
+ }
948
+
949
+ // Cannot have PSV connected to downstream node of PRV
950
+ if (vtype == PSV && type == PRV && vj1 == j2) return 220;
951
+ if (vtype == PRV && type == PSV && vj2 == j1) return 220;
952
+
953
+ // Cannot have PSV connected to downstream node of FCV
954
+ // nor have PRV connected to upstream node of FCV
955
+ if (vtype == FCV && type == PSV && vj2 == j1) return 220;
956
+ if (vtype == FCV && type == PRV && vj1 == j2) return 220;
957
+ if (vtype == PSV && type == FCV && vj1 == j2) return 220;
958
+ if (vtype == PRV && type == FCV && vj2 == j1) return 220;
959
+ }
960
+ }
961
+ return 0;
962
+ }
963
+
964
+ int unlinked(Project *pr)
965
+ /*
966
+ **--------------------------------------------------------------
967
+ ** Input: none
968
+ ** Output: returns error code if any unlinked junctions found
969
+ ** Purpose: checks for unlinked junctions in network
970
+ **
971
+ ** NOTE: unlinked tanks have no effect on computations.
972
+ **--------------------------------------------------------------
973
+ */
974
+ {
975
+ Network *net = &pr->network;
976
+ int i, count = 0;
977
+ char errmsg[MAXMSG + 1] = "";
978
+
979
+ for (i = 1; i <= net->Njuncs; i++)
980
+ {
981
+ if (pr->network.Adjlist[i] == NULL)
982
+ {
983
+ count++;
984
+ sprintf(pr->Msg, "Error 234: %s %s", geterrmsg(234, errmsg), net->Node[i].ID);
985
+ writeline(pr, pr->Msg);
986
+ }
987
+ if (count >= 10) break;
988
+ }
989
+ if (count > 0) return 233;
990
+ return 0;
991
+ }
992
+
993
+ int findnode(Network *network, const char *id)
994
+ /*----------------------------------------------------------------
995
+ ** Input: id = node ID
996
+ ** Output: none
997
+ ** Returns: index of node with given ID, or 0 if ID not found
998
+ ** Purpose: uses hash table to find index of node with given ID
999
+ **----------------------------------------------------------------
1000
+ */
1001
+ {
1002
+ return (hashtable_find(network->NodeHashTable, id));
1003
+ }
1004
+
1005
+ int findlink(Network *network, const char *id)
1006
+ /*----------------------------------------------------------------
1007
+ ** Input: id = link ID
1008
+ ** Output: none
1009
+ ** Returns: index of link with given ID, or 0 if ID not found
1010
+ ** Purpose: uses hash table to find index of link with given ID
1011
+ **----------------------------------------------------------------
1012
+ */
1013
+ {
1014
+ return (hashtable_find(network->LinkHashTable, id));
1015
+ }
1016
+
1017
+ int findtank(Network *network, int index)
1018
+ /*----------------------------------------------------------------
1019
+ ** Input: index = node index
1020
+ ** Output: none
1021
+ ** Returns: index of tank with given node index, or NOTFOUND if tank not found
1022
+ ** Purpose: for use in the deletenode function
1023
+ **----------------------------------------------------------------
1024
+ */
1025
+ {
1026
+ int i;
1027
+ for (i = 1; i <= network->Ntanks; i++)
1028
+ {
1029
+ if (network->Tank[i].Node == index) return i;
1030
+ }
1031
+ return NOTFOUND;
1032
+ }
1033
+
1034
+ int findpump(Network *network, int index)
1035
+ /*----------------------------------------------------------------
1036
+ ** Input: index = link ID
1037
+ ** Output: none
1038
+ ** Returns: index of pump with given link index, or NOTFOUND if pump not found
1039
+ ** Purpose: for use in the deletelink function
1040
+ **----------------------------------------------------------------
1041
+ */
1042
+ {
1043
+ int i;
1044
+ for (i = 1; i <= network->Npumps; i++)
1045
+ {
1046
+ if (network->Pump[i].Link == index) return i;
1047
+ }
1048
+ return NOTFOUND;
1049
+ }
1050
+
1051
+ int findvalve(Network *network, int index)
1052
+ /*----------------------------------------------------------------
1053
+ ** Input: index = link ID
1054
+ ** Output: none
1055
+ ** Returns: index of valve with given link index, or NOTFOUND if valve not found
1056
+ ** Purpose: for use in the deletelink function
1057
+ **----------------------------------------------------------------
1058
+ */
1059
+ {
1060
+ int i;
1061
+ for (i = 1; i <= network->Nvalves; i++)
1062
+ {
1063
+ if (network->Valve[i].Link == index) return i;
1064
+ }
1065
+ return NOTFOUND;
1066
+ }
1067
+
1068
+ int findpattern(Network *network, const char *id)
1069
+ /*----------------------------------------------------------------
1070
+ ** Input: id = time pattern ID
1071
+ ** Output: none
1072
+ ** Returns: time pattern index, or -1 if pattern not found
1073
+ ** Purpose: finds index of time pattern given its ID
1074
+ **----------------------------------------------------------------
1075
+ */
1076
+ {
1077
+ int i;
1078
+
1079
+ // Don't forget to include the "dummy" pattern 0 in the search
1080
+ for (i = 0; i <= network->Npats; i++)
1081
+ {
1082
+ if (strcmp(id, network->Pattern[i].ID) == 0) return i;
1083
+ }
1084
+ return -1;
1085
+ }
1086
+
1087
+ int findcurve(Network *network, const char *id)
1088
+ /*----------------------------------------------------------------
1089
+ ** Input: id = data curve ID
1090
+ ** Output: none
1091
+ ** Returns: data curve index, or 0 if curve not found
1092
+ ** Purpose: finds index of data curve given its ID
1093
+ **----------------------------------------------------------------
1094
+ */
1095
+ {
1096
+ int i;
1097
+ for (i = 1; i <= network->Ncurves; i++)
1098
+ {
1099
+ if (strcmp(id, network->Curve[i].ID) == 0) return i;
1100
+ }
1101
+ return 0;
1102
+ }
1103
+
1104
+ void adjustpattern(int *pat, int index)
1105
+ /*----------------------------------------------------------------
1106
+ ** Local function that modifies a reference to a deleted time pattern
1107
+ **----------------------------------------------------------------
1108
+ */
1109
+ {
1110
+ if (*pat == index) *pat = 0;
1111
+ else if (*pat > index) (*pat)--;
1112
+ }
1113
+
1114
+ void adjustpatterns(Network *network, int index)
1115
+ /*----------------------------------------------------------------
1116
+ ** Input: index = index of time pattern being deleted
1117
+ ** Output: none
1118
+ ** Purpose: modifies references made to a deleted time pattern
1119
+ **----------------------------------------------------------------
1120
+ */
1121
+ {
1122
+ int j;
1123
+ Pdemand demand;
1124
+ Psource source;
1125
+
1126
+ // Adjust patterns used by junctions
1127
+ for (j = 1; j <= network->Nnodes; j++)
1128
+ {
1129
+ // Adjust demand patterns
1130
+ for (demand = network->Node[j].D; demand != NULL; demand = demand->next)
1131
+ {
1132
+ adjustpattern(&demand->Pat, index);
1133
+ }
1134
+ // Adjust WQ source patterns
1135
+ source = network->Node[j].S;
1136
+ if (source) adjustpattern(&source->Pat, index);
1137
+ }
1138
+
1139
+ // Adjust patterns used by reservoir tanks
1140
+ for (j = 1; j <= network->Ntanks; j++)
1141
+ {
1142
+ adjustpattern(&network->Tank[j].Pat, index);
1143
+ }
1144
+
1145
+ // Adjust patterns used by pumps
1146
+ for (j = 1; j <= network->Npumps; j++)
1147
+ {
1148
+ adjustpattern(&network->Pump[j].Upat, index);
1149
+ adjustpattern(&network->Pump[j].Epat, index);
1150
+ }
1151
+ }
1152
+
1153
+ void adjustcurve(int *curve, int index)
1154
+ /*----------------------------------------------------------------
1155
+ ** Local function that modifies a reference to a deleted data curve
1156
+ **----------------------------------------------------------------
1157
+ */
1158
+ {
1159
+ if (*curve == index) *curve = 0;
1160
+ else if (*curve > index) (*curve)--;
1161
+ }
1162
+
1163
+ void adjustcurves(Network *network, int index)
1164
+ /*----------------------------------------------------------------
1165
+ ** Input: index = index of data curve being deleted
1166
+ ** Output: none
1167
+ ** Purpose: modifies references made to a deleted data curve
1168
+ **----------------------------------------------------------------
1169
+ */
1170
+ {
1171
+ int j, k, curve;
1172
+
1173
+ // Adjust tank volume curves
1174
+ for (j = 1; j <= network->Ntanks; j++)
1175
+ {
1176
+ adjustcurve(&network->Tank[j].Vcurve, index);
1177
+ }
1178
+
1179
+ // Adjust pump curves
1180
+ for (j = 1; j <= network->Npumps; j++)
1181
+ {
1182
+ adjustcurve(&network->Pump[j].Hcurve, index);
1183
+ adjustcurve(&network->Pump[j].Ecurve, index);
1184
+ }
1185
+
1186
+ // Adjust PCV & GPV curves
1187
+ for (j = 1; j <= network->Nvalves; j++)
1188
+ {
1189
+ k = network->Valve[j].Link;
1190
+ if (network->Link[k].Type == PCV)
1191
+ {
1192
+ if ((curve = network->Valve[j].Curve) > 0)
1193
+ {
1194
+ adjustcurve(&curve, index);
1195
+ network->Valve[j].Curve = curve;
1196
+ if (curve == 0)
1197
+ network->Link[k].Kc = 0.0;
1198
+ }
1199
+ }
1200
+ if (network->Link[k].Type == GPV)
1201
+ {
1202
+ curve = INT(network->Link[k].Kc);
1203
+ adjustcurve(&curve, index);
1204
+ network->Link[k].Kc = curve;
1205
+ }
1206
+ }
1207
+ }
1208
+
1209
+
1210
+ int resizecurve(Scurve *curve, int size)
1211
+ /*----------------------------------------------------------------
1212
+ ** Input: curve = a data curve object
1213
+ ** size = desired number of curve data points
1214
+ ** Output: error code
1215
+ ** Purpose: resizes a data curve to a desired size
1216
+ **----------------------------------------------------------------
1217
+ */
1218
+ {
1219
+ double *x;
1220
+ double *y;
1221
+
1222
+ if (curve->Capacity < size)
1223
+ {
1224
+ x = (double *)realloc(curve->X, size * sizeof(double));
1225
+ if (x == NULL) return 101;
1226
+ y = (double *)realloc(curve->Y, size * sizeof(double));
1227
+ if (y == NULL)
1228
+ {
1229
+ free(x);
1230
+ return 101;
1231
+ }
1232
+ curve->X = x;
1233
+ curve->Y = y;
1234
+ curve->Capacity = size;
1235
+ }
1236
+ return 0;
1237
+ }
1238
+
1239
+
1240
+ int setcontrol(EN_Project p, int type, int linkIndex, double setting,
1241
+ int nodeIndex, double level, Scontrol *control)
1242
+ /*----------------------------------------------------------------
1243
+ ** Input: type = type of control (see EN_ControlType)
1244
+ ** linkIndex = index of link being controlled
1245
+ ** setting = link control setting (e.g., pump speed)
1246
+ ** nodeIndex = index of node controlling a link (for level controls)
1247
+ ** level = control activation level (pressure for junction nodes,
1248
+ ** water level for tank nodes or time value for time-based
1249
+ ** control)
1250
+ ** Output: control = control struct whose properties are being set
1251
+ ** Returns: error code
1252
+ ** Purpose: assigns properties to a control struct.
1253
+ **----------------------------------------------------------------
1254
+ */
1255
+ {
1256
+ Network *net = &p->network;
1257
+ Parser *parser = &p->parser;
1258
+
1259
+ long t = 0;
1260
+ double lvl = 0.0, s = MISSING;
1261
+ double *Ucf = p->Ucf;
1262
+ LinkType linktype;
1263
+ StatusType status = ACTIVE;
1264
+
1265
+ // Cannot control check valve
1266
+ linktype = net->Link[linkIndex].Type;
1267
+ if (linktype == CVPIPE) return 207;
1268
+
1269
+ // Check for valid control type and node index
1270
+ if (type < 0 || type > TIMEOFDAY) return 251;
1271
+ if (type == LOWLEVEL || type == HILEVEL)
1272
+ {
1273
+ if (nodeIndex < 1 || nodeIndex > net->Nnodes) return 203;
1274
+ }
1275
+ else nodeIndex = 0;
1276
+
1277
+ // Check if control setting is a status level
1278
+ if (setting == SET_OPEN)
1279
+ {
1280
+ status = OPEN;
1281
+ if (linktype == PUMP) s = 1.0;
1282
+ if (linktype == GPV) s = net->Link[linkIndex].Kc;
1283
+ }
1284
+ else if (setting == SET_CLOSED)
1285
+ {
1286
+ status = CLOSED;
1287
+ if (linktype == PUMP) s = 0.0;
1288
+ if (linktype == GPV) s = net->Link[linkIndex].Kc;
1289
+ }
1290
+
1291
+ // Convert units of control setting
1292
+ else
1293
+ {
1294
+ s = setting;
1295
+ switch (linktype)
1296
+ {
1297
+ case PIPE:
1298
+ case PUMP:
1299
+ if (s < 0.0) return 202;
1300
+ else if (s == 0.0) status = CLOSED;
1301
+ else status = OPEN;
1302
+ break;
1303
+ case PRV:
1304
+ case PSV:
1305
+ case PBV:
1306
+ s /= Ucf[PRESSURE];
1307
+ break;
1308
+ case FCV:
1309
+ s /= Ucf[FLOW];
1310
+ break;
1311
+ case GPV:
1312
+ if (s == 0.0) status = CLOSED;
1313
+ else if (s == 1.0) status = OPEN;
1314
+ else return 202;
1315
+ s = net->Link[linkIndex].Kc;
1316
+ break;
1317
+ }
1318
+ }
1319
+
1320
+ // Determine if control level is a pressure, tank level or time value
1321
+ if (type == LOWLEVEL || type == HILEVEL)
1322
+ {
1323
+ if (nodeIndex > net->Njuncs) lvl = net->Node[nodeIndex].El + level / Ucf[ELEV];
1324
+ else lvl = net->Node[nodeIndex].El + level / Ucf[PRESSURE];
1325
+ }
1326
+ else if (type == TIMER || type == TIMEOFDAY)
1327
+ {
1328
+ t = (long)level;
1329
+ if (t < 0) return 202;
1330
+ }
1331
+
1332
+ // Assign values to control struct
1333
+ control->Link = linkIndex;
1334
+ control->Node = nodeIndex;
1335
+ control->Type = type;
1336
+ control->Status = status;
1337
+ control->Setting = s;
1338
+ control->Time = t;
1339
+ control->Grade = lvl;
1340
+ control->isEnabled = TRUE;
1341
+ return 0;
1342
+ }
1343
+
1344
+
1345
+ int getcomment(Network *network, int object, int index, char *comment)
1346
+ //----------------------------------------------------------------
1347
+ // Input: object = a type of network object
1348
+ // index = index of the specified object
1349
+ // comment = the object's comment string
1350
+ // Output: error code
1351
+ // Purpose: gets the comment string assigned to an object.
1352
+ //----------------------------------------------------------------
1353
+ {
1354
+ char *currentcomment;
1355
+
1356
+ // Get pointer to specified object's comment
1357
+ switch (object)
1358
+ {
1359
+ case NODE:
1360
+ if (index < 1 || index > network->Nnodes) return 251;
1361
+ currentcomment = network->Node[index].Comment;
1362
+ break;
1363
+ case LINK:
1364
+ if (index < 1 || index > network->Nlinks) return 251;
1365
+ currentcomment = network->Link[index].Comment;
1366
+ break;
1367
+ case TIMEPAT:
1368
+ if (index < 1 || index > network->Npats) return 251;
1369
+ currentcomment = network->Pattern[index].Comment;
1370
+ break;
1371
+ case CURVE:
1372
+ if (index < 1 || index > network->Ncurves) return 251;
1373
+ currentcomment = network->Curve[index].Comment;
1374
+ break;
1375
+ default:
1376
+ strcpy(comment, "");
1377
+ return 251;
1378
+ }
1379
+
1380
+ // Copy the object's comment to the returned string
1381
+ if (currentcomment) strcpy(comment, currentcomment);
1382
+ else comment[0] = '\0';
1383
+ return 0;
1384
+ }
1385
+
1386
+ int setcomment(Network *network, int object, int index, const char *newcomment)
1387
+ //----------------------------------------------------------------
1388
+ // Input: object = a type of network object
1389
+ // index = index of the specified object
1390
+ // newcomment = new comment string
1391
+ // Output: error code
1392
+ // Purpose: sets the comment string of an object.
1393
+ //----------------------------------------------------------------
1394
+ {
1395
+ char *comment;
1396
+
1397
+ switch (object)
1398
+ {
1399
+ case NODE:
1400
+ if (index < 1 || index > network->Nnodes) return 251;
1401
+ comment = network->Node[index].Comment;
1402
+ network->Node[index].Comment = xstrcpy(&comment, newcomment, MAXMSG);
1403
+ return 0;
1404
+
1405
+ case LINK:
1406
+ if (index < 1 || index > network->Nlinks) return 251;
1407
+ comment = network->Link[index].Comment;
1408
+ network->Link[index].Comment = xstrcpy(&comment, newcomment, MAXMSG);
1409
+ return 0;
1410
+
1411
+ case TIMEPAT:
1412
+ if (index < 1 || index > network->Npats) return 251;
1413
+ comment = network->Pattern[index].Comment;
1414
+ network->Pattern[index].Comment = xstrcpy(&comment, newcomment, MAXMSG);
1415
+ return 0;
1416
+
1417
+ case CURVE:
1418
+ if (index < 1 || index > network->Ncurves) return 251;
1419
+ comment = network->Curve[index].Comment;
1420
+ network->Curve[index].Comment = xstrcpy(&comment, newcomment, MAXMSG);
1421
+ return 0;
1422
+
1423
+ default: return 251;
1424
+ }
1425
+ }
1426
+
1427
+ int gettag(Network *network, int object, int index, char *tag)
1428
+ //----------------------------------------------------------------
1429
+ // Input: object = a type of network object
1430
+ // index = index of the specified object
1431
+ // tag = the object's tag string
1432
+ // Output: error code
1433
+ // Purpose: gets the tag string assigned to an object.
1434
+ //----------------------------------------------------------------
1435
+ {
1436
+ char *currenttag;
1437
+
1438
+ // Get pointer to specified object's tag
1439
+ switch (object)
1440
+ {
1441
+ case NODE:
1442
+ if (index < 1 || index > network->Nnodes) return 251;
1443
+ currenttag = network->Node[index].Tag;
1444
+ break;
1445
+ case LINK:
1446
+ if (index < 1 || index > network->Nlinks) return 251;
1447
+ currenttag = network->Link[index].Tag;
1448
+ break;
1449
+ default:
1450
+ strcpy(tag, "");
1451
+ return 251;
1452
+ }
1453
+ // Copy the object's tag to the returned string
1454
+ if (currenttag) strcpy(tag, currenttag);
1455
+ else tag[0] = '\0';
1456
+ return 0;
1457
+ }
1458
+
1459
+ int settag(Network *network, int object, int index, const char *newtag)
1460
+ //----------------------------------------------------------------
1461
+ // Input: object = a type of network object
1462
+ // index = index of the specified object
1463
+ // newtag = new tag string
1464
+ // Output: error code
1465
+ // Purpose: sets the tag string of an object.
1466
+ //----------------------------------------------------------------
1467
+ {
1468
+ char *tag;
1469
+
1470
+ switch (object)
1471
+ {
1472
+ case NODE:
1473
+ if (index < 1 || index > network->Nnodes) return 251;
1474
+ tag = network->Node[index].Tag;
1475
+ network->Node[index].Tag = xstrcpy(&tag, newtag, MAXMSG);
1476
+ return 0;
1477
+
1478
+ case LINK:
1479
+ if (index < 1 || index > network->Nlinks) return 251;
1480
+ tag = network->Link[index].Tag;
1481
+ network->Link[index].Tag = xstrcpy(&tag, newtag, MAXMSG);
1482
+ return 0;
1483
+
1484
+ default: return 251;
1485
+ }
1486
+ }
1487
+
1488
+ int namevalid(const char *name)
1489
+ //----------------------------------------------------------------
1490
+ // Input: name = name used to ID an object
1491
+ // Output: returns TRUE if name is valid, FALSE if not
1492
+ // Purpose: checks that an object's ID name is valid.
1493
+ //----------------------------------------------------------------
1494
+ {
1495
+ size_t n = strlen(name);
1496
+ if (n < 1 || n > MAXID || strpbrk(name, " ;") || name[0] == '"') return FALSE;
1497
+ return TRUE;
1498
+ }
1499
+
1500
+ void getTmpName(char *fname)
1501
+ //----------------------------------------------------------------
1502
+ // Input: fname = file name string
1503
+ // Output: an unused file name
1504
+ // Purpose: creates a temporary file name with an "en" prefix
1505
+ // or a blank name if an error occurs.
1506
+ //----------------------------------------------------------------
1507
+ {
1508
+ #ifdef _WIN32
1509
+
1510
+ char* name = NULL;
1511
+
1512
+ // --- use Windows _tempnam function to get a pointer to an
1513
+ // unused file name that begins with "en"
1514
+ strcpy(fname, "");
1515
+ name = _tempnam(NULL, "en");
1516
+ if (name)
1517
+ {
1518
+ // --- copy the file name to fname
1519
+ if (strlen(name) < MAXFNAME) strncpy(fname, name, MAXFNAME);
1520
+
1521
+ // --- free the pointer returned by _tempnam
1522
+ free(name);
1523
+ }
1524
+ // --- for non-Windows systems:
1525
+ #else
1526
+ // --- use system function mkstemp() to create a temporary file name
1527
+ /*
1528
+ int f = -1;
1529
+ strcpy(fname, "enXXXXXX");
1530
+ f = mkstemp(fname);
1531
+ close(f);
1532
+ remove(fname);
1533
+ */
1534
+ strcpy(fname, "enXXXXXX");
1535
+ FILE *f = fdopen(mkstemp(fname), "r");
1536
+ if (f == NULL) strcpy(fname, "");
1537
+ else fclose(f);
1538
+ remove(fname);
1539
+ #endif
1540
+ }
1541
+
1542
+ char *xstrcpy(char **s1, const char *s2, const size_t n)
1543
+ //----------------------------------------------------------------
1544
+ // Input: s1 = destination string
1545
+ // s2 = source string
1546
+ // n = maximum size of strings
1547
+ // Output: none
1548
+ // Purpose: like strcpy except for dynamic strings.
1549
+ // Note: The calling program is responsible for ensuring that
1550
+ // s1 points to a valid memory location or is NULL. E.g.,
1551
+ // the following code will likely cause a segment fault:
1552
+ // char *s;
1553
+ // s = xstrcpy(&s, "Some text");
1554
+ // while this would work correctly:
1555
+ // char *s = NULL;
1556
+ // s = xstrcpy(&s, "Some text");
1557
+ //----------------------------------------------------------------
1558
+ {
1559
+ size_t n1 = 0, n2 = 0;
1560
+
1561
+ // Find size of source string
1562
+ if (s2) n2 = strlen(s2);
1563
+ if (n2 > n) n2 = n;
1564
+
1565
+ // Source string is empty -- free destination string
1566
+ if (n2 == 0)
1567
+ {
1568
+ free(*s1);
1569
+ *s1 = NULL;
1570
+ return NULL;
1571
+ }
1572
+
1573
+ // See if size of destination string needs to grow
1574
+ if (*s1) n1 = strlen(*s1);
1575
+ if (n2 > n1) *s1 = realloc(*s1, (n2 + 1) * sizeof(char));
1576
+
1577
+ // Copy the source string into the destination string
1578
+ if (*s1) strncpy(*s1, s2, n2+1);
1579
+ return *s1;
1580
+ }
1581
+
1582
+ int strcomp(const char *s1, const char *s2)
1583
+ /*---------------------------------------------------------------
1584
+ ** Input: s1 = character string
1585
+ ** s2 = character string
1586
+ ** Output: none
1587
+ ** Returns: 1 if s1 is same as s2, 0 otherwise
1588
+ ** Purpose: case insensitive comparison of strings s1 & s2
1589
+ **---------------------------------------------------------------
1590
+ */
1591
+ {
1592
+ int i;
1593
+ for (i = 0; UCHAR(s1[i]) == UCHAR(s2[i]); i++)
1594
+ {
1595
+ if (!s1[i + 1] && !s2[i + 1]) return 1;
1596
+ }
1597
+ return 0;
1598
+ }
1599
+
1600
+ double interp(int n, double x[], double y[], double xx)
1601
+ /*----------------------------------------------------------------
1602
+ ** Input: n = number of data pairs defining a curve
1603
+ ** x = x-data values of curve
1604
+ ** y = y-data values of curve
1605
+ ** xx = specified x-value
1606
+ ** Output: none
1607
+ ** Returns: y-value on curve at x = xx
1608
+ ** Purpose: uses linear interpolation to find y-value on a
1609
+ ** data curve corresponding to specified x-value.
1610
+ ** NOTE: does not extrapolate beyond endpoints of curve.
1611
+ **----------------------------------------------------------------
1612
+ */
1613
+ {
1614
+ int k, m;
1615
+ double dx, dy;
1616
+
1617
+ if (n == 0) return 0.0;
1618
+ m = n - 1; // Highest data index
1619
+ if (xx <= x[0]) return (y[0]); // xx off low end of curve
1620
+ for (k = 1; k <= m; k++) // Bracket xx on curve
1621
+ {
1622
+ if (x[k] >= xx) // Interp. over interval
1623
+ {
1624
+ dx = x[k] - x[k - 1];
1625
+ dy = y[k] - y[k - 1];
1626
+ if (ABS(dx) < TINY) return (y[k]);
1627
+ else return (y[k] - (x[k] - xx) * dy / dx);
1628
+ }
1629
+ }
1630
+ return (y[m]); // xx off high end of curve
1631
+ }
1632
+
1633
+ char *geterrmsg(int errcode, char *msg)
1634
+ /*----------------------------------------------------------------
1635
+ ** Input: errcode = error code
1636
+ ** Output: none
1637
+ ** Returns: pointer to string with error message
1638
+ ** Purpose: retrieves text of error message
1639
+ **----------------------------------------------------------------
1640
+ */
1641
+ {
1642
+ switch (errcode)
1643
+ {
1644
+
1645
+ //#define DAT(code,string) case code: sprintf(msg, "%s", string); break;
1646
+ #define DAT(code,string) case code: strcpy(msg, string); break;
1647
+ #include "errors.dat"
1648
+ #undef DAT
1649
+
1650
+ default:
1651
+ strcpy(msg, "");
1652
+ }
1653
+ return (msg);
1654
+ }
1655
+
1656
+ void errmsg(Project *pr, int errcode)
1657
+ /*----------------------------------------------------------------
1658
+ ** Input: errcode = error code
1659
+ ** Output: none
1660
+ ** Purpose: writes error message to report file
1661
+ **----------------------------------------------------------------
1662
+ */
1663
+ {
1664
+ char errmsg[MAXMSG + 1] = "";
1665
+ if (errcode == 309) /* Report file write error - */
1666
+ { /* Do not write msg to file. */
1667
+
1668
+ }
1669
+ else if (pr->report.RptFile != NULL && pr->report.Messageflag && errcode > 100)
1670
+ {
1671
+ sprintf(pr->Msg, "Error %d: %s", errcode, geterrmsg(errcode, errmsg));
1672
+ writeline(pr, pr->Msg);
1673
+ }
1674
+ }
1675
+
1676
+ void writewin(void(*vp)(char *), char *s)
1677
+ /*----------------------------------------------------------------
1678
+ ** Input: text string
1679
+ ** Output: none
1680
+ ** Purpose: passes character string to viewprog() in
1681
+ ** application which calls the EPANET DLL
1682
+ **----------------------------------------------------------------
1683
+ */
1684
+ {
1685
+ char progmsg[MAXMSG + 1];
1686
+ if (vp != NULL)
1687
+ {
1688
+ strncpy(progmsg, s, MAXMSG);
1689
+ vp(progmsg);
1690
+ }
1691
+ }