epanet-plus 0.0.1__cp39-cp39-win32.whl

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

Potentially problematic release.


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

Files changed (105) hide show
  1. docs/conf.py +67 -0
  2. epanet-msx-src/dispersion.h +27 -0
  3. epanet-msx-src/hash.c +107 -0
  4. epanet-msx-src/hash.h +28 -0
  5. epanet-msx-src/include/epanetmsx.h +104 -0
  6. epanet-msx-src/include/epanetmsx_export.h +42 -0
  7. epanet-msx-src/mathexpr.c +937 -0
  8. epanet-msx-src/mathexpr.h +39 -0
  9. epanet-msx-src/mempool.c +204 -0
  10. epanet-msx-src/mempool.h +24 -0
  11. epanet-msx-src/msxchem.c +1285 -0
  12. epanet-msx-src/msxcompiler.c +368 -0
  13. epanet-msx-src/msxdict.h +42 -0
  14. epanet-msx-src/msxdispersion.c +586 -0
  15. epanet-msx-src/msxerr.c +116 -0
  16. epanet-msx-src/msxfile.c +260 -0
  17. epanet-msx-src/msxfuncs.c +175 -0
  18. epanet-msx-src/msxfuncs.h +35 -0
  19. epanet-msx-src/msxinp.c +1504 -0
  20. epanet-msx-src/msxout.c +398 -0
  21. epanet-msx-src/msxproj.c +791 -0
  22. epanet-msx-src/msxqual.c +2011 -0
  23. epanet-msx-src/msxrpt.c +400 -0
  24. epanet-msx-src/msxtank.c +422 -0
  25. epanet-msx-src/msxtoolkit.c +1164 -0
  26. epanet-msx-src/msxtypes.h +551 -0
  27. epanet-msx-src/msxutils.c +524 -0
  28. epanet-msx-src/msxutils.h +56 -0
  29. epanet-msx-src/newton.c +158 -0
  30. epanet-msx-src/newton.h +34 -0
  31. epanet-msx-src/rk5.c +287 -0
  32. epanet-msx-src/rk5.h +39 -0
  33. epanet-msx-src/ros2.c +293 -0
  34. epanet-msx-src/ros2.h +35 -0
  35. epanet-msx-src/smatrix.c +816 -0
  36. epanet-msx-src/smatrix.h +29 -0
  37. epanet-src/AUTHORS +60 -0
  38. epanet-src/LICENSE +21 -0
  39. epanet-src/enumstxt.h +151 -0
  40. epanet-src/epanet.c +5937 -0
  41. epanet-src/epanet2.c +961 -0
  42. epanet-src/epanet2.def +131 -0
  43. epanet-src/errors.dat +79 -0
  44. epanet-src/flowbalance.c +186 -0
  45. epanet-src/funcs.h +219 -0
  46. epanet-src/genmmd.c +1000 -0
  47. epanet-src/hash.c +177 -0
  48. epanet-src/hash.h +28 -0
  49. epanet-src/hydcoeffs.c +1303 -0
  50. epanet-src/hydraul.c +1164 -0
  51. epanet-src/hydsolver.c +781 -0
  52. epanet-src/hydstatus.c +442 -0
  53. epanet-src/include/epanet2.h +466 -0
  54. epanet-src/include/epanet2_2.h +1962 -0
  55. epanet-src/include/epanet2_enums.h +518 -0
  56. epanet-src/inpfile.c +884 -0
  57. epanet-src/input1.c +672 -0
  58. epanet-src/input2.c +970 -0
  59. epanet-src/input3.c +2265 -0
  60. epanet-src/leakage.c +527 -0
  61. epanet-src/mempool.c +146 -0
  62. epanet-src/mempool.h +24 -0
  63. epanet-src/output.c +853 -0
  64. epanet-src/project.c +1691 -0
  65. epanet-src/quality.c +695 -0
  66. epanet-src/qualreact.c +800 -0
  67. epanet-src/qualroute.c +696 -0
  68. epanet-src/report.c +1559 -0
  69. epanet-src/rules.c +1500 -0
  70. epanet-src/smatrix.c +871 -0
  71. epanet-src/text.h +508 -0
  72. epanet-src/types.h +928 -0
  73. epanet-src/util/cstr_helper.c +59 -0
  74. epanet-src/util/cstr_helper.h +38 -0
  75. epanet-src/util/errormanager.c +92 -0
  76. epanet-src/util/errormanager.h +39 -0
  77. epanet-src/util/filemanager.c +212 -0
  78. epanet-src/util/filemanager.h +81 -0
  79. epanet-src/validate.c +408 -0
  80. epanet.cp39-win32.pyd +0 -0
  81. epanet_plus/VERSION +1 -0
  82. epanet_plus/__init__.py +8 -0
  83. epanet_plus/epanet_plus.c +118 -0
  84. epanet_plus/epanet_toolkit.py +2730 -0
  85. epanet_plus/epanet_wrapper.py +2414 -0
  86. epanet_plus/include/epanet_plus.h +9 -0
  87. epanet_plus-0.0.1.dist-info/METADATA +152 -0
  88. epanet_plus-0.0.1.dist-info/RECORD +105 -0
  89. epanet_plus-0.0.1.dist-info/WHEEL +5 -0
  90. epanet_plus-0.0.1.dist-info/licenses/LICENSE +21 -0
  91. epanet_plus-0.0.1.dist-info/top_level.txt +10 -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/rules.c ADDED
@@ -0,0 +1,1500 @@
1
+ /*
2
+ ******************************************************************************
3
+ Project: OWA EPANET
4
+ Version: 2.3
5
+ Module: rules.c
6
+ Description: implements rule-based controls
7
+ Authors: see AUTHORS
8
+ Copyright: see AUTHORS
9
+ License: see LICENSE
10
+ Last Updated: 02/11/2025
11
+ ******************************************************************************
12
+ */
13
+
14
+ #include <stdlib.h>
15
+ #include <stdio.h>
16
+ #include <string.h>
17
+
18
+ #include "types.h"
19
+ #include "funcs.h"
20
+ #include "hash.h"
21
+ #include "text.h"
22
+
23
+ #ifdef _WIN32
24
+ #define snprintf _snprintf
25
+ #endif
26
+
27
+ enum Rulewords {
28
+ r_RULE,
29
+ r_IF,
30
+ r_AND,
31
+ r_OR,
32
+ r_THEN,
33
+ r_ELSE,
34
+ r_PRIORITY,
35
+ r_DISABLED,
36
+ r_ERROR
37
+ };
38
+ char *Ruleword[] = {w_RULE, w_IF, w_AND, w_OR,
39
+ w_THEN, w_ELSE, w_PRIORITY, w_DISABLED, NULL};
40
+
41
+ enum Varwords {
42
+ r_DEMAND,
43
+ r_HEAD,
44
+ r_GRADE,
45
+ r_LEVEL,
46
+ r_PRESSURE,
47
+ r_FLOW,
48
+ r_STATUS,
49
+ r_SETTING,
50
+ r_POWER,
51
+ r_TIME,
52
+ r_CLOCKTIME,
53
+ r_FILLTIME,
54
+ r_DRAINTIME
55
+ };
56
+ char *Varword[] = {w_DEMAND, w_HEAD, w_GRADE, w_LEVEL, w_PRESSURE,
57
+ w_FLOW, w_STATUS, w_SETTING, w_POWER, w_TIME,
58
+ w_CLOCKTIME, w_FILLTIME, w_DRAINTIME, NULL};
59
+
60
+ enum Objects {
61
+ r_JUNC,
62
+ r_RESERV,
63
+ r_TANK,
64
+ r_PIPE,
65
+ r_PUMP,
66
+ r_VALVE,
67
+ r_NODE,
68
+ r_LINK,
69
+ r_SYSTEM
70
+ };
71
+ char *Object[] = {w_JUNC, w_RESERV, w_TANK, w_PIPE, w_PUMP,
72
+ w_VALVE, w_NODE, w_LINK, w_SYSTEM, NULL};
73
+
74
+ // NOTE: place "<=" & ">=" before "<" & ">" so that findmatch() works correctly.
75
+ enum Operators { EQ, NE, LE, GE, LT, GT, IS, NOT, BELOW, ABOVE };
76
+ char *Operator[] = {"=", "<>", "<=", ">=", "<", ">",
77
+ w_IS, w_NOT, w_BELOW, w_ABOVE, NULL};
78
+
79
+ enum Values { IS_NUMBER, IS_OPEN, IS_CLOSED, IS_ACTIVE };
80
+ char *Value[] = {"XXXX", w_OPEN, w_CLOSED, w_ACTIVE, NULL};
81
+
82
+ // Local functions
83
+ static void newrule(Project *);
84
+ static int newpremise(Project *, int);
85
+ static int newaction(Project *);
86
+ static int newpriority(Project *);
87
+
88
+ static int evalpremises(Project *, int);
89
+ static int checkpremise(Project *, Spremise *);
90
+ static int checktime(Project *, Spremise *);
91
+ static int checkstatus(Project *, Spremise *);
92
+ static int checkvalue(Project *, Spremise *);
93
+
94
+ static int onactionlist(Project *, int, Saction *);
95
+ static void updateactionlist(Project *, int, Saction *);
96
+ static int takeactions(Project *);
97
+ static void clearactionlist(Rules *);
98
+ static void clearrule(Project *, int);
99
+
100
+ static void writepremise(Spremise *, FILE *, Network *);
101
+ static void writeaction(Saction *, FILE *, Network *);
102
+ static void getobjtxt(int, int, char *);
103
+ static void gettimetxt(double, char *);
104
+
105
+
106
+ void initrules(Project *pr)
107
+ //--------------------------------------------------------------
108
+ // Initializes rule base.
109
+ //--------------------------------------------------------------
110
+ {
111
+ pr->rules.RuleState = r_PRIORITY;
112
+ pr->rules.LastPremise = NULL;
113
+ pr->rules.LastThenAction = NULL;
114
+ pr->rules.LastElseAction = NULL;
115
+ pr->rules.ActionList = NULL;
116
+ pr->network.Rule = NULL;
117
+ }
118
+
119
+ void addrule(Parser *parser, char *tok)
120
+ //--------------------------------------------------------------
121
+ // Updates rule count if RULE keyword found in line of input.
122
+ //--------------------------------------------------------------
123
+ {
124
+ if (match(tok, w_RULE)) parser->MaxRules++;
125
+ }
126
+
127
+ void deleterule(Project *pr, int index)
128
+ //-----------------------------------------------------------
129
+ // Deletes a specific rule
130
+ //-----------------------------------------------------------
131
+ {
132
+ Network *net = &pr->network;
133
+
134
+ int i;
135
+ Srule *lastRule;
136
+
137
+ // Free memory allocated to rule's premises & actions
138
+ clearrule(pr, index);
139
+
140
+ // Shift position of higher indexed rules down one
141
+ for (i = index; i <= net->Nrules - 1; i++)
142
+ {
143
+ net->Rule[i] = net->Rule[i + 1];
144
+ }
145
+
146
+ // Remove premises & actions from last (inactive) entry in Rule array
147
+ lastRule = &net->Rule[net->Nrules];
148
+ lastRule->Premises = NULL;
149
+ lastRule->ThenActions = NULL;
150
+ lastRule->ElseActions = NULL;
151
+
152
+ // Reduce active rule count by one
153
+ net->Nrules--;
154
+ }
155
+
156
+ int allocrules(Project *pr)
157
+ //--------------------------------------------------------------
158
+ // Allocates memory for rule-based controls.
159
+ //--------------------------------------------------------------
160
+ {
161
+ Network *net = &pr->network;
162
+ int n = pr->parser.MaxRules + 1;
163
+
164
+ net->Rule = (Srule *)calloc(n, sizeof(Srule));
165
+ if (net->Rule == NULL) return 101;
166
+ return 0;
167
+ }
168
+
169
+ void freerules(Project *pr)
170
+ //--------------------------------------------------------------
171
+ // Frees memory used for rule-based controls.
172
+ //--------------------------------------------------------------
173
+ {
174
+ int i;
175
+
176
+ // Already freed
177
+ if (pr->network.Rule == NULL)
178
+ return;
179
+
180
+ for (i = 1; i <= pr->network.Nrules; i++) clearrule(pr, i);
181
+ free(pr->network.Rule);
182
+ pr->network.Rule = NULL;
183
+ }
184
+
185
+ int ruledata(Project *pr)
186
+ //--------------------------------------------------------------
187
+ // Parses a line from [RULES] section of input.
188
+ //--------------------------------------------------------------
189
+ {
190
+ Network *net = &pr->network;
191
+ Parser *parser = &pr->parser;
192
+ Rules *rules = &pr->rules;
193
+
194
+ int key, // Keyword code
195
+ err;
196
+ char **Tok = parser->Tok; // Tokenized line of a rule statement
197
+
198
+ // Exit if current rule has an error
199
+ if (rules->RuleState == r_ERROR) return 0;
200
+
201
+ // Find the key word that begins the rule statement
202
+ err = 0;
203
+ key = findmatch(Tok[0], Ruleword);
204
+ switch (key)
205
+ {
206
+ case -1:
207
+ err = 201; // Unrecognized keyword
208
+ break;
209
+
210
+ case r_RULE:
211
+ // Missing the rule label
212
+ if (parser->Ntokens != 2)
213
+ {
214
+ err = 201;
215
+ break;
216
+ }
217
+ net->Nrules++;
218
+ newrule(pr);
219
+ rules->RuleState = r_RULE;
220
+ rules->Errcode = 0;
221
+ break;
222
+
223
+ case r_IF:
224
+ if (rules->RuleState != r_RULE)
225
+ {
226
+ err = 221; // Mis-placed IF clause
227
+ break;
228
+ }
229
+ rules->RuleState = r_IF;
230
+ err = newpremise(pr, r_AND);
231
+ break;
232
+
233
+ case r_AND:
234
+ if (rules->RuleState == r_IF) err = newpremise(pr, r_AND);
235
+ else if (rules->RuleState == r_THEN || rules->RuleState == r_ELSE)
236
+ {
237
+ err = newaction(pr);
238
+ }
239
+ else err = 221;
240
+ break;
241
+
242
+ case r_OR:
243
+ if (rules->RuleState == r_IF) err = newpremise(pr, r_OR);
244
+ else err = 221;
245
+ break;
246
+
247
+ case r_THEN:
248
+ if (rules->RuleState != r_IF)
249
+ {
250
+ err = 221; // Mis-placed THEN clause
251
+ break;
252
+ }
253
+ rules->RuleState = r_THEN;
254
+ err = newaction(pr);
255
+ break;
256
+
257
+ case r_ELSE:
258
+ if (rules->RuleState != r_THEN)
259
+ {
260
+ err = 221; // Mis-placed ELSE clause
261
+ break;
262
+ }
263
+ rules->RuleState = r_ELSE;
264
+ err = newaction(pr);
265
+ break;
266
+
267
+ case r_PRIORITY:
268
+ if (rules->RuleState != r_THEN && rules->RuleState != r_ELSE)
269
+ {
270
+ err = 221;
271
+ break;
272
+ }
273
+ rules->RuleState = r_PRIORITY;
274
+ err = newpriority(pr);
275
+ break;
276
+
277
+ case r_DISABLED:
278
+ if (rules->RuleState != r_THEN && rules->RuleState != r_ELSE &&
279
+ rules->RuleState != r_PRIORITY)
280
+ {
281
+ err = 221;
282
+ break;
283
+ }
284
+ net->Rule[net->Nrules].isEnabled = FALSE;
285
+ break;
286
+
287
+ default:
288
+ err = 201;
289
+ }
290
+
291
+ // Set RuleState to r_ERROR if errors found
292
+ if (err)
293
+ {
294
+ rules->RuleState = r_ERROR;
295
+ rules->Errcode = err;
296
+ err = 200;
297
+ }
298
+ return err;
299
+ }
300
+
301
+ void ruleerrmsg(Project *pr)
302
+ //-----------------------------------------------------------
303
+ // Report a rule parsing error message
304
+ //-----------------------------------------------------------
305
+ {
306
+ Network *net = &pr->network;
307
+ Parser *parser = &pr->parser;
308
+ Rules *rules = &pr->rules;
309
+
310
+ int i;
311
+ char label[MAXMSG + 1];
312
+ char msg[MAXLINE + 1];
313
+ char **Tok = parser->Tok;
314
+
315
+ // Get text of error message
316
+ switch (rules->Errcode)
317
+ {
318
+ case 201: strcpy(msg, R_ERR201); break;
319
+ case 202: strcpy(msg, R_ERR202); break;
320
+ case 203: strcpy(msg, R_ERR203); break;
321
+ case 204: strcpy(msg, R_ERR204); break;
322
+ case 207: strcpy(msg, R_ERR207); break;
323
+ case 221: strcpy(msg, R_ERR221); break;
324
+ default: return;
325
+ }
326
+
327
+ // Get label of rule being parsed
328
+ if (net->Nrules > 0)
329
+ {
330
+ strncpy(label, t_RULE, MAXMSG);
331
+ strncat(label, " ", MAXMSG);
332
+ strncat(label, net->Rule[net->Nrules].label, MAXMSG);
333
+ }
334
+ else strncpy(label, t_RULES_SECT, MAXMSG);
335
+
336
+ // Write rule label and error message to status report
337
+ snprintf(pr->Msg, MAXMSG, "%s", msg);
338
+ strncat(pr->Msg, label, MAXMSG);
339
+ strncat(pr->Msg, ":", MAXMSG);
340
+ writeline(pr, pr->Msg);
341
+
342
+ // Write text of rule clause being parsed to status report
343
+ strcpy(msg, Tok[0]);
344
+ for (i = 1; i < parser->Ntokens; i++)
345
+ {
346
+ strncat(msg, " ", MAXLINE);
347
+ strncat(msg, Tok[i], MAXLINE);
348
+ }
349
+ writeline(pr, msg);
350
+ }
351
+
352
+ void adjustrules(Project *pr, int objtype, int index)
353
+ //-----------------------------------------------------------
354
+ // Adjusts rules when a specific node or link is deleted.
355
+ //-----------------------------------------------------------
356
+ {
357
+ Network *net = &pr->network;
358
+
359
+ int i, delete;
360
+ Spremise *p;
361
+ Saction *a;
362
+
363
+ // Delete rules that refer to objtype and index
364
+ for (i = net->Nrules; i >= 1; i--)
365
+ {
366
+ delete = FALSE;
367
+ p = net->Rule[i].Premises;
368
+ while (p != NULL && !delete)
369
+ {
370
+ if (objtype == p->object && p->index == index) delete = TRUE;
371
+ p = p->next;
372
+ }
373
+ if (objtype == r_LINK)
374
+ {
375
+ a = net->Rule[i].ThenActions;
376
+ while (a != NULL && !delete)
377
+ {
378
+ if (a->link == index) delete = TRUE;
379
+ a = a->next;
380
+ }
381
+ a = net->Rule[i].ElseActions;
382
+ while (a != NULL && !delete)
383
+ {
384
+ if (a->link == index) delete = TRUE;
385
+ a = a->next;
386
+ }
387
+ }
388
+ if (delete) deleterule(pr, i);
389
+ }
390
+
391
+ // Adjust all higher object indices to reflect deletion of object index
392
+ for (i = 1; i <= net->Nrules; i++)
393
+ {
394
+ p = net->Rule[i].Premises;
395
+ while (p != NULL)
396
+ {
397
+ if (objtype == p->object && p->index > index) p->index--;
398
+ p = p->next;
399
+ }
400
+ if (objtype == r_LINK)
401
+ {
402
+ a = net->Rule[i].ThenActions;
403
+ while (a != NULL)
404
+ {
405
+ if (a->link > index) a->link--;
406
+ a = a->next;
407
+ }
408
+ a = net->Rule[i].ElseActions;
409
+ while (a != NULL)
410
+ {
411
+ if (a->link > index) a->link--;
412
+ a = a->next;
413
+ }
414
+ }
415
+ }
416
+ }
417
+
418
+ void adjusttankrules(Project *pr, int ndiff)
419
+ //-----------------------------------------------------------
420
+ // Adjusts tank indices in rule premises.
421
+ //-----------------------------------------------------------
422
+ {
423
+ Network *net = &pr->network;
424
+
425
+ int i, njuncs;
426
+ Spremise *p;
427
+
428
+ njuncs = net->Njuncs;
429
+ for (i = 1; i <= net->Nrules; i++)
430
+ {
431
+ p = net->Rule[i].Premises;
432
+ while (p != NULL)
433
+ {
434
+ if (p->object == r_NODE && p->index > njuncs)
435
+ p->index += ndiff;
436
+ p = p->next;
437
+ }
438
+ }
439
+ }
440
+
441
+ Spremise *getpremise(Spremise *premises, int i)
442
+ //----------------------------------------------------------
443
+ // Return the i-th premise in a rule
444
+ //----------------------------------------------------------
445
+ {
446
+ int count = 0;
447
+ Spremise *p;
448
+
449
+ p = premises;
450
+ while (p != NULL)
451
+ {
452
+ count++;
453
+ if (count == i) break;
454
+ p = p->next;
455
+ }
456
+ return p;
457
+ }
458
+
459
+ Saction *getaction(Saction *actions, int i)
460
+ //----------------------------------------------------------
461
+ // Return the i-th action from a rule's action list
462
+ //----------------------------------------------------------
463
+ {
464
+ int count = 0;
465
+ Saction *a;
466
+
467
+ a = actions;
468
+ while (a != NULL)
469
+ {
470
+ count++;
471
+ if (count == i) break;
472
+ a = a->next;
473
+ }
474
+ return a;
475
+ }
476
+
477
+ int writerule(Project *pr, FILE *f, int ruleIndex)
478
+ //-----------------------------------------------------------------------------
479
+ // Write a rule to an INP file.
480
+ //-----------------------------------------------------------------------------
481
+ {
482
+ Network *net = &pr->network;
483
+
484
+ Srule *rule = &net->Rule[ruleIndex];
485
+ Spremise *p;
486
+ Saction *a;
487
+
488
+ // Write each premise clause to the file
489
+ p = rule->Premises;
490
+ fprintf(f, "\nIF ");
491
+ while (p != NULL)
492
+ {
493
+ writepremise(p, f, net);
494
+ p = p->next;
495
+ if (p) fprintf(f, "\n%-5s", Ruleword[p->logop]);
496
+ }
497
+
498
+ // Write each THEN action clause to the file
499
+ a = rule->ThenActions;
500
+ if (a) fprintf(f, "\nTHEN ");
501
+ while (a != NULL)
502
+ {
503
+ writeaction(a, f, net);
504
+ a = a->next;
505
+ if (a) fprintf(f, "\nAND ");
506
+ }
507
+
508
+ // Write each ELSE action clause to the file
509
+ a = rule->ElseActions;
510
+ if (a) fprintf(f, "\nELSE ");
511
+ while (a != NULL)
512
+ {
513
+ writeaction(a, f, net);
514
+ a = a->next;
515
+ if (a) fprintf(f, "\nAND ");
516
+ }
517
+
518
+ // Write the rule's priority to the file
519
+ if (rule->priority > 0) fprintf(f, "\nPRIORITY %f", rule->priority);
520
+ return 0;
521
+ }
522
+
523
+ int checkrules(Project *pr, long dt)
524
+ //-----------------------------------------------------
525
+ // Checks which rules should fire at current time.
526
+ //-----------------------------------------------------
527
+ {
528
+ Network *net = &pr->network;
529
+ Times *time = &pr->times;
530
+ Rules *rules = &pr->rules;
531
+
532
+ int i;
533
+ int actionCount = 0; // Number of actions actually taken
534
+
535
+ // Start of rule evaluation time interval
536
+ rules->Time1 = time->Htime - dt + 1;
537
+
538
+ // Iterate through each rule
539
+ rules->ActionList = NULL;
540
+ for (i = 1; i <= net->Nrules; i++)
541
+ {
542
+ // skip if the rule is disabled
543
+ if (!net->Rule[i].isEnabled)
544
+ {
545
+ continue;
546
+ }
547
+ // If premises true, add THEN clauses to action list
548
+ if (evalpremises(pr, i) == TRUE)
549
+ {
550
+ updateactionlist(pr, i, net->Rule[i].ThenActions);
551
+ }
552
+
553
+ // If premises false, add ELSE actions to list
554
+ else
555
+ {
556
+ if (net->Rule[i].ElseActions != NULL)
557
+ {
558
+ updateactionlist(pr, i, net->Rule[i].ElseActions);
559
+ }
560
+ }
561
+ }
562
+
563
+ // Execute actions then clear action list
564
+ if (rules->ActionList != NULL) actionCount = takeactions(pr);
565
+ clearactionlist(rules);
566
+ return actionCount;
567
+ }
568
+
569
+ void updateruleunits(Project *pr, double dcf, double pcf, double hcf, double qcf)
570
+ //-----------------------------------------------------------
571
+ // Updates the units of a rule's premises and actions.
572
+ //-----------------------------------------------------------
573
+ {
574
+ Network *net = &pr->network;
575
+ Slink *Link = net->Link;
576
+
577
+ int i, k;
578
+ double x;
579
+ Spremise *p;
580
+ Saction *a;
581
+
582
+ for (i = 1; i <= net->Nrules; i++)
583
+ {
584
+ p = net->Rule[i].Premises;
585
+ while (p != NULL)
586
+ {
587
+
588
+ switch (p->variable)
589
+ {
590
+ case r_DEMAND:
591
+ p->value *= dcf;
592
+ break;
593
+
594
+ case r_HEAD:
595
+ case r_GRADE:
596
+ p->value *= hcf;
597
+ break;
598
+
599
+ case r_PRESSURE:
600
+ p->value *= pcf;
601
+ break;
602
+
603
+ case r_LEVEL:
604
+ p->value *= hcf;
605
+ break;
606
+
607
+ case r_FLOW:
608
+ p->value *= qcf;
609
+ break;
610
+
611
+ case r_SETTING:
612
+
613
+ switch (Link[p->index].Type)
614
+ {
615
+ case PRV:
616
+ case PSV:
617
+ case PBV:
618
+ p->value *= pcf;
619
+ break;
620
+ case FCV:
621
+ p->value *= qcf;
622
+ break;
623
+ default:
624
+ break;
625
+ }
626
+ break;
627
+
628
+ default:
629
+ break;
630
+
631
+ }
632
+ p = p->next;
633
+ }
634
+
635
+ a = net->Rule[i].ThenActions;
636
+ while (a != NULL)
637
+ {
638
+ k = a->link;
639
+ x = a->setting;
640
+
641
+ // Change link's setting
642
+ if (x != MISSING)
643
+ {
644
+ switch (net->Link[k].Type)
645
+ {
646
+ case PRV:
647
+ case PSV:
648
+ case PBV:
649
+ a->setting *= pcf;
650
+ break;
651
+ case FCV:
652
+ a->setting *= qcf;
653
+ break;
654
+ default:
655
+ break;
656
+ }
657
+ }
658
+ a = a->next;
659
+ }
660
+ a = net->Rule[i].ElseActions;
661
+ while (a != NULL)
662
+ {
663
+ k = a->link;
664
+ x = a->setting;
665
+
666
+ // Change link's setting
667
+ if (x != MISSING)
668
+ {
669
+ switch (net->Link[k].Type)
670
+ {
671
+ case PRV:
672
+ case PSV:
673
+ case PBV:
674
+ a->setting *= pcf;
675
+ break;
676
+ case FCV:
677
+ a->setting *= qcf;
678
+ break;
679
+ default:
680
+ break;
681
+ }
682
+ }
683
+ a = a->next;
684
+ }
685
+ }
686
+ }
687
+
688
+
689
+ void newrule(Project *pr)
690
+ //----------------------------------------------------------
691
+ // Adds a new rule to the project
692
+ //----------------------------------------------------------
693
+ {
694
+ Network *net = &pr->network;
695
+
696
+ char **Tok = pr->parser.Tok;
697
+ Srule *rule = &net->Rule[net->Nrules];
698
+
699
+ strncpy(rule->label, Tok[1], MAXID);
700
+ rule->Premises = NULL;
701
+ rule->ThenActions = NULL;
702
+ rule->ElseActions = NULL;
703
+ rule->priority = 0.0;
704
+ rule->isEnabled = TRUE;
705
+ pr->rules.LastPremise = NULL;
706
+ pr->rules.LastThenAction = NULL;
707
+ pr->rules.LastElseAction = NULL;
708
+ }
709
+
710
+ int newpremise(Project *pr, int logop)
711
+ //--------------------------------------------------------------------
712
+ // Adds new premise to current rule.
713
+ // Formats are:
714
+ // IF/AND/OR <object> <id> <variable> <operator> <value>
715
+ // IF/AND/OR SYSTEM <variable> <operator> <value> (units)
716
+ //---------------------------------------------------------------------
717
+ {
718
+ Network *net = &pr->network;
719
+ Parser *parser = &pr->parser;
720
+ Rules *rules = &pr->rules;
721
+
722
+ int i, j, k, m, r, s, v;
723
+ double x;
724
+ char **Tok = parser->Tok;
725
+ Spremise *p;
726
+
727
+ // Check for correct number of tokens
728
+ if (parser->Ntokens != 5 && parser->Ntokens != 6) return 201;
729
+
730
+ // Find network object & id if present
731
+ i = findmatch(Tok[1], Object);
732
+ if (i == r_SYSTEM)
733
+ {
734
+ j = 0;
735
+ v = findmatch(Tok[2], Varword);
736
+ if (v != r_DEMAND && v != r_TIME && v != r_CLOCKTIME) return 201;
737
+ }
738
+ else
739
+ {
740
+ v = findmatch(Tok[3], Varword);
741
+ if (v < 0) return (201);
742
+ switch (i)
743
+ {
744
+ case r_NODE:
745
+ case r_JUNC:
746
+ case r_RESERV:
747
+ case r_TANK:
748
+ k = r_NODE;
749
+ break;
750
+ case r_LINK:
751
+ case r_PIPE:
752
+ case r_PUMP:
753
+ case r_VALVE:
754
+ k = r_LINK;
755
+ break;
756
+ default:
757
+ return 201;
758
+ }
759
+ i = k;
760
+ if (i == r_NODE)
761
+ {
762
+ j = findnode(net, Tok[2]);
763
+ if (j == 0) return 203;
764
+ switch (v)
765
+ {
766
+ case r_DEMAND:
767
+ case r_HEAD:
768
+ case r_GRADE:
769
+ case r_LEVEL:
770
+ case r_PRESSURE:
771
+ break;
772
+ case r_FILLTIME:
773
+ case r_DRAINTIME:
774
+ if (j <= net->Njuncs) return 201;
775
+ break;
776
+ default:
777
+ return 201;
778
+ }
779
+ }
780
+ else
781
+ {
782
+ j = findlink(net, Tok[2]);
783
+ if (j == 0) return 204;
784
+ switch (v)
785
+ {
786
+ case r_FLOW:
787
+ case r_STATUS:
788
+ case r_SETTING:
789
+ break;
790
+ default:
791
+ return 201;
792
+ }
793
+ }
794
+ }
795
+
796
+ // Parse relational operator (r) and check for synonyms
797
+ if (i == r_SYSTEM) m = 3;
798
+ else m = 4;
799
+ k = findmatch(Tok[m], Operator);
800
+ if (k < 0) return 201;
801
+ switch (k)
802
+ {
803
+ case IS:
804
+ r = EQ;
805
+ break;
806
+ case NOT:
807
+ r = NE;
808
+ break;
809
+ case BELOW:
810
+ r = LT;
811
+ break;
812
+ case ABOVE:
813
+ r = GT;
814
+ break;
815
+ default:
816
+ r = k;
817
+ }
818
+
819
+ // Parse for status (s) or numerical value (x)
820
+ s = 0;
821
+ x = MISSING;
822
+ if (v == r_TIME || v == r_CLOCKTIME)
823
+ {
824
+ if (parser->Ntokens == 6) x = hour(Tok[4], Tok[5]) * 3600.;
825
+ else x = hour(Tok[4], "") * 3600.;
826
+ if (x < 0.0) return 202;
827
+ }
828
+ else if ((k = findmatch(Tok[parser->Ntokens - 1], Value)) > IS_NUMBER) s = k;
829
+ else
830
+ {
831
+ if (!getfloat(Tok[parser->Ntokens - 1], &x))
832
+ return (202);
833
+ if (v == r_FILLTIME || v == r_DRAINTIME) x = x * 3600.0;
834
+ }
835
+
836
+ // Create new premise structure
837
+ p = (Spremise *)malloc(sizeof(Spremise));
838
+ if (p == NULL) return 101;
839
+ p->object = i;
840
+ p->index = j;
841
+ p->variable = v;
842
+ p->relop = r;
843
+ p->logop = logop;
844
+ p->status = s;
845
+ p->value = x;
846
+
847
+ // Add premise to current rule's premise list
848
+ p->next = NULL;
849
+ if (rules->LastPremise == NULL) net->Rule[net->Nrules].Premises = p;
850
+ else rules->LastPremise->next = p;
851
+ rules->LastPremise = p;
852
+ return 0;
853
+ }
854
+
855
+ int newaction(Project *pr)
856
+ //----------------------------------------------------------
857
+ // Adds new action to current rule.
858
+ // Format is:
859
+ // THEN/ELSE/AND LINK <id> <variable> IS <value>
860
+ //----------------------------------------------------------
861
+ {
862
+ Network *net = &pr->network;
863
+ Parser *parser = &pr->parser;
864
+ Rules *rules = &pr->rules;
865
+
866
+ int j, k, s;
867
+ double x;
868
+ Saction *a;
869
+ char **Tok = parser->Tok;
870
+
871
+ // Check for correct number of tokens
872
+ if (parser->Ntokens != 6) return 201;
873
+
874
+ // Check that link exists
875
+ j = findlink(net, Tok[2]);
876
+ if (j == 0) return 204;
877
+
878
+ // Cannot control a CV
879
+ if (net->Link[j].Type == CVPIPE) return 207;
880
+
881
+ // Find value for status or setting
882
+ s = -1;
883
+ x = MISSING;
884
+ if ((k = findmatch(Tok[5], Value)) > IS_NUMBER) s = k;
885
+ else
886
+ {
887
+ if (!getfloat(Tok[5], &x)) return 202;
888
+ if (x < 0.0) return 202;
889
+ }
890
+
891
+ // Cannot change setting for a GPV
892
+ if (x != MISSING && net->Link[j].Type == GPV) return 202;
893
+
894
+ // Set status for pipe in case setting was specified
895
+ if (x != MISSING && net->Link[j].Type == PIPE)
896
+ {
897
+ if (x == 0.0) s = IS_CLOSED;
898
+ else s = IS_OPEN;
899
+ x = MISSING;
900
+ }
901
+
902
+ // Create a new action structure
903
+ a = (Saction *)malloc(sizeof(Saction));
904
+ if (a == NULL) return 101;
905
+ a->link = j;
906
+ a->status = s;
907
+ a->setting = x;
908
+
909
+ // Add action to current rule's action list
910
+ if (rules->RuleState == r_THEN)
911
+ {
912
+ a->next = NULL;
913
+ if (rules->LastThenAction == NULL)
914
+ {
915
+ net->Rule[net->Nrules].ThenActions = a;
916
+ }
917
+ else rules->LastThenAction->next = a;
918
+ rules->LastThenAction = a;
919
+ }
920
+ else
921
+ {
922
+ a->next = NULL;
923
+ if (rules->LastElseAction == NULL)
924
+ {
925
+ net->Rule[net->Nrules].ElseActions = a;
926
+ }
927
+ else rules->LastElseAction->next = a;
928
+ rules->LastElseAction = a;
929
+ }
930
+ return 0;
931
+ }
932
+
933
+ int newpriority(Project *pr)
934
+ //---------------------------------------------------
935
+ // Adds priority rating to current rule
936
+ //---------------------------------------------------
937
+ {
938
+ Network *net = &pr->network;
939
+
940
+ double x;
941
+ char **Tok = pr->parser.Tok;
942
+
943
+ if (!getfloat(Tok[1], &x)) return 202;
944
+ net->Rule[net->Nrules].priority = x;
945
+ return 0;
946
+ }
947
+
948
+ int evalpremises(Project *pr, int i)
949
+ //----------------------------------------------------------
950
+ // Checks if premises to rule i are true
951
+ //----------------------------------------------------------
952
+ {
953
+ Network *net = &pr->network;
954
+
955
+ int result;
956
+ Spremise *p;
957
+
958
+ result = TRUE;
959
+ p = net->Rule[i].Premises;
960
+ while (p != NULL)
961
+ {
962
+ if (p->logop == r_OR)
963
+ {
964
+ if (result == FALSE) result = checkpremise(pr, p);
965
+ }
966
+ else
967
+ {
968
+ if (result == FALSE) return (FALSE);
969
+ result = checkpremise(pr, p);
970
+ }
971
+ p = p->next;
972
+ }
973
+ return result;
974
+ }
975
+
976
+ int checkpremise(Project *pr, Spremise *p)
977
+ //----------------------------------------------------------
978
+ // Checks if a particular premise is true
979
+ //----------------------------------------------------------
980
+ {
981
+ if (p->variable == r_TIME ||
982
+ p->variable == r_CLOCKTIME) return (checktime(pr,p));
983
+ else if (p->status > IS_NUMBER) return (checkstatus(pr,p));
984
+ else return (checkvalue(pr,p));
985
+ }
986
+
987
+ int checktime(Project *pr, Spremise *p)
988
+ //------------------------------------------------------------
989
+ // Checks if condition on system time holds
990
+ //------------------------------------------------------------
991
+ {
992
+ Times *time = &pr->times;
993
+ Rules *rules = &pr->rules;
994
+
995
+ char flag;
996
+ long t1, t2, x;
997
+
998
+ // Get start and end of rule evaluation time interval
999
+ if (p->variable == r_TIME)
1000
+ {
1001
+ t1 = rules->Time1;
1002
+ t2 = time->Htime;
1003
+ }
1004
+ else if (p->variable == r_CLOCKTIME)
1005
+ {
1006
+ t1 = (rules->Time1 + time->Tstart) % SECperDAY;
1007
+ t2 = (time->Htime + time->Tstart) % SECperDAY;
1008
+ }
1009
+ else return (0);
1010
+
1011
+ // Test premise's time
1012
+ x = (long)(p->value);
1013
+ switch (p->relop)
1014
+ {
1015
+ // For inequality, test against current time
1016
+ case LT:
1017
+ if (t2 >= x) return (0);
1018
+ break;
1019
+ case LE:
1020
+ if (t2 > x) return (0);
1021
+ break;
1022
+ case GT:
1023
+ if (t2 <= x) return (0);
1024
+ break;
1025
+ case GE:
1026
+ if (t2 < x) return (0);
1027
+ break;
1028
+
1029
+ // For equality, test if within interval
1030
+ case EQ:
1031
+ case NE:
1032
+ flag = FALSE;
1033
+ if (t2 < t1) // E.g., 11:00 am to 1:00 am
1034
+ {
1035
+ if (x >= t1 || x <= t2)
1036
+ flag = TRUE;
1037
+ }
1038
+ else
1039
+ {
1040
+ if (x >= t1 && x <= t2)
1041
+ flag = TRUE;
1042
+ }
1043
+ if (p->relop == EQ && flag == FALSE) return (0);
1044
+ if (p->relop == NE && flag == TRUE) return (0);
1045
+ break;
1046
+ }
1047
+
1048
+ // If we get to here then premise was satisfied
1049
+ return 1;
1050
+ }
1051
+
1052
+ int checkstatus(Project *pr, Spremise *p)
1053
+ //------------------------------------------------------------
1054
+ // Checks if condition on link status holds
1055
+ //------------------------------------------------------------
1056
+ {
1057
+ Hydraul *hyd = &pr->hydraul;
1058
+
1059
+ char i;
1060
+ int j;
1061
+
1062
+ switch (p->status)
1063
+ {
1064
+ case IS_OPEN:
1065
+ case IS_CLOSED:
1066
+ case IS_ACTIVE:
1067
+ i = hyd->LinkStatus[p->index];
1068
+ if (i <= CLOSED) j = IS_CLOSED;
1069
+ else if (i == ACTIVE) j = IS_ACTIVE;
1070
+ else j = IS_OPEN;
1071
+ if (j == p->status && p->relop == EQ) return 1;
1072
+ if (j != p->status && p->relop == NE) return 1;
1073
+ }
1074
+ return 0;
1075
+ }
1076
+
1077
+ int checkvalue(Project *pr, Spremise *p)
1078
+ //----------------------------------------------------------
1079
+ // Checks if numerical condition on a variable is true.
1080
+ // Uses tolerance of 0.001 when testing conditions.
1081
+ //----------------------------------------------------------
1082
+ {
1083
+ Network *net = &pr->network;
1084
+ Hydraul *hyd = &pr->hydraul;
1085
+
1086
+ int i, j, v;
1087
+ double x, // A variable's value
1088
+ tol = 1.e-3; // Equality tolerance
1089
+ int Njuncs = net->Njuncs;
1090
+ double *Ucf = pr->Ucf;
1091
+ double *NodeDemand = hyd->NodeDemand;
1092
+ double *LinkFlow = hyd->LinkFlow;
1093
+ double *LinkSetting = hyd->LinkSetting;
1094
+ Snode *Node = net->Node;
1095
+ Slink *Link = net->Link;
1096
+ Stank *Tank = net->Tank;
1097
+
1098
+ // Find the value being checked
1099
+ i = p->index;
1100
+ v = p->variable;
1101
+ switch (v)
1102
+ {
1103
+ case r_DEMAND:
1104
+ if (p->object == r_SYSTEM) x = hyd->Dsystem * Ucf[DEMAND];
1105
+ else x = NodeDemand[i] * Ucf[DEMAND];
1106
+ break;
1107
+
1108
+ case r_HEAD:
1109
+ case r_GRADE:
1110
+ x = hyd->NodeHead[i] * Ucf[HEAD];
1111
+ break;
1112
+
1113
+ case r_PRESSURE:
1114
+ x = (hyd->NodeHead[i] - Node[i].El) * Ucf[PRESSURE];
1115
+ break;
1116
+
1117
+ case r_LEVEL:
1118
+ x = (hyd->NodeHead[i] - Node[i].El) * Ucf[HEAD];
1119
+ break;
1120
+
1121
+ case r_FLOW:
1122
+ x = ABS(LinkFlow[i]) * Ucf[FLOW];
1123
+ break;
1124
+
1125
+ case r_SETTING:
1126
+ if (LinkSetting[i] == MISSING) return 0;
1127
+ x = LinkSetting[i];
1128
+ switch (Link[i].Type)
1129
+ {
1130
+ case PRV:
1131
+ case PSV:
1132
+ case PBV:
1133
+ x = x * Ucf[PRESSURE];
1134
+ break;
1135
+ case FCV:
1136
+ x = x * Ucf[FLOW];
1137
+ break;
1138
+ default:
1139
+ break;
1140
+ }
1141
+ break;
1142
+
1143
+ case r_FILLTIME:
1144
+ if (i <= Njuncs) return 0;
1145
+ j = i - Njuncs;
1146
+ if (Tank[j].A == 0.0) return 0;
1147
+ if (NodeDemand[i] <= TINY) return 0;
1148
+ x = (Tank[j].Vmax - Tank[j].V) / NodeDemand[i];
1149
+ break;
1150
+
1151
+ case r_DRAINTIME:
1152
+ if (i <= Njuncs) return 0;
1153
+ j = i - Njuncs;
1154
+ if (Tank[j].A == 0.0) return 0;
1155
+ if (NodeDemand[i] >= -TINY) return 0;
1156
+ x = (Tank[j].Vmin - Tank[j].V) / NodeDemand[i];
1157
+ break;
1158
+
1159
+ default:
1160
+ return 0;
1161
+ }
1162
+
1163
+ // Compare value x against the premise
1164
+ switch (p->relop)
1165
+ {
1166
+ case EQ: if (ABS(x - p->value) > tol) return 0; break;
1167
+ case NE: if (ABS(x - p->value) < tol) return 0; break;
1168
+ case LT: if (x > p->value + tol) return 0; break;
1169
+ case LE: if (x > p->value - tol) return 0; break;
1170
+ case GT: if (x < p->value - tol) return 0; break;
1171
+ case GE: if (x < p->value + tol) return 0; break;
1172
+ }
1173
+ return 1;
1174
+ }
1175
+
1176
+ void updateactionlist(Project *pr, int i, Saction *actions)
1177
+ //---------------------------------------------------
1178
+ // Adds rule's actions to action list
1179
+ //--------------------------------------------------
1180
+ {
1181
+ Rules *rules = &pr->rules;
1182
+
1183
+ SactionList *actionItem;
1184
+ Saction *a;
1185
+
1186
+ // Iterate through each action of Rule i
1187
+ a = actions;
1188
+ while (a != NULL)
1189
+ {
1190
+ // Add action to list if its link not already on it
1191
+ if (!onactionlist(pr, i, a))
1192
+ {
1193
+ actionItem = (SactionList *)malloc(sizeof(SactionList));
1194
+ if (actionItem != NULL)
1195
+ {
1196
+ actionItem->action = a;
1197
+ actionItem->ruleIndex = i;
1198
+ actionItem->next = rules->ActionList;
1199
+ rules->ActionList = actionItem;
1200
+ }
1201
+ }
1202
+ a = a->next;
1203
+ }
1204
+ }
1205
+
1206
+ int onactionlist(Project *pr, int i, Saction *a)
1207
+ //-----------------------------------------------------------------------------
1208
+ // Checks if action a from rule i can be added to the action list
1209
+ //-----------------------------------------------------------------------------
1210
+ {
1211
+ Network *net = &pr->network;
1212
+
1213
+ int link, i1;
1214
+ SactionList *actionItem;
1215
+ Saction *a1;
1216
+
1217
+ // Search action list for link included in action a
1218
+ link = a->link;
1219
+ actionItem = pr->rules.ActionList;
1220
+ while (actionItem != NULL)
1221
+ {
1222
+ a1 = actionItem->action;
1223
+ i1 = actionItem->ruleIndex;
1224
+
1225
+ // Link appears in list
1226
+ if (link == a1->link)
1227
+ {
1228
+ // Replace its action with 'a' if rule i has higher priority
1229
+ if (net->Rule[i].priority > net->Rule[i1].priority)
1230
+ {
1231
+ actionItem->action = a;
1232
+ actionItem->ruleIndex = i;
1233
+ }
1234
+
1235
+ // Return indicating that 'a' should not be added to action list
1236
+ return 1;
1237
+ }
1238
+ actionItem = actionItem->next;
1239
+ }
1240
+
1241
+ // Return indicating that it's ok to add 'a' to the action list
1242
+ return 0;
1243
+ }
1244
+
1245
+ int takeactions(Project *pr)
1246
+ //-----------------------------------------------------------
1247
+ // Implements actions on action list
1248
+ //-----------------------------------------------------------
1249
+ {
1250
+ Network *net = &pr->network;
1251
+ Hydraul *hyd = &pr->hydraul;
1252
+ Report *rpt = &pr->report;
1253
+ Rules *rules = &pr->rules;
1254
+
1255
+ char flag;
1256
+ int k, s, n;
1257
+ double tol = 1.e-3, v, x;
1258
+ Saction *a;
1259
+ SactionList *actionItem;
1260
+
1261
+ n = 0;
1262
+ actionItem = rules->ActionList;
1263
+ while (actionItem != NULL)
1264
+ {
1265
+ flag = FALSE;
1266
+ a = actionItem->action;
1267
+ k = a->link;
1268
+ s = hyd->LinkStatus[k];
1269
+ v = hyd->LinkSetting[k];
1270
+ x = a->setting;
1271
+
1272
+ // Switch link from closed to open
1273
+ if (a->status == IS_OPEN && s <= CLOSED)
1274
+ {
1275
+ setlinkstatus(pr, k, 1, &hyd->LinkStatus[k], &hyd->LinkSetting[k]);
1276
+ flag = TRUE;
1277
+ }
1278
+
1279
+ // Switch link from not closed to closed
1280
+ else if (a->status == IS_CLOSED && s > CLOSED)
1281
+ {
1282
+ setlinkstatus(pr, k, 0, &hyd->LinkStatus[k], &hyd->LinkSetting[k]);
1283
+ flag = TRUE;
1284
+ }
1285
+
1286
+ // Change link's setting
1287
+ else if (x != MISSING)
1288
+ {
1289
+ switch (net->Link[k].Type)
1290
+ {
1291
+ case PRV:
1292
+ case PSV:
1293
+ case PBV:
1294
+ x = x / pr->Ucf[PRESSURE];
1295
+ break;
1296
+ case FCV:
1297
+ x = x / pr->Ucf[FLOW];
1298
+ break;
1299
+ default:
1300
+ break;
1301
+ }
1302
+ if (ABS(x - v) > tol)
1303
+ {
1304
+ setlinksetting(pr, k, x, &hyd->LinkStatus[k],
1305
+ &hyd->LinkSetting[k]);
1306
+ flag = TRUE;
1307
+ }
1308
+ }
1309
+
1310
+ // Report rule action
1311
+ if (flag == TRUE)
1312
+ {
1313
+ n++;
1314
+ if (rpt->Statflag)
1315
+ {
1316
+ writeruleaction(pr, k, net->Rule[actionItem->ruleIndex].label);
1317
+ }
1318
+ }
1319
+
1320
+ // Move to next action on list
1321
+ actionItem = actionItem->next;
1322
+ }
1323
+ return n;
1324
+ }
1325
+
1326
+ void clearactionlist(Rules *rules)
1327
+ //----------------------------------------------------------
1328
+ // Clears memory used for action list
1329
+ //----------------------------------------------------------
1330
+ {
1331
+ SactionList *nextItem;
1332
+ SactionList *actionItem;
1333
+ actionItem = rules->ActionList;
1334
+ while (actionItem != NULL)
1335
+ {
1336
+ nextItem = actionItem->next;
1337
+ free(actionItem);
1338
+ actionItem = nextItem;
1339
+ }
1340
+ }
1341
+
1342
+ void clearrule(Project *pr, int i)
1343
+ //-----------------------------------------------------------
1344
+ // Clears memory used by a rule for premises & actions
1345
+ //-----------------------------------------------------------
1346
+ {
1347
+ Network *net = &pr->network;
1348
+
1349
+ Spremise *p;
1350
+ Spremise *pnext;
1351
+ Saction *a;
1352
+ Saction *anext;
1353
+
1354
+ p = net->Rule[i].Premises;
1355
+ while (p != NULL)
1356
+ {
1357
+ pnext = p->next;
1358
+ free(p);
1359
+ p = pnext;
1360
+ }
1361
+ a = net->Rule[i].ThenActions;
1362
+ while (a != NULL)
1363
+ {
1364
+ anext = a->next;
1365
+ free(a);
1366
+ a = anext;
1367
+ }
1368
+ a = net->Rule[i].ElseActions;
1369
+ while (a != NULL)
1370
+ {
1371
+ anext = a->next;
1372
+ free(a);
1373
+ a = anext;
1374
+ }
1375
+ }
1376
+
1377
+
1378
+ void writepremise(Spremise *p, FILE *f, Network *net)
1379
+ //-----------------------------------------------------------------------------
1380
+ // Write a rule's premise clause to an INP file.
1381
+ //-----------------------------------------------------------------------------
1382
+ {
1383
+ char s_obj[20];
1384
+ char s_id[MAXID + 1];
1385
+ char s_value[20];
1386
+ int subtype;
1387
+
1388
+ // Get the type name & ID of object referred to in the premise
1389
+ if (p->object == r_NODE)
1390
+ {
1391
+ subtype = net->Node[p->index].Type;
1392
+ getobjtxt(r_NODE, subtype, s_obj);
1393
+ strcpy(s_id, net->Node[p->index].ID);
1394
+ }
1395
+ else if (p->object == r_LINK)
1396
+ {
1397
+ subtype = net->Link[p->index].Type;
1398
+ getobjtxt(r_LINK, subtype, s_obj);
1399
+ strcpy(s_id, net->Link[p->index].ID);
1400
+ }
1401
+ else
1402
+ {
1403
+ strcpy(s_obj, "SYSTEM");
1404
+ strcpy(s_id, "");
1405
+ }
1406
+
1407
+ // If premise has no value field, use its status field as a value
1408
+ if (p->value == MISSING) strcpy(s_value, Value[p->status]);
1409
+
1410
+ // Otherwise get text of premise's value field
1411
+ else
1412
+ {
1413
+ // For time values convert from seconds to hr:min:sec
1414
+ switch (p->variable)
1415
+ {
1416
+ case r_CLOCKTIME:
1417
+ case r_DRAINTIME:
1418
+ case r_FILLTIME:
1419
+ case r_TIME:
1420
+ gettimetxt(p->value, s_value);
1421
+ break;
1422
+ default: sprintf(s_value, "%.4f", p->value);
1423
+ }
1424
+ }
1425
+
1426
+ // Write the premise clause to the file
1427
+ fprintf(f, "%s %s %s %s %s", s_obj, s_id, Varword[p->variable],
1428
+ Operator[p->relop], s_value);
1429
+ }
1430
+
1431
+ void writeaction(Saction *a, FILE *f, Network *net)
1432
+ //-----------------------------------------------------------------------------
1433
+ // Write a rule's action clause to an INP file.
1434
+ //-----------------------------------------------------------------------------
1435
+ {
1436
+ char s_id[MAXID + 1];
1437
+ char s_obj[20];
1438
+ char s_var[20];
1439
+ char s_value[20];
1440
+ int subtype;
1441
+
1442
+ subtype = net->Link[a->link].Type;
1443
+ getobjtxt(r_LINK, subtype, s_obj);
1444
+ strcpy(s_id, net->Link[a->link].ID);
1445
+ if (a->setting == MISSING)
1446
+ {
1447
+ strcpy(s_var, "STATUS");
1448
+ strcpy(s_value, Value[a->status]);
1449
+ }
1450
+ else
1451
+ {
1452
+ strcpy(s_var, "SETTING");
1453
+ sprintf(s_value, "%.4f", a->setting);
1454
+ }
1455
+ fprintf(f, "%s %s %s = %s", s_obj, s_id, s_var, s_value);
1456
+ }
1457
+
1458
+ void getobjtxt(int objtype, int subtype, char *objtxt)
1459
+ //-----------------------------------------------------------------------------
1460
+ // Retrieve the text label for a specific type of object.
1461
+ //-----------------------------------------------------------------------------
1462
+ {
1463
+ if (objtype == r_NODE)
1464
+ {
1465
+ switch (subtype)
1466
+ {
1467
+ case JUNCTION: strcpy(objtxt, "JUNCTION"); break;
1468
+ case RESERVOIR: strcpy(objtxt, "RESERVOIR"); break;
1469
+ case TANK: strcpy(objtxt, "TANK"); break;
1470
+ default: strcpy(objtxt, "NODE");
1471
+ }
1472
+ }
1473
+ else if (objtype == r_LINK)
1474
+ {
1475
+ switch (subtype)
1476
+ {
1477
+ case CVPIPE:
1478
+ case PIPE: strcpy(objtxt, "PIPE"); break;
1479
+ case PUMP: strcpy(objtxt, "PUMP"); break;
1480
+ default: strcpy(objtxt, "VALVE");
1481
+ }
1482
+ }
1483
+ else strcpy(objtxt, "SYSTEM");
1484
+ }
1485
+
1486
+ void gettimetxt(double secs, char *timetxt)
1487
+ //-----------------------------------------------------------------------------
1488
+ // Convert number of seconds to a text string in hrs:min:sec format.
1489
+ //-----------------------------------------------------------------------------
1490
+ {
1491
+ int hours = 0, minutes = 0, seconds = 0;
1492
+ hours = (int)secs / 3600;
1493
+ if (hours > 24 * 7) sprintf(timetxt, "%.4f", secs / 3600.0);
1494
+ else
1495
+ {
1496
+ minutes = (int)((secs - 3600 * hours) / 60);
1497
+ seconds = (int)(secs - 3600 * hours - minutes * 60);
1498
+ sprintf(timetxt, "%d:%02d:%02d", hours, minutes, seconds);
1499
+ }
1500
+ }