epyt-flow 0.14.0__py3-none-any.whl → 0.14.1__py3-none-any.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.
Files changed (60) hide show
  1. epyt_flow/EPANET/EPANET/SRC_engines/AUTHORS +40 -8
  2. epyt_flow/EPANET/EPANET/SRC_engines/LICENSE +3 -3
  3. epyt_flow/EPANET/EPANET/SRC_engines/enumstxt.h +24 -7
  4. epyt_flow/EPANET/EPANET/SRC_engines/epanet.c +726 -374
  5. epyt_flow/EPANET/EPANET/SRC_engines/epanet2.c +128 -32
  6. epyt_flow/EPANET/EPANET/SRC_engines/errors.dat +7 -1
  7. epyt_flow/EPANET/EPANET/SRC_engines/flowbalance.c +186 -0
  8. epyt_flow/EPANET/EPANET/SRC_engines/funcs.h +40 -14
  9. epyt_flow/EPANET/EPANET/SRC_engines/hash.c +177 -177
  10. epyt_flow/EPANET/EPANET/SRC_engines/hash.h +28 -28
  11. epyt_flow/EPANET/EPANET/SRC_engines/hydcoeffs.c +192 -40
  12. epyt_flow/EPANET/EPANET/SRC_engines/hydraul.c +101 -46
  13. epyt_flow/EPANET/EPANET/SRC_engines/hydsolver.c +85 -24
  14. epyt_flow/EPANET/EPANET/SRC_engines/hydstatus.c +29 -63
  15. epyt_flow/EPANET/EPANET/SRC_engines/include/epanet2.h +70 -37
  16. epyt_flow/EPANET/EPANET/SRC_engines/include/epanet2_2.h +408 -234
  17. epyt_flow/EPANET/EPANET/SRC_engines/include/epanet2_enums.h +87 -37
  18. epyt_flow/EPANET/EPANET/SRC_engines/inpfile.c +153 -79
  19. epyt_flow/EPANET/EPANET/SRC_engines/input1.c +59 -94
  20. epyt_flow/EPANET/EPANET/SRC_engines/input2.c +73 -202
  21. epyt_flow/EPANET/EPANET/SRC_engines/input3.c +446 -351
  22. epyt_flow/EPANET/EPANET/SRC_engines/leakage.c +527 -0
  23. epyt_flow/EPANET/EPANET/SRC_engines/mempool.c +8 -4
  24. epyt_flow/EPANET/EPANET/SRC_engines/mempool.h +23 -23
  25. epyt_flow/EPANET/EPANET/SRC_engines/output.c +5 -4
  26. epyt_flow/EPANET/EPANET/SRC_engines/project.c +407 -75
  27. epyt_flow/EPANET/EPANET/SRC_engines/quality.c +12 -2
  28. epyt_flow/EPANET/EPANET/SRC_engines/qualreact.c +70 -13
  29. epyt_flow/EPANET/EPANET/SRC_engines/qualroute.c +7 -5
  30. epyt_flow/EPANET/EPANET/SRC_engines/report.c +88 -20
  31. epyt_flow/EPANET/EPANET/SRC_engines/rules.c +144 -6
  32. epyt_flow/EPANET/EPANET/SRC_engines/smatrix.c +19 -19
  33. epyt_flow/EPANET/EPANET/SRC_engines/text.h +16 -5
  34. epyt_flow/EPANET/EPANET/SRC_engines/types.h +73 -19
  35. epyt_flow/EPANET/EPANET/SRC_engines/util/cstr_helper.c +59 -0
  36. epyt_flow/EPANET/EPANET/SRC_engines/util/cstr_helper.h +38 -0
  37. epyt_flow/EPANET/EPANET/SRC_engines/util/errormanager.c +92 -0
  38. epyt_flow/EPANET/EPANET/SRC_engines/util/errormanager.h +39 -0
  39. epyt_flow/EPANET/EPANET/SRC_engines/util/filemanager.c +212 -0
  40. epyt_flow/EPANET/EPANET/SRC_engines/util/filemanager.h +81 -0
  41. epyt_flow/EPANET/EPANET/SRC_engines/validate.c +408 -0
  42. epyt_flow/EPANET/compile_linux.sh +1 -1
  43. epyt_flow/EPANET/compile_macos.sh +1 -1
  44. epyt_flow/VERSION +1 -1
  45. epyt_flow/gym/scenario_control_env.py +26 -3
  46. epyt_flow/simulation/events/quality_events.py +6 -6
  47. epyt_flow/simulation/events/sensor_faults.py +24 -24
  48. epyt_flow/simulation/events/system_event.py +3 -3
  49. epyt_flow/simulation/scada/scada_data.py +1 -1
  50. epyt_flow/simulation/scenario_simulator.py +14 -11
  51. epyt_flow/topology.py +8 -1
  52. epyt_flow/uncertainty/model_uncertainty.py +292 -150
  53. {epyt_flow-0.14.0.dist-info → epyt_flow-0.14.1.dist-info}/METADATA +2 -2
  54. {epyt_flow-0.14.0.dist-info → epyt_flow-0.14.1.dist-info}/RECORD +57 -51
  55. epyt_flow/EPANET/EPANET/SRC_engines/Readme_SRC_Engines.txt +0 -18
  56. epyt_flow/EPANET/EPANET/SRC_engines/epanet2.def +0 -131
  57. epyt_flow/EPANET/EPANET/SRC_engines/main.c +0 -93
  58. {epyt_flow-0.14.0.dist-info → epyt_flow-0.14.1.dist-info}/WHEEL +0 -0
  59. {epyt_flow-0.14.0.dist-info → epyt_flow-0.14.1.dist-info}/licenses/LICENSE +0 -0
  60. {epyt_flow-0.14.0.dist-info → epyt_flow-0.14.1.dist-info}/top_level.txt +0 -0
@@ -1,13 +1,13 @@
1
1
  /*
2
2
  ******************************************************************************
3
3
  Project: OWA EPANET
4
- Version: 2.2
4
+ Version: 2.3
5
5
  Module: epanet.c
6
6
  Description: implementation of EPANET's API functions
7
7
  Authors: see AUTHORS
8
8
  Copyright: see AUTHORS
9
9
  License: see LICENSE
10
- Last Updated: 11/15/2019
10
+ Last Updated: 04/23/2025
11
11
  ******************************************************************************
12
12
  */
13
13
 
@@ -143,7 +143,7 @@ int DLLEXPORT EN_init(EN_Project p, const char *rptFile, const char *outFile,
143
143
  p->report.Rptflag = 1;
144
144
 
145
145
  // Check for valid arguments
146
- if (unitsType < 0 || unitsType > CMD) return 251;
146
+ if (unitsType < 0 || unitsType > CMS) return 251;
147
147
  if (headLossType < 0 || headLossType > CM) return 251;
148
148
 
149
149
  // Open files
@@ -179,61 +179,29 @@ int DLLEXPORT EN_open(EN_Project p, const char *inpFile, const char *rptFile,
179
179
  ** outFile = name of binary output file
180
180
  ** Output: none
181
181
  ** Returns: error code
182
- ** Purpose: opens an EPANET input file & reads in network data
182
+ ** Purpose: reads an EPANET input file with no errors allowed.
183
183
  **----------------------------------------------------------------
184
184
  */
185
- {
186
- int errcode = 0;
187
-
188
- // Set system flags
189
- p->Openflag = FALSE;
190
- p->hydraul.OpenHflag = FALSE;
191
- p->quality.OpenQflag = FALSE;
192
- p->outfile.SaveHflag = FALSE;
193
- p->outfile.SaveQflag = FALSE;
194
- p->Warnflag = FALSE;
195
- p->report.Messageflag = TRUE;
196
- p->report.Rptflag = 1;
197
-
198
- // Initialize data arrays to NULL
199
- initpointers(p);
200
-
201
- // Open input & report files
202
- ERRCODE(openfiles(p, inpFile, rptFile, outFile));
203
- if (errcode > 0)
204
- {
205
- errmsg(p, errcode);
206
- return errcode;
207
- }
208
-
209
- // Allocate memory for project's data arrays
210
- writewin(p->viewprog, FMT100);
211
- ERRCODE(netsize(p));
212
- ERRCODE(allocdata(p));
213
-
214
- // Read input data
215
- ERRCODE(getdata(p));
216
-
217
- // Close input file
218
- if (p->parser.InFile != NULL)
219
- {
220
- fclose(p->parser.InFile);
221
- p->parser.InFile = NULL;
222
- }
223
-
224
- // If using previously saved hydraulics file then open it
225
- if (p->outfile.Hydflag == USE) ERRCODE(openhydfile(p));
185
+ {
186
+ writewin(p->viewprog, FMT100);
187
+ return openproject(p, inpFile, rptFile, outFile, FALSE);
188
+ }
226
189
 
227
- // Write input summary to report file
228
- if (!errcode)
229
- {
230
- if (p->report.Summaryflag) writesummary(p);
231
- writetime(p, FMT104);
232
- p->Openflag = TRUE;
233
- }
234
- else errmsg(p, errcode);
235
- return errcode;
236
- }
190
+ int DLLEXPORT EN_openX(EN_Project p, const char *inpFile,
191
+ const char *rptFile, const char *outFile)
192
+ /*----------------------------------------------------------------
193
+ ** Input: inpFile = name of input file
194
+ ** rptFile = name of report file
195
+ ** outFile = name of binary output file
196
+ ** Output: none
197
+ ** Returns: error code
198
+ ** Purpose: reads an EPANET input file with errors allowed.
199
+ **----------------------------------------------------------------
200
+ */
201
+ {
202
+ writewin(p->viewprog, FMT100);
203
+ return openproject(p, inpFile, rptFile, outFile, TRUE);
204
+ }
237
205
 
238
206
  int DLLEXPORT EN_gettitle(EN_Project p, char *line1, char *line2, char *line3)
239
207
  /*----------------------------------------------------------------
@@ -251,7 +219,8 @@ int DLLEXPORT EN_gettitle(EN_Project p, char *line1, char *line2, char *line3)
251
219
  return 0;
252
220
  }
253
221
 
254
- int DLLEXPORT EN_settitle(EN_Project p, char *line1, char *line2, char *line3)
222
+ int DLLEXPORT EN_settitle(EN_Project p, const char *line1,
223
+ const char *line2, const char *line3)
255
224
  /*----------------------------------------------------------------
256
225
  ** Input: line1, line2, line3 = project's title lines
257
226
  ** Returns: error code
@@ -279,7 +248,8 @@ int DLLEXPORT EN_getcomment(EN_Project p, int object, int index, char *comment)
279
248
  return getcomment(&p->network, object, index, comment);
280
249
  }
281
250
 
282
- int DLLEXPORT EN_setcomment(EN_Project p, int object, int index, char *comment)
251
+ int DLLEXPORT EN_setcomment(EN_Project p, int object, int index,
252
+ const char *comment)
283
253
  /*----------------------------------------------------------------
284
254
  ** Input: object = a type of object (see EN_ObjectType)
285
255
  ** index = the object's index
@@ -292,6 +262,32 @@ int DLLEXPORT EN_setcomment(EN_Project p, int object, int index, char *comment)
292
262
  return setcomment(&p->network, object, index, comment);
293
263
  }
294
264
 
265
+ int DLLEXPORT EN_gettag(EN_Project p, int object, int index, char *tag)
266
+ /*----------------------------------------------------------------
267
+ ** Input: object = either EN_NODE or EN_LINK
268
+ ** index = the object's index
269
+ ** Output: tag = the tag string assigned to the object
270
+ ** Returns: error code
271
+ ** Purpose: Retrieves an object's tag string
272
+ **----------------------------------------------------------------
273
+ */
274
+ {
275
+ return gettag(&p->network, object, index, tag);
276
+ }
277
+
278
+ int DLLEXPORT EN_settag(EN_Project p, int object, int index,
279
+ const char *tag)
280
+ /*----------------------------------------------------------------
281
+ ** Input: object = either EN_NODE or EN_LINK
282
+ ** index = the object's index
283
+ ** tag = a descriptive comment to assign
284
+ ** Returns: error code
285
+ ** Purpose: Assigns a tag string to an object
286
+ **----------------------------------------------------------------
287
+ */
288
+ {
289
+ return settag(&p->network, object, index, tag);
290
+ }
295
291
  int DLLEXPORT EN_getcount(EN_Project p, int object, int *count)
296
292
  /*----------------------------------------------------------------
297
293
  ** Input: object = type of object to count (see EN_CountType)
@@ -357,7 +353,6 @@ int DLLEXPORT EN_close(EN_Project p)
357
353
  */
358
354
  {
359
355
  // Free all project data
360
- if (p->Openflag) writetime(p, FMT105);
361
356
  freedata(p);
362
357
 
363
358
  // Close output file
@@ -493,7 +488,11 @@ int DLLEXPORT EN_openH(EN_Project p)
493
488
 
494
489
  // Open hydraulics solver
495
490
  ERRCODE(openhyd(p));
496
- if (!errcode) p->hydraul.OpenHflag = TRUE;
491
+ if (!errcode)
492
+ {
493
+ p->hydraul.OpenHflag = TRUE;
494
+ writetime(p, FMT104);
495
+ }
497
496
  else errmsg(p, errcode);
498
497
  return errcode;
499
498
  }
@@ -536,6 +535,10 @@ int DLLEXPORT EN_initH(EN_Project p, int initFlag)
536
535
  return errcode;
537
536
  }
538
537
  }
538
+
539
+ // Open pipe leakage modeling system
540
+ errcode = openleakage(p);
541
+ if (errcode) return errcode;
539
542
 
540
543
  // Initialize hydraulics solver
541
544
  inithyd(p, fflag);
@@ -590,7 +593,11 @@ int DLLEXPORT EN_closeH(EN_Project p)
590
593
  */
591
594
  {
592
595
  if (!p->Openflag) return 102;
593
- if (p->hydraul.OpenHflag) closehyd(p);
596
+ if (p->hydraul.OpenHflag)
597
+ {
598
+ closeleakage(p);
599
+ closehyd(p);
600
+ }
594
601
  p->hydraul.OpenHflag = FALSE;
595
602
  return 0;
596
603
  }
@@ -832,6 +839,7 @@ int DLLEXPORT EN_closeQ(EN_Project p)
832
839
  closequal(p);
833
840
  p->quality.OpenQflag = FALSE;
834
841
  closeoutfile(p);
842
+ writetime(p, FMT105);
835
843
  return 0;
836
844
  }
837
845
 
@@ -841,7 +849,35 @@ int DLLEXPORT EN_closeQ(EN_Project p)
841
849
 
842
850
  ********************************************************************/
843
851
 
844
- int DLLEXPORT EN_writeline(EN_Project p, char *line)
852
+
853
+ int DLLEXPORT EN_setreportcallback(EN_Project p, void (*callback)(void*,void*,const char*))
854
+ /*----------------------------------------------------------------
855
+ ** Input: callback = a pointer to a reporting function
856
+ ** Output: none
857
+ ** Returns: error code
858
+ ** Purpose: replaces EPANET's normal use of a designated report file
859
+ **----------------------------------------------------------------
860
+ */
861
+ {
862
+ p->report.reportCallback = callback;
863
+ return 0;
864
+ }
865
+
866
+ int DLLEXPORT EN_setreportcallbackuserdata(EN_Project p, void *userData)
867
+ /*----------------------------------------------------------------
868
+ ** Input: userData = a pointer to a client-side data object
869
+ ** Output: none
870
+ ** Returns: error code
871
+ ** Purpose: sets the client-side data object used in conjunction with
872
+ ** the callback function in EN_setreportcallback
873
+ **----------------------------------------------------------------
874
+ */
875
+ {
876
+ p->report.reportCallbackUserData = userData;
877
+ return 0;
878
+ }
879
+
880
+ int DLLEXPORT EN_writeline(EN_Project p, const char *line)
845
881
  /*----------------------------------------------------------------
846
882
  ** Input: line = line of text
847
883
  ** Output: none
@@ -877,7 +913,7 @@ int DLLEXPORT EN_report(EN_Project p)
877
913
  return errcode;
878
914
  }
879
915
 
880
- int DLLEXPORT EN_copyreport(EN_Project p, char *filename)
916
+ int DLLEXPORT EN_copyreport(EN_Project p, const char *filename)
881
917
  /*----------------------------------------------------------------
882
918
  ** Input: filename = name of file to receive copy of report
883
919
  ** Output: none
@@ -926,7 +962,7 @@ int DLLEXPORT EN_resetreport(EN_Project p)
926
962
  return 0;
927
963
  }
928
964
 
929
- int DLLEXPORT EN_setreport(EN_Project p, char *format)
965
+ int DLLEXPORT EN_setreport(EN_Project p, const char *format)
930
966
  /*----------------------------------------------------------------
931
967
  ** Input: format = a report formatting command
932
968
  ** Output: none
@@ -983,7 +1019,7 @@ int DLLEXPORT EN_getversion(int *version)
983
1019
 
984
1020
  int DLLEXPORT EN_geterror(int errcode, char *errmsg, int maxLen)
985
1021
  /*----------------------------------------------------------------
986
- ** Input: errcode = an error or warnng code
1022
+ ** Input: errcode = an error or warning code
987
1023
  ** maxLen = maximum characters that errmsg can hold
988
1024
  ** Output: errmsg = text of error/warning message
989
1025
  ** Returns: error code
@@ -1056,6 +1092,9 @@ int DLLEXPORT EN_getstatistic(EN_Project p, int type, double *value)
1056
1092
  case EN_DEMANDREDUCTION:
1057
1093
  *value = p->hydraul.DemandReduction;
1058
1094
  break;
1095
+ case EN_LEAKAGELOSS:
1096
+ *value = p->hydraul.LeakageLoss;
1097
+ break;
1059
1098
  case EN_MASSBALANCE:
1060
1099
  *value = p->quality.MassBalance.ratio;
1061
1100
  break;
@@ -1187,7 +1226,18 @@ int DLLEXPORT EN_getoption(EN_Project p, int option, double *value)
1187
1226
  case EN_CONCENLIMIT:
1188
1227
  v = qual->Climit * p->Ucf[QUALITY];
1189
1228
  break;
1190
-
1229
+ case EN_DEMANDPATTERN:
1230
+ v = hyd->DefPat;
1231
+ break;
1232
+ case EN_EMITBACKFLOW:
1233
+ v = hyd->EmitBackFlag;
1234
+ break;
1235
+ case EN_PRESS_UNITS:
1236
+ v = (double)p->parser.Pressflag;
1237
+ break;
1238
+ case EN_STATUS_REPORT:
1239
+ v = (double)( p->report.Statflag);
1240
+ break;
1191
1241
  default:
1192
1242
  return 251;
1193
1243
  }
@@ -1211,9 +1261,12 @@ int DLLEXPORT EN_setoption(EN_Project p, int option, double value)
1211
1261
 
1212
1262
  int Njuncs = net->Njuncs;
1213
1263
  double *Ucf = p->Ucf;
1214
- int i, j, pat;
1264
+ int i, j, pat, unit;
1215
1265
  double Ke, n, ucf;
1216
1266
 
1267
+ double qfactor, hfactor, pfactor, dfactor;
1268
+ double dcf, pcf, hcf, qcf;
1269
+
1217
1270
  if (!p->Openflag) return 102;
1218
1271
 
1219
1272
  // The EN_UNBALANCED option can be < 0 indicating that the simulation
@@ -1230,7 +1283,7 @@ int DLLEXPORT EN_setoption(EN_Project p, int option, double value)
1230
1283
  // All other option values must be non-negative
1231
1284
  if (value < 0.0) return 213;
1232
1285
 
1233
- // Process the speficied option
1286
+ // Process the specified option
1234
1287
  switch (option)
1235
1288
  {
1236
1289
  case EN_TRIALS:
@@ -1302,7 +1355,12 @@ int DLLEXPORT EN_setoption(EN_Project p, int option, double value)
1302
1355
 
1303
1356
  case EN_SP_GRAVITY:
1304
1357
  if (value <= 0.0) return 213;
1305
- Ucf[PRESSURE] *= (value / hyd->SpGrav);
1358
+ if (p->parser.Pressflag == PSI ||
1359
+ p->parser.Pressflag == KPA ||
1360
+ p->parser.Pressflag == BAR)
1361
+ {
1362
+ Ucf[PRESSURE] *= (value / hyd->SpGrav);
1363
+ }
1306
1364
  hyd->SpGrav = value;
1307
1365
  break;
1308
1366
 
@@ -1344,6 +1402,42 @@ int DLLEXPORT EN_setoption(EN_Project p, int option, double value)
1344
1402
  qual->Climit = value / p->Ucf[QUALITY];
1345
1403
  break;
1346
1404
 
1405
+ case EN_DEMANDPATTERN:
1406
+ pat = ROUND(value);
1407
+ if (pat < 0 || pat > net->Npats) return 205;
1408
+ hyd->DefPat = pat;
1409
+ break;
1410
+
1411
+ case EN_EMITBACKFLOW:
1412
+ if (value == 0.0 || value == 1.0) hyd->EmitBackFlag = (int)value;
1413
+ else return 213;
1414
+ break;
1415
+
1416
+ case EN_PRESS_UNITS:
1417
+ unit = ROUND(value);
1418
+ if (unit < 0 || unit > FEET) return 205;
1419
+ p->parser.Pressflag = unit;
1420
+
1421
+ dfactor = Ucf[DEMAND];
1422
+ pfactor = Ucf[PRESSURE];
1423
+ hfactor = Ucf[HEAD];
1424
+ qfactor = Ucf[FLOW];
1425
+ initunits(p);
1426
+
1427
+ // Update units in rules
1428
+ dcf = Ucf[DEMAND] / dfactor;
1429
+ pcf = Ucf[PRESSURE] / pfactor;
1430
+ hcf = Ucf[HEAD] / hfactor;
1431
+ qcf = Ucf[FLOW] / qfactor;
1432
+ updateruleunits(p, dcf, pcf, hcf, qcf);
1433
+ break;
1434
+
1435
+ case EN_STATUS_REPORT:
1436
+ i = ROUND(value);
1437
+ if (i < EN_NO_REPORT || i > EN_FULL_REPORT) return 213;
1438
+ p->report.Statflag = i;
1439
+ break;
1440
+
1347
1441
  default:
1348
1442
  return 251;
1349
1443
  }
@@ -1376,8 +1470,9 @@ int DLLEXPORT EN_setflowunits(EN_Project p, int units)
1376
1470
  {
1377
1471
  Network *net = &p->network;
1378
1472
 
1379
- int i, j;
1380
- double qfactor, vfactor, hfactor, efactor, xfactor, yfactor;
1473
+ int i, j, oldUnitFlag;
1474
+ double qfactor, vfactor, hfactor, efactor, pfactor, dfactor, xfactor, yfactor;
1475
+ double dcf, pcf, hcf, qcf;
1381
1476
  double *Ucf = p->Ucf;
1382
1477
 
1383
1478
  if (!p->Openflag) return 102;
@@ -1387,7 +1482,10 @@ int DLLEXPORT EN_setflowunits(EN_Project p, int units)
1387
1482
  vfactor = Ucf[VOLUME];
1388
1483
  hfactor = Ucf[HEAD];
1389
1484
  efactor = Ucf[ELEV];
1485
+ pfactor = Ucf[PRESSURE];
1486
+ dfactor = Ucf[DEMAND];
1390
1487
 
1488
+ oldUnitFlag = p->parser.Unitsflag;
1391
1489
  p->parser.Flowflag = units;
1392
1490
  switch (units)
1393
1491
  {
@@ -1396,6 +1494,7 @@ int DLLEXPORT EN_setflowunits(EN_Project p, int units)
1396
1494
  case MLD:
1397
1495
  case CMH:
1398
1496
  case CMD:
1497
+ case CMS:
1399
1498
  p->parser.Unitsflag = SI;
1400
1499
  break;
1401
1500
  default:
@@ -1404,10 +1503,20 @@ int DLLEXPORT EN_setflowunits(EN_Project p, int units)
1404
1503
  }
1405
1504
 
1406
1505
  // Revise pressure units depending on flow units
1407
- if (p->parser.Unitsflag != SI) p->parser.Pressflag = PSI;
1408
- else if (p->parser.Pressflag == PSI) p->parser.Pressflag = METERS;
1506
+ if (oldUnitFlag != p->parser.Unitsflag)
1507
+ {
1508
+ if (p->parser.Unitsflag == US) p->parser.Pressflag = PSI;
1509
+ else p->parser.Pressflag = METERS;
1510
+ }
1409
1511
  initunits(p);
1410
1512
 
1513
+ // Update pressure units in rules
1514
+ dcf = Ucf[DEMAND] / dfactor;
1515
+ pcf = Ucf[PRESSURE] / pfactor;
1516
+ hcf = Ucf[HEAD] / hfactor;
1517
+ qcf = Ucf[FLOW] / qfactor;
1518
+ updateruleunits(p, dcf, pcf, hcf, qcf);
1519
+
1411
1520
  //update curves
1412
1521
  for (i = 1; i <= net->Ncurves; i++)
1413
1522
  {
@@ -1591,12 +1700,65 @@ int DLLEXPORT EN_settimeparam(EN_Project p, int param, long value)
1591
1700
  time->Qtime = value;
1592
1701
  break;
1593
1702
 
1703
+ case EN_STARTTIME:
1704
+ if (value > SECperDAY) return 213;
1705
+ time->Tstart = value;
1706
+ break;
1707
+
1594
1708
  default:
1595
1709
  return 251;
1596
1710
  }
1597
1711
  return 0;
1598
1712
  }
1599
1713
 
1714
+
1715
+ /// get the time to next event, and give a reason for the time step truncation
1716
+ int DLLEXPORT EN_timetonextevent(EN_Project p, int *eventType,
1717
+ long *duration, int *elementIndex)
1718
+ /*----------------------------------------------------------------
1719
+ ** Input: none
1720
+ ** Output: eventType = event causing a new time step
1721
+ ** to occur (see EN_TimestepEvent)
1722
+ ** duration = seconds until next time step occurs
1723
+ ** elementIndex = index of tank node or simple control
1724
+ ** that triggers a new time step
1725
+ ** Returns: error code
1726
+ ** Purpose: Get information about when the next hydraulic time step occurs
1727
+ **----------------------------------------------------------------
1728
+ */
1729
+ {
1730
+ Times *time = &p->times;
1731
+ long hydStep, tankStep, controlStep;
1732
+ int iTank, iControl;
1733
+
1734
+ hydStep = time->Hstep;
1735
+ tankStep = hydStep;
1736
+ controlStep = hydStep;
1737
+
1738
+ iTank = tanktimestep(p, &tankStep);
1739
+ iControl = controltimestep(p, &controlStep);
1740
+
1741
+ // return the lesser of the three step lengths
1742
+ if (controlStep < tankStep) {
1743
+ *eventType = (int)EN_STEP_CONTROLEVENT;
1744
+ *duration = controlStep;
1745
+ *elementIndex = iControl;
1746
+ }
1747
+ else if (tankStep < hydStep) {
1748
+ *eventType = (int)EN_STEP_TANKEVENT;
1749
+ *duration = tankStep;
1750
+ *elementIndex = iTank;
1751
+ }
1752
+ else {
1753
+ *eventType = (int)EN_STEP_HYD;
1754
+ *duration = hydStep;
1755
+ *elementIndex = 0;
1756
+ }
1757
+
1758
+ return 0;
1759
+ }
1760
+
1761
+
1600
1762
  int DLLEXPORT EN_getqualinfo(EN_Project p, int *qualType, char *chemName,
1601
1763
  char *chemUnits, int *traceNode)
1602
1764
  /*----------------------------------------------------------------
@@ -1652,8 +1814,8 @@ int DLLEXPORT EN_getqualtype(EN_Project p, int *qualType, int *traceNode)
1652
1814
  return 0;
1653
1815
  }
1654
1816
 
1655
- int DLLEXPORT EN_setqualtype(EN_Project p, int qualType, char *chemName,
1656
- char *chemUnits, char *traceNode)
1817
+ int DLLEXPORT EN_setqualtype(EN_Project p, int qualType, const char *chemName,
1818
+ const char *chemUnits, const char *traceNode)
1657
1819
  /*----------------------------------------------------------------
1658
1820
  ** Input: qualType = type of quality analysis to run (see EN_QualityType)
1659
1821
  ** chemname = name of chemical constituent
@@ -1670,7 +1832,7 @@ int DLLEXPORT EN_setqualtype(EN_Project p, int qualType, char *chemName,
1670
1832
  Quality *qual = &p->quality;
1671
1833
 
1672
1834
  double *Ucf = p->Ucf;
1673
- int i, oldQualFlag, traceNodeIndex;
1835
+ int i, oldQualFlag, traceNodeIndex = 0;
1674
1836
  double ccf = 1.0;
1675
1837
 
1676
1838
  if (!p->Openflag) return 102;
@@ -1682,6 +1844,7 @@ int DLLEXPORT EN_setqualtype(EN_Project p, int qualType, char *chemName,
1682
1844
  if (traceNodeIndex == 0) return 212;
1683
1845
  }
1684
1846
 
1847
+ qual->TraceNode = traceNodeIndex;
1685
1848
  oldQualFlag = qual->Qualflag;
1686
1849
  qual->Qualflag = qualType;
1687
1850
  qual->Ctol *= Ucf[QUALITY];
@@ -1696,8 +1859,6 @@ int DLLEXPORT EN_setqualtype(EN_Project p, int qualType, char *chemName,
1696
1859
  }
1697
1860
  if (qual->Qualflag == TRACE) // Source trace analysis
1698
1861
  {
1699
- qual->TraceNode = findnode(net, traceNode);
1700
- if (qual->TraceNode == 0) return 212;
1701
1862
  strncpy(qual->ChemName, w_TRACE, MAXID);
1702
1863
  strncpy(qual->ChemUnits, u_PERCENT, MAXID);
1703
1864
  strcpy(rpt->Field[QUALITY].Units, u_PERCENT);
@@ -1732,7 +1893,7 @@ int DLLEXPORT EN_setqualtype(EN_Project p, int qualType, char *chemName,
1732
1893
 
1733
1894
  ********************************************************************/
1734
1895
 
1735
- int DLLEXPORT EN_addnode(EN_Project p, char *id, int nodeType, int *index)
1896
+ int DLLEXPORT EN_addnode(EN_Project p, const char *id, int nodeType, int *index)
1736
1897
  /*----------------------------------------------------------------
1737
1898
  ** Input: id = node ID name
1738
1899
  ** nodeType = type of node (see EN_NodeType)
@@ -1761,17 +1922,21 @@ int DLLEXPORT EN_addnode(EN_Project p, char *id, int nodeType, int *index)
1761
1922
 
1762
1923
  // Check if a node with same id already exists
1763
1924
  if (EN_getnodeindex(p, id, &i) == 0) return 215;
1764
-
1925
+
1765
1926
  // Check for valid node type
1766
- if (nodeType < EN_JUNCTION || nodeType > EN_TANK) return 251;
1927
+ if (nodeType < EN_JUNCTION || nodeType > EN_TANK) return 251;
1767
1928
 
1768
- // Grow node-related arrays to accomodate the new node
1929
+ // Grow node-related arrays to accommodate the new node
1769
1930
  size = (net->Nnodes + 2) * sizeof(Snode);
1770
1931
  net->Node = (Snode *)realloc(net->Node, size);
1771
1932
  size = (net->Nnodes + 2) * sizeof(double);
1772
1933
  hyd->NodeDemand = (double *)realloc(hyd->NodeDemand, size);
1773
1934
  qual->NodeQual = (double *)realloc(qual->NodeQual, size);
1774
1935
  hyd->NodeHead = (double *)realloc(hyd->NodeHead, size);
1936
+ hyd->FullDemand = (double *)realloc(hyd->FullDemand, size);
1937
+ hyd->EmitterFlow = (double *)realloc(hyd->EmitterFlow, size);
1938
+ hyd->LeakageFlow = (double *)realloc(hyd->LeakageFlow, size);
1939
+ hyd->DemandFlow = (double *)realloc(hyd->DemandFlow, size);
1775
1940
 
1776
1941
  // Actions taken when a new Junction is added
1777
1942
  if (nodeType == EN_JUNCTION)
@@ -1782,7 +1947,7 @@ int DLLEXPORT EN_addnode(EN_Project p, char *id, int nodeType, int *index)
1782
1947
  hashtable_update(net->NodeHashTable, net->Node[i].ID, i + 1);
1783
1948
  net->Node[i + 1] = net->Node[i];
1784
1949
  }
1785
-
1950
+
1786
1951
  // set index of new Junction node
1787
1952
  net->Njuncs++;
1788
1953
  nIdx = net->Njuncs;
@@ -1808,7 +1973,7 @@ int DLLEXPORT EN_addnode(EN_Project p, char *id, int nodeType, int *index)
1808
1973
  if (control->Node > net->Njuncs - 1) control->Node += 1;
1809
1974
  }
1810
1975
  // adjust indices of tanks/reservoirs in Rule premises (see RULES.C)
1811
- adjusttankrules(p);
1976
+ adjusttankrules(p, 1);
1812
1977
  }
1813
1978
 
1814
1979
  // Actions taken when a new Tank/Reservoir is added
@@ -1840,7 +2005,7 @@ int DLLEXPORT EN_addnode(EN_Project p, char *id, int nodeType, int *index)
1840
2005
  tank->Pat = 0;
1841
2006
  tank->Vcurve = 0;
1842
2007
  tank->MixModel = 0;
1843
- tank->V1max = 10000;
2008
+ tank->V1frac = 1;
1844
2009
  tank->CanOverflow = FALSE;
1845
2010
  }
1846
2011
  net->Nnodes++;
@@ -1858,6 +2023,7 @@ int DLLEXPORT EN_addnode(EN_Project p, char *id, int nodeType, int *index)
1858
2023
  node->X = MISSING;
1859
2024
  node->Y = MISSING;
1860
2025
  node->Comment = NULL;
2026
+ node->Tag = NULL;
1861
2027
 
1862
2028
  // Insert new node into hash table
1863
2029
  hashtable_insert(net->NodeHashTable, node->ID, nIdx);
@@ -1917,6 +2083,7 @@ int DLLEXPORT EN_deletenode(EN_Project p, int index, int actionCode)
1917
2083
  freedemands(node);
1918
2084
  free(node->S);
1919
2085
  free(node->Comment);
2086
+ free(node->Tag);
1920
2087
 
1921
2088
  // Shift position of higher entries in Node & Coord arrays down one
1922
2089
  for (i = index; i <= net->Nnodes - 1; i++)
@@ -1925,6 +2092,7 @@ int DLLEXPORT EN_deletenode(EN_Project p, int index, int actionCode)
1925
2092
  // ... update node's entry in the hash table
1926
2093
  hashtable_update(net->NodeHashTable, net->Node[i].ID, i);
1927
2094
  }
2095
+ if (index < p->quality.TraceNode) (p->quality.TraceNode)--;
1928
2096
 
1929
2097
  // If deleted node is a tank, remove it from the Tank array
1930
2098
  if (nodeType != EN_JUNCTION)
@@ -1979,7 +2147,7 @@ int DLLEXPORT EN_deletenode(EN_Project p, int index, int actionCode)
1979
2147
  return 0;
1980
2148
  }
1981
2149
 
1982
- int DLLEXPORT EN_getnodeindex(EN_Project p, char *id, int *index)
2150
+ int DLLEXPORT EN_getnodeindex(EN_Project p, const char *id, int *index)
1983
2151
  /*----------------------------------------------------------------
1984
2152
  ** Input: id = node ID name
1985
2153
  ** Output: index = node index
@@ -2011,7 +2179,7 @@ int DLLEXPORT EN_getnodeid(EN_Project p, int index, char *id)
2011
2179
  return 0;
2012
2180
  }
2013
2181
 
2014
- int DLLEXPORT EN_setnodeid(EN_Project p, int index, char *newid)
2182
+ int DLLEXPORT EN_setnodeid(EN_Project p, int index, const char *newid)
2015
2183
  /*----------------------------------------------------------------
2016
2184
  ** Input: index = node index
2017
2185
  ** newid = new node ID name
@@ -2074,8 +2242,10 @@ int DLLEXPORT EN_getnodevalue(EN_Project p, int index, int property, double *val
2074
2242
  Network *net = &p->network;
2075
2243
  Hydraul *hyd = &p->hydraul;
2076
2244
  Quality *qual = &p->quality;
2245
+ Parser *parser = &p->parser;
2077
2246
 
2078
2247
  double v = 0.0;
2248
+ double ecfTmp; // Unit conversion factor for emitter pressure
2079
2249
  Psource source;
2080
2250
 
2081
2251
  Snode *Node = net->Node;
@@ -2120,7 +2290,8 @@ int DLLEXPORT EN_getnodevalue(EN_Project p, int index, int property, double *val
2120
2290
  v = 0.0;
2121
2291
  if (Node[index].Ke > 0.0)
2122
2292
  {
2123
- v = Ucf[FLOW] / pow((Ucf[PRESSURE] * Node[index].Ke), (1.0 / hyd->Qexp));
2293
+ ecfTmp = (parser->Unitsflag == US) ? (PSIperFT * hyd->SpGrav) : MperFT;
2294
+ v = Ucf[FLOW] / pow((ecfTmp * Node[index].Ke), (1.0 / hyd->Qexp));
2124
2295
  }
2125
2296
  break;
2126
2297
 
@@ -2157,10 +2328,12 @@ int DLLEXPORT EN_getnodevalue(EN_Project p, int index, int property, double *val
2157
2328
 
2158
2329
  case EN_MIXZONEVOL:
2159
2330
  v = 0.0;
2160
- if (index > nJuncs) v = Tank[index - nJuncs].V1max * Ucf[VOLUME];
2331
+ if (index > nJuncs)
2332
+ v = Tank[index - nJuncs].V1frac * Tank[index - nJuncs].Vmax *
2333
+ Ucf[VOLUME];
2161
2334
  break;
2162
2335
 
2163
- case EN_DEMAND:
2336
+ case EN_DEMAND: // Consumer Demand + Emitter Flow + Leakage Flow
2164
2337
  v = hyd->NodeDemand[index] * Ucf[FLOW];
2165
2338
  break;
2166
2339
 
@@ -2217,9 +2390,9 @@ int DLLEXPORT EN_getnodevalue(EN_Project p, int index, int property, double *val
2217
2390
 
2218
2391
  case EN_MIXFRACTION:
2219
2392
  v = 1.0;
2220
- if (index > nJuncs && Tank[index - nJuncs].Vmax > 0.0)
2393
+ if (index > nJuncs)
2221
2394
  {
2222
- v = Tank[index - nJuncs].V1max / Tank[index - nJuncs].Vmax;
2395
+ v = Tank[index - nJuncs].V1frac;
2223
2396
  }
2224
2397
  break;
2225
2398
 
@@ -2237,14 +2410,34 @@ int DLLEXPORT EN_getnodevalue(EN_Project p, int index, int property, double *val
2237
2410
  if (Node[index].Type != TANK) return 0;
2238
2411
  v = Tank[index - nJuncs].CanOverflow;
2239
2412
  break;
2240
-
2413
+
2241
2414
  case EN_DEMANDDEFICIT:
2242
2415
  if (index > nJuncs) return 0;
2243
- // After an analysis, DemandFlow contains node's required demand
2244
- // while NodeDemand contains delivered demand + emitter flow
2245
- if (hyd->DemandFlow[index] < 0.0) return 0;
2246
- v = (hyd->DemandFlow[index] -
2247
- (hyd->NodeDemand[index] - hyd->EmitterFlow[index])) * Ucf[FLOW];
2416
+ // FullDemand contains node's required consumer demand
2417
+ // while DemandFlow contains delivered consumer demand
2418
+ if (hyd->FullDemand[index] <= 0.0) return 0;
2419
+ v = (hyd->FullDemand[index] - hyd->DemandFlow[index]) * Ucf[FLOW];
2420
+ if (v < 0.0) v = 0.0;
2421
+ break;
2422
+
2423
+ case EN_NODE_INCONTROL:
2424
+ v = (double)incontrols(p, NODE, index);
2425
+ break;
2426
+
2427
+ case EN_EMITTERFLOW:
2428
+ v = hyd->EmitterFlow[index] * Ucf[FLOW];
2429
+ break;
2430
+
2431
+ case EN_LEAKAGEFLOW:
2432
+ v = hyd->LeakageFlow[index] * Ucf[FLOW];
2433
+ break;
2434
+
2435
+ case EN_DEMANDFLOW: // Consumer demand delivered
2436
+ v = hyd->DemandFlow[index] * Ucf[FLOW];
2437
+ break;
2438
+
2439
+ case EN_FULLDEMAND: // Consumer demand requested
2440
+ v = hyd->FullDemand[index] * Ucf[FLOW];
2248
2441
  break;
2249
2442
 
2250
2443
  default:
@@ -2254,6 +2447,25 @@ int DLLEXPORT EN_getnodevalue(EN_Project p, int index, int property, double *val
2254
2447
  return 0;
2255
2448
  }
2256
2449
 
2450
+ int DLLEXPORT EN_getnodevalues(EN_Project p, int property, double *values)
2451
+ /*----------------------------------------------------------------
2452
+ ** Input: property = node property code (see EN_NodeProperty)
2453
+ ** Output: values = array of node property values
2454
+ ** Returns: error code
2455
+ ** Purpose: retrieves an array of node property values
2456
+ **----------------------------------------------------------------
2457
+ */
2458
+ {
2459
+ int errcode = 0, i = 0;
2460
+
2461
+ for (i = 1; i <= p->network.Nnodes; i++)
2462
+ {
2463
+ errcode = EN_getnodevalue(p, i, property, &values[i - 1]);
2464
+ if (errcode != 0) { return errcode; }
2465
+ }
2466
+ return 0;
2467
+ }
2468
+
2257
2469
  int DLLEXPORT EN_setnodevalue(EN_Project p, int index, int property, double value)
2258
2470
  /*----------------------------------------------------------------
2259
2471
  ** Input: index = node index
@@ -2268,6 +2480,7 @@ int DLLEXPORT EN_setnodevalue(EN_Project p, int index, int property, double valu
2268
2480
  Network *net = &p->network;
2269
2481
  Hydraul *hyd = &p->hydraul;
2270
2482
  Quality *qual = &p->quality;
2483
+ Parser *parser = &p->parser;
2271
2484
 
2272
2485
  Snode *Node = net->Node;
2273
2486
  Stank *Tank = net->Tank;
@@ -2281,8 +2494,7 @@ int DLLEXPORT EN_setnodevalue(EN_Project p, int index, int property, double valu
2281
2494
 
2282
2495
  int i, j, n;
2283
2496
  Psource source;
2284
- double hTmp;
2285
- double vTmp;
2497
+ double hTmp, ecfTmp;
2286
2498
 
2287
2499
  if (!p->Openflag) return 102;
2288
2500
  if (index <= 0 || index > nNodes) return 203;
@@ -2324,8 +2536,13 @@ int DLLEXPORT EN_setnodevalue(EN_Project p, int index, int property, double valu
2324
2536
  case EN_EMITTER:
2325
2537
  if (index > nJuncs) return 0;
2326
2538
  if (value < 0.0) return 209;
2327
- if (value > 0.0) value = pow((Ucf[FLOW] / value), hyd->Qexp) / Ucf[PRESSURE];
2539
+ if (value > 0.0)
2540
+ {
2541
+ ecfTmp = (parser->Unitsflag == US) ? (PSIperFT * hyd->SpGrav) : MperFT;
2542
+ value = pow((Ucf[FLOW] / value), hyd->Qexp) / ecfTmp;
2543
+ }
2328
2544
  Node[index].Ke = value;
2545
+ if (hyd->EmitterFlow[index] == 0.0) hyd->EmitterFlow[index] = 1.0;
2329
2546
  break;
2330
2547
 
2331
2548
  case EN_INITQUAL:
@@ -2364,7 +2581,7 @@ int DLLEXPORT EN_setnodevalue(EN_Project p, int index, int property, double valu
2364
2581
  break;
2365
2582
 
2366
2583
  case EN_TANKLEVEL:
2367
- if (index <= nJuncs) return 0;
2584
+ if (index <= nJuncs) return 263;
2368
2585
  j = index - nJuncs;
2369
2586
  if (Tank[j].A == 0.0) /* Tank is a reservoir */
2370
2587
  {
@@ -2388,9 +2605,9 @@ int DLLEXPORT EN_setnodevalue(EN_Project p, int index, int property, double valu
2388
2605
 
2389
2606
  case EN_TANKDIAM:
2390
2607
  if (value <= 0.0) return 209; // invalid diameter
2391
- if (index <= nJuncs) return 0; // node is not a tank
2608
+ if (index <= nJuncs) return 263; // node is not a tank
2392
2609
  j = index - nJuncs; // tank index
2393
- if (Tank[j].A == 0.0) return 0; // tank is a reservoir
2610
+ if (Tank[j].A == 0.0) return 263; // tank is a reservoir
2394
2611
  value /= Ucf[ELEV]; // diameter in feet
2395
2612
  Tank[j].A = PI * SQR(value) / 4.0; // new tank area
2396
2613
  if (Tank[j].Vcurve > 0) // tank has a volume curve
@@ -2399,7 +2616,7 @@ int DLLEXPORT EN_setnodevalue(EN_Project p, int index, int property, double valu
2399
2616
 
2400
2617
  // Since the volume curve no longer applies we assume that the tank's
2401
2618
  // shape below Hmin is cylindrical and Vmin equals area times Hmin
2402
- Tank[j].Vmin = Tank[j].A * Tank[j].Hmin;
2619
+ Tank[j].Vmin = Tank[j].A * (Tank[j].Hmin - Node[index].El);
2403
2620
  }
2404
2621
  // Since tank's area has changed its volumes must be updated
2405
2622
  // NOTE: For a non-volume curve tank we can't change the Vmin
@@ -2407,16 +2624,14 @@ int DLLEXPORT EN_setnodevalue(EN_Project p, int index, int property, double valu
2407
2624
  // shape below Hmin. Vmin can always be changed by setting
2408
2625
  // EN_MINVOLUME in a subsequent function call.
2409
2626
  Tank[j].V0 = tankvolume(p, j, Tank[j].H0); // new init. volume
2410
- vTmp = Tank[j].Vmax; // old max. volume
2411
2627
  Tank[j].Vmax = tankvolume(p, j, Tank[j].Hmax); // new max. volume
2412
- Tank[j].V1max *= Tank[j].Vmax / vTmp; // new mix zone volume
2413
2628
  break;
2414
2629
 
2415
2630
  case EN_MINVOLUME:
2416
2631
  if (value < 0.0) return 209; // invalid volume
2417
- if (index <= nJuncs) return 0; // node is not a tank
2632
+ if (index <= nJuncs) return 263; // node is not a tank
2418
2633
  j = index - nJuncs; // tank index
2419
- if (Tank[j].A == 0.0) return 0; // tank is a reservoir
2634
+ if (Tank[j].A == 0.0) return 263; // tank is a reservoir
2420
2635
  i = Tank[j].Vcurve; // volume curve index
2421
2636
  if (i > 0) // tank has a volume curve
2422
2637
  {
@@ -2434,14 +2649,12 @@ int DLLEXPORT EN_setnodevalue(EN_Project p, int index, int property, double valu
2434
2649
  // If the volume supplied by the function is 0 then the tank shape
2435
2650
  // below Hmin is assumed to be cylindrical and a new Vmin value is
2436
2651
  // computed. Otherwise Vmin is set to the supplied value.
2437
- if (value == 0.0) Tank[j].Vmin = Tank[j].A * Tank[j].Hmin;
2652
+ if (value == 0.0) Tank[j].Vmin = Tank[j].A * (Tank[j].Hmin - Node[index].El);
2438
2653
  else Tank[j].Vmin = value / Ucf[VOLUME];
2439
2654
 
2440
2655
  // Since Vmin changes the other volumes need updating
2441
2656
  Tank[j].V0 = tankvolume(p, j, Tank[j].H0); // new init. volume
2442
- vTmp = Tank[j].Vmax; // old max. volume
2443
2657
  Tank[j].Vmax = tankvolume(p, j, Tank[j].Hmax); // new max. volume
2444
- Tank[j].V1max *= Tank[j].Vmax / vTmp; // new mix zone volume
2445
2658
  }
2446
2659
  break;
2447
2660
 
@@ -2451,9 +2664,9 @@ int DLLEXPORT EN_setnodevalue(EN_Project p, int index, int property, double valu
2451
2664
  i = ROUND(value); // curve index
2452
2665
  if (i <= 0 ||
2453
2666
  i > net->Ncurves) return 205; // invalid curve index
2454
- if (index <= nJuncs) return 0; // node not a tank
2667
+ if (index <= nJuncs) return 263; // node not a tank
2455
2668
  j = index - nJuncs; // tank index
2456
- if (Tank[j].A == 0.0) return 0; // tank is a reservoir
2669
+ if (Tank[j].A == 0.0) return 263; // tank is a reservoir
2457
2670
  curve = &net->Curve[i]; // curve object
2458
2671
 
2459
2672
  // Check that tank's min/max levels lie within curve
@@ -2466,18 +2679,16 @@ int DLLEXPORT EN_setnodevalue(EN_Project p, int index, int property, double valu
2466
2679
  Tank[j].Vcurve = i; // assign curve to tank
2467
2680
  Tank[j].Vmin = tankvolume(p, j, Tank[j].Hmin); // new min. volume
2468
2681
  Tank[j].V0 = tankvolume(p, j, Tank[j].H0); // new init. volume
2469
- vTmp = Tank[j].Vmax; // old max. volume
2470
2682
  Tank[j].Vmax = tankvolume(p, j, Tank[j].Hmax); // new max. volume
2471
- Tank[j].V1max *= Tank[j].Vmax / vTmp; // new mix zone volume
2472
- Tank[j].A = (curve->Y[n] - curve->Y[0]) / // nominal area
2683
+ Tank[j].A = (curve->Y[n] - curve->Y[0]) / // nominal area
2473
2684
  (curve->X[n] - curve->X[0]);
2474
2685
  break;
2475
2686
 
2476
2687
  case EN_MINLEVEL:
2477
2688
  if (value < 0.0) return 209; // invalid water level
2478
- if (index <= nJuncs) return 0; // node not a tank
2689
+ if (index <= nJuncs) return 263; // node not a tank
2479
2690
  j = index - nJuncs; // tank index
2480
- if (Tank[j].A == 0.0) return 0; // tank is a reservoir
2691
+ if (Tank[j].A == 0.0) return 263; // tank is a reservoir
2481
2692
  hTmp = value / Ucf[ELEV] + Node[index].El; // convert level to head
2482
2693
  if (hTmp >= Tank[j].Hmax ||
2483
2694
  hTmp > Tank[j].H0) return 225; // invalid water levels
@@ -2496,9 +2707,9 @@ int DLLEXPORT EN_setnodevalue(EN_Project p, int index, int property, double valu
2496
2707
 
2497
2708
  case EN_MAXLEVEL:
2498
2709
  if (value <= 0.0) return 209; // invalid water level
2499
- if (index <= nJuncs) return 0; // node not a tank
2710
+ if (index <= nJuncs) return 263; // node not a tank
2500
2711
  j = index - nJuncs; // tank index
2501
- if (Tank[j].A == 0.0) return 0; // tank is a reservoir
2712
+ if (Tank[j].A == 0.0) return 263; // tank is a reservoir
2502
2713
  hTmp = value / Ucf[ELEV] + Node[index].El; // convert level to head
2503
2714
  if (hTmp < Tank[j].Hmin ||
2504
2715
  hTmp < Tank[j].H0) return 225; // invalid water levels
@@ -2510,14 +2721,12 @@ int DLLEXPORT EN_setnodevalue(EN_Project p, int index, int property, double valu
2510
2721
  if (value > curve->X[n]) return 225; // new level is off curve
2511
2722
  }
2512
2723
  Tank[j].Hmax = hTmp; // new max. head
2513
- vTmp = Tank[j].Vmax; // old max. volume
2514
2724
  Tank[j].Vmax = tankvolume(p, j, hTmp); // new max. volume
2515
- Tank[j].V1max *= Tank[j].Vmax / vTmp; // new mix zone volume
2516
2725
  break;
2517
2726
 
2518
2727
  case EN_MIXMODEL:
2519
2728
  j = ROUND(value);
2520
- if (index <= nJuncs) return 0;
2729
+ if (index <= nJuncs) return 263;
2521
2730
  if (j < MIX1 || j > LIFO) return 251;
2522
2731
  if (Tank[index - nJuncs].A > 0.0)
2523
2732
  {
@@ -2526,17 +2735,17 @@ int DLLEXPORT EN_setnodevalue(EN_Project p, int index, int property, double valu
2526
2735
  break;
2527
2736
 
2528
2737
  case EN_MIXFRACTION:
2529
- if (index <= nJuncs) return 0;
2738
+ if (index <= nJuncs) return 263;
2530
2739
  if (value < 0.0 || value > 1.0) return 209;
2531
2740
  j = index - nJuncs;
2532
2741
  if (Tank[j].A > 0.0)
2533
2742
  {
2534
- Tank[j].V1max = value * Tank[j].Vmax;
2743
+ Tank[j].V1frac = value;
2535
2744
  }
2536
2745
  break;
2537
2746
 
2538
2747
  case EN_TANK_KBULK:
2539
- if (index <= nJuncs) return 0;
2748
+ if (index <= nJuncs) return 263;
2540
2749
  j = index - nJuncs;
2541
2750
  if (Tank[j].A > 0.0)
2542
2751
  {
@@ -2546,7 +2755,7 @@ int DLLEXPORT EN_setnodevalue(EN_Project p, int index, int property, double valu
2546
2755
  break;
2547
2756
 
2548
2757
  case EN_CANOVERFLOW:
2549
- if (Node[index].Type != TANK) return 0;
2758
+ if (Node[index].Type != TANK) return 263;
2550
2759
  Tank[index - nJuncs].CanOverflow = (value != 0.0);
2551
2760
  break;
2552
2761
 
@@ -2557,7 +2766,7 @@ int DLLEXPORT EN_setnodevalue(EN_Project p, int index, int property, double valu
2557
2766
  }
2558
2767
 
2559
2768
  int DLLEXPORT EN_setjuncdata(EN_Project p, int index, double elev,
2560
- double dmnd, char *dmndpat)
2769
+ double dmnd, const char *dmndpat)
2561
2770
  /*----------------------------------------------------------------
2562
2771
  ** Input: index = junction node index
2563
2772
  ** elev = junction elevation
@@ -2602,7 +2811,7 @@ int DLLEXPORT EN_setjuncdata(EN_Project p, int index, double elev,
2602
2811
  int DLLEXPORT EN_settankdata(EN_Project p, int index, double elev,
2603
2812
  double initlvl, double minlvl,
2604
2813
  double maxlvl, double diam,
2605
- double minvol, char *volcurve)
2814
+ double minvol, const char *volcurve)
2606
2815
  /*----------------------------------------------------------------
2607
2816
  ** Input: index = tank node index
2608
2817
  ** elev = tank bottom elevation
@@ -2621,16 +2830,16 @@ int DLLEXPORT EN_settankdata(EN_Project p, int index, double elev,
2621
2830
  Network *net = &p->network;
2622
2831
 
2623
2832
  int i, j, n, curveIndex = 0;
2624
- double area, elevation = elev;
2625
2833
  double *Ucf = p->Ucf;
2834
+ double area;
2626
2835
  Stank *Tank = net->Tank;
2627
2836
  Scurve *curve;
2628
2837
 
2629
2838
  // Check that tank exists
2630
2839
  if (!p->Openflag) return 102;
2631
- if (index <= net->Njuncs || index > net->Nnodes) return 203;
2840
+ if (index <= net->Njuncs || index > net->Nnodes) return 263;
2632
2841
  j = index - net->Njuncs;
2633
- if (Tank[j].A == 0) return 0; // Tank is a Reservoir
2842
+ if (Tank[j].A == 0) return 263; // Tank is a Reservoir
2634
2843
 
2635
2844
  // Check for valid parameter values
2636
2845
  if (initlvl < 0.0 || minlvl < 0.0 || maxlvl < 0.0) return 209;
@@ -2659,16 +2868,16 @@ int DLLEXPORT EN_settankdata(EN_Project p, int index, double elev,
2659
2868
  else area = PI * diam * diam / 4.0;
2660
2869
 
2661
2870
  // Assign parameters to tank object
2662
- net->Node[Tank[j].Node].El = elevation;
2871
+ net->Node[Tank[j].Node].El = elev / Ucf[ELEV];
2663
2872
  Tank[j].A = area / Ucf[ELEV] / Ucf[ELEV];
2664
- Tank[j].H0 = elevation + initlvl / Ucf[ELEV];
2665
- Tank[j].Hmin = elevation + minlvl / Ucf[ELEV];
2666
- Tank[j].Hmax = elevation + maxlvl / Ucf[ELEV];
2873
+ Tank[j].H0 = (elev + initlvl) / Ucf[ELEV];
2874
+ Tank[j].Hmin = (elev + minlvl) / Ucf[ELEV];
2875
+ Tank[j].Hmax = (elev + maxlvl) / Ucf[ELEV];
2667
2876
  Tank[j].Vcurve = curveIndex;
2668
2877
  if (curveIndex == 0)
2669
2878
  {
2670
2879
  if (minvol > 0.0) Tank[j].Vmin = minvol / Ucf[VOLUME];
2671
- else Tank[j].Vmin = Tank[j].A * Tank[j].Hmin;
2880
+ else Tank[j].Vmin = Tank[j].A * (Tank[j].Hmin - elev / Ucf[ELEV]);
2672
2881
  }
2673
2882
  else Tank[j].Vmin = tankvolume(p, j, Tank[j].Hmin);
2674
2883
  Tank[j].V0 = tankvolume(p, j, Tank[j].H0);
@@ -2778,7 +2987,7 @@ int DLLEXPORT EN_setdemandmodel(EN_Project p, int model, double pmin,
2778
2987
  }
2779
2988
 
2780
2989
  int DLLEXPORT EN_adddemand(EN_Project p, int nodeIndex, double baseDemand,
2781
- char *demandPattern, char *demandName)
2990
+ const char *demandPattern, const char *demandName)
2782
2991
  /*----------------------------------------------------------------
2783
2992
  ** Input: nodeIndex = node index
2784
2993
  ** baseDemand = baseline demand value
@@ -2865,7 +3074,7 @@ int DLLEXPORT EN_deletedemand(EN_Project p, int nodeIndex, int demandIndex)
2865
3074
  return 0;
2866
3075
  }
2867
3076
 
2868
- int DLLEXPORT EN_getdemandindex(EN_Project p, int nodeIndex, char *demandName,
3077
+ int DLLEXPORT EN_getdemandindex(EN_Project p, int nodeIndex, const char *demandName,
2869
3078
  int *demandIndex)
2870
3079
  /*----------------------------------------------------------------
2871
3080
  ** Input: nodeIndex = node index
@@ -3014,7 +3223,7 @@ int DLLEXPORT EN_getdemandname(EN_Project p, int nodeIndex, int demandIndex,
3014
3223
  }
3015
3224
 
3016
3225
  int DLLEXPORT EN_setdemandname(EN_Project p, int nodeIndex, int demandIndex,
3017
- char *demandName)
3226
+ const char *demandName)
3018
3227
  /*----------------------------------------------------------------
3019
3228
  ** Input: nodeIndex = node index
3020
3229
  ** demandIndex = demand category index
@@ -3104,8 +3313,8 @@ int DLLEXPORT EN_setdemandpattern(EN_Project p, int nodeIndex, int demandIndex,
3104
3313
 
3105
3314
  ********************************************************************/
3106
3315
 
3107
- int DLLEXPORT EN_addlink(EN_Project p, char *id, int linkType,
3108
- char *fromNode, char *toNode, int *index)
3316
+ int DLLEXPORT EN_addlink(EN_Project p, const char *id, int linkType,
3317
+ const char *fromNode, const char *toNode, int *index)
3109
3318
  /*----------------------------------------------------------------
3110
3319
  ** Input: id = link ID name
3111
3320
  ** type = link type (see EN_LinkType)
@@ -3137,7 +3346,7 @@ int DLLEXPORT EN_addlink(EN_Project p, char *id, int linkType,
3137
3346
  if (EN_getlinkindex(p, id, &i) == 0) return 215;
3138
3347
 
3139
3348
  // Check for valid link type
3140
- if (linkType < CVPIPE || linkType > GPV) return 251;
3349
+ if (linkType < CVPIPE || linkType > PCV) return 251;
3141
3350
 
3142
3351
  // Lookup the link's from and to nodes
3143
3352
  n1 = hashtable_find(net->NodeHashTable, fromNode);
@@ -3151,7 +3360,7 @@ int DLLEXPORT EN_addlink(EN_Project p, char *id, int linkType,
3151
3360
  if (errcode) return errcode;
3152
3361
  }
3153
3362
 
3154
- // Grow link-related arrays to accomodate the new link
3363
+ // Grow link-related arrays to accommodate the new link
3155
3364
  net->Nlinks++;
3156
3365
  p->parser.MaxLinks = net->Nlinks;
3157
3366
  n = net->Nlinks;
@@ -3170,7 +3379,7 @@ int DLLEXPORT EN_addlink(EN_Project p, char *id, int linkType,
3170
3379
  if (linkType <= PIPE) net->Npipes++;
3171
3380
  else if (linkType == PUMP)
3172
3381
  {
3173
- // Grow pump array to accomodate the new link
3382
+ // Grow pump array to accommodate the new link
3174
3383
  net->Npumps++;
3175
3384
  size = (net->Npumps + 1) * sizeof(Spump);
3176
3385
  net->Pump = (Spump *)realloc(net->Pump, size);
@@ -3192,17 +3401,18 @@ int DLLEXPORT EN_addlink(EN_Project p, char *id, int linkType,
3192
3401
  }
3193
3402
  else
3194
3403
  {
3195
- // Grow valve array to accomodate the new link
3404
+ // Grow valve array to accommodate the new link
3196
3405
  net->Nvalves++;
3197
3406
  size = (net->Nvalves + 1) * sizeof(Svalve);
3198
3407
  net->Valve = (Svalve *)realloc(net->Valve, size);
3199
3408
  net->Valve[net->Nvalves].Link = n;
3409
+ net->Valve[net->Nvalves].Curve = 0;
3200
3410
  }
3201
3411
 
3202
3412
  link->Type = linkType;
3203
3413
  link->N1 = n1;
3204
3414
  link->N2 = n2;
3205
- link->Status = OPEN;
3415
+ link->InitStatus = OPEN;
3206
3416
 
3207
3417
  if (linkType == PUMP)
3208
3418
  {
@@ -3231,15 +3441,19 @@ int DLLEXPORT EN_addlink(EN_Project p, char *id, int linkType,
3231
3441
  link->Kc = 0.0; // Valve setting.
3232
3442
  link->Km = 0.0; // Loss coeff
3233
3443
  link->Len = 0.0;
3234
- link->Status = ACTIVE;
3444
+ link->InitStatus = ACTIVE;
3235
3445
  }
3236
3446
  link->Kb = 0;
3237
3447
  link->Kw = 0;
3448
+ link->LeakArea = 0;
3449
+ link->LeakExpan = 0;
3450
+ link->InitSetting = link->Kc;
3238
3451
  link->R = 0;
3239
3452
  link->Rc = 0;
3240
3453
  link->Rpt = 0;
3241
3454
  link->ResultIndex = 0;
3242
3455
  link->Comment = NULL;
3456
+ link->Tag = NULL;
3243
3457
  link->Vertices = NULL;
3244
3458
 
3245
3459
  hashtable_insert(net->LinkHashTable, link->ID, n);
@@ -3292,6 +3506,7 @@ int DLLEXPORT EN_deletelink(EN_Project p, int index, int actionCode)
3292
3506
 
3293
3507
  // Remove link's comment and vertices
3294
3508
  free(link->Comment);
3509
+ free(link->Tag);
3295
3510
  freelinkvertices(link);
3296
3511
 
3297
3512
  // Shift position of higher entries in Link array down one
@@ -3312,6 +3527,12 @@ int DLLEXPORT EN_deletelink(EN_Project p, int index, int actionCode)
3312
3527
  if (net->Valve[i].Link > index) net->Valve[i].Link -= 1;
3313
3528
  }
3314
3529
 
3530
+ // Reduce the number of pipes count by one if it is a pipe.
3531
+ if (linkType == PIPE)
3532
+ {
3533
+ net->Npipes--;
3534
+ }
3535
+
3315
3536
  // Delete any pump associated with the deleted link
3316
3537
  if (linkType == PUMP)
3317
3538
  {
@@ -3354,7 +3575,7 @@ int DLLEXPORT EN_deletelink(EN_Project p, int index, int actionCode)
3354
3575
  return 0;
3355
3576
  }
3356
3577
 
3357
- int DLLEXPORT EN_getlinkindex(EN_Project p, char *id, int *index)
3578
+ int DLLEXPORT EN_getlinkindex(EN_Project p, const char *id, int *index)
3358
3579
  /*----------------------------------------------------------------
3359
3580
  ** Input: id = link ID name
3360
3581
  ** Output: index = link index
@@ -3386,7 +3607,7 @@ int DLLEXPORT EN_getlinkid(EN_Project p, int index, char *id)
3386
3607
  return 0;
3387
3608
  }
3388
3609
 
3389
- int DLLEXPORT EN_setlinkid(EN_Project p, int index, char *newid)
3610
+ int DLLEXPORT EN_setlinkid(EN_Project p, int index, const char *newid)
3390
3611
  /*----------------------------------------------------------------
3391
3612
  ** Input: index = link index
3392
3613
  ** id = link ID name
@@ -3456,7 +3677,7 @@ int DLLEXPORT EN_setlinktype(EN_Project p, int *index, int linkType, int actionC
3456
3677
  if (p->hydraul.OpenHflag || p->quality.OpenQflag) return 262;
3457
3678
 
3458
3679
  // Check for valid input parameters
3459
- if (linkType < 0 || linkType > GPV || actionCode < EN_UNCONDITIONAL ||
3680
+ if (linkType < 0 || linkType > PCV || actionCode < EN_UNCONDITIONAL ||
3460
3681
  actionCode > EN_CONDITIONAL)
3461
3682
  {
3462
3683
  return 251;
@@ -3480,7 +3701,7 @@ int DLLEXPORT EN_setlinktype(EN_Project p, int *index, int linkType, int actionC
3480
3701
  if (oldType <= PIPE && linkType <= PIPE)
3481
3702
  {
3482
3703
  net->Link[i].Type = linkType;
3483
- if (linkType == CVPIPE) net->Link[i].Status = OPEN;
3704
+ if (linkType == CVPIPE) net->Link[i].InitStatus = OPEN;
3484
3705
  return 0;
3485
3706
  }
3486
3707
 
@@ -3583,8 +3804,6 @@ int DLLEXPORT EN_getlinkvalue(EN_Project p, int index, int property, double *val
3583
3804
  Slink *Link = net->Link;
3584
3805
  Spump *Pump = net->Pump;
3585
3806
  double *Ucf = p->Ucf;
3586
- double *LinkFlow = hyd->LinkFlow;
3587
- double *LinkSetting = hyd->LinkSetting;
3588
3807
 
3589
3808
  // Check for valid arguments
3590
3809
  *value = 0.0;
@@ -3622,18 +3841,19 @@ int DLLEXPORT EN_getlinkvalue(EN_Project p, int index, int property, double *val
3622
3841
  break;
3623
3842
 
3624
3843
  case EN_INITSTATUS:
3625
- if (Link[index].Status <= CLOSED) v = 0.0;
3844
+ if (Link[index].InitStatus <= CLOSED) v = 0.0;
3626
3845
  else v = 1.0;
3846
+ if (Link[index].Type > PUMP && Link[index].InitStatus > OPEN) v = 2.0;
3627
3847
  break;
3628
3848
 
3629
3849
  case EN_INITSETTING:
3630
- if (Link[index].Type == PIPE || Link[index].Type == CVPIPE)
3631
- {
3632
- return EN_getlinkvalue(p, index, EN_ROUGHNESS, value);
3633
- }
3634
- v = Link[index].Kc;
3850
+ v = Link[index].InitSetting;
3635
3851
  switch (Link[index].Type)
3636
3852
  {
3853
+ case CVPIPE:
3854
+ case PIPE:
3855
+ if (hyd->Formflag == DW) v = v * (1000.0 * Ucf[ELEV]);
3856
+ break;
3637
3857
  case PRV:
3638
3858
  case PSV:
3639
3859
  case PBV:
@@ -3656,7 +3876,7 @@ int DLLEXPORT EN_getlinkvalue(EN_Project p, int index, int property, double *val
3656
3876
 
3657
3877
  case EN_FLOW:
3658
3878
  if (hyd->LinkStatus[index] <= CLOSED) v = 0.0;
3659
- else v = LinkFlow[index] * Ucf[FLOW];
3879
+ else v = hyd->LinkFlow[index] * Ucf[FLOW];
3660
3880
  break;
3661
3881
 
3662
3882
  case EN_VELOCITY:
@@ -3664,7 +3884,7 @@ int DLLEXPORT EN_getlinkvalue(EN_Project p, int index, int property, double *val
3664
3884
  else if (hyd->LinkStatus[index] <= CLOSED) v = 0.0;
3665
3885
  else
3666
3886
  {
3667
- q = ABS(LinkFlow[index]);
3887
+ q = ABS(hyd->LinkFlow[index]);
3668
3888
  a = PI * SQR(Link[index].Diam) / 4.0;
3669
3889
  v = q / a * Ucf[VELOCITY];
3670
3890
  }
@@ -3683,6 +3903,8 @@ int DLLEXPORT EN_getlinkvalue(EN_Project p, int index, int property, double *val
3683
3903
  case EN_STATUS:
3684
3904
  if (hyd->LinkStatus[index] <= CLOSED) v = 0.0;
3685
3905
  else v = 1.0;
3906
+ if (Link[index].Type > PUMP &&
3907
+ hyd->LinkStatus[index] > OPEN) v = 2.0;
3686
3908
  break;
3687
3909
 
3688
3910
  case EN_SETTING:
@@ -3690,8 +3912,8 @@ int DLLEXPORT EN_getlinkvalue(EN_Project p, int index, int property, double *val
3690
3912
  {
3691
3913
  return EN_getlinkvalue(p, index, EN_ROUGHNESS, value);
3692
3914
  }
3693
- if (LinkSetting[index] == MISSING) v = 0.0;
3694
- else v = LinkSetting[index];
3915
+ if (hyd->LinkSetting[index] == MISSING) v = 0.0;
3916
+ else v = hyd->LinkSetting[index];
3695
3917
  switch (Link[index].Type)
3696
3918
  {
3697
3919
  case PRV:
@@ -3778,6 +4000,40 @@ int DLLEXPORT EN_getlinkvalue(EN_Project p, int index, int property, double *val
3778
4000
  v = (double)Pump[findpump(&p->network, index)].Epat;
3779
4001
  }
3780
4002
  break;
4003
+
4004
+ case EN_PCV_CURVE:
4005
+ if (Link[index].Type == PCV)
4006
+ {
4007
+ v = net->Valve[findvalve(&p->network, index)].Curve;
4008
+ }
4009
+ break;
4010
+
4011
+ case EN_GPV_CURVE:
4012
+ if (Link[index].Type == GPV)
4013
+ {
4014
+ v = Link[index].Kc;
4015
+ }
4016
+ break;
4017
+
4018
+ case EN_LINK_INCONTROL:
4019
+ v = (double)incontrols(p, LINK, index);
4020
+ break;
4021
+
4022
+ case EN_LEAK_AREA:
4023
+ v = Link[index].LeakArea * Ucf[LENGTH];
4024
+ break;
4025
+
4026
+ case EN_LEAK_EXPAN:
4027
+ v = Link[index].LeakExpan * Ucf[LENGTH];
4028
+ break;
4029
+
4030
+ case EN_LINK_LEAKAGE:
4031
+ v = findlinkleakage(p, index) * Ucf[FLOW];
4032
+ break;
4033
+
4034
+ case EN_VALVE_TYPE:
4035
+ if (Link[index].Type > PUMP) v = Link[index].Type;
4036
+ break;
3781
4037
 
3782
4038
  default:
3783
4039
  return 251;
@@ -3786,6 +4042,24 @@ int DLLEXPORT EN_getlinkvalue(EN_Project p, int index, int property, double *val
3786
4042
  return 0;
3787
4043
  }
3788
4044
 
4045
+ int DLLEXPORT EN_getlinkvalues(EN_Project p, int property, double *values)
4046
+ /*----------------------------------------------------------------
4047
+ ** Input: property = link property code (see EN_LinkProperty)
4048
+ ** Output: values = array of link property values
4049
+ ** Returns: error code
4050
+ ** Purpose: retrieves property values for all links
4051
+ **----------------------------------------------------------------
4052
+ */
4053
+ {
4054
+ int errcode = 0, i = 0;
4055
+ for(i = 1; i <= p->network.Nlinks; i++)
4056
+ {
4057
+ errcode = EN_getlinkvalue(p, i, property, &values[i-1]);
4058
+ if(errcode != 0) { return errcode; }
4059
+ }
4060
+ return 0;
4061
+ }
4062
+
3789
4063
  int DLLEXPORT EN_setlinkvalue(EN_Project p, int index, int property, double value)
3790
4064
  /*----------------------------------------------------------------
3791
4065
  ** Input: index = link index
@@ -3803,10 +4077,9 @@ int DLLEXPORT EN_setlinkvalue(EN_Project p, int index, int property, double valu
3803
4077
 
3804
4078
  Slink *Link = net->Link;
3805
4079
  double *Ucf = p->Ucf;
3806
- double *LinkSetting = hyd->LinkSetting;
3807
4080
  char s;
3808
4081
  double r;
3809
- int pumpIndex, patIndex, curveIndex;
4082
+ int pumpIndex, patIndex, curveIndex, valveType;
3810
4083
 
3811
4084
  if (!p->Openflag) return 102;
3812
4085
  if (index <= 0 || index > net->Nlinks) return 204;
@@ -3839,14 +4112,15 @@ int DLLEXPORT EN_setlinkvalue(EN_Project p, int index, int property, double valu
3839
4112
  if (value <= 0.0) return 211;
3840
4113
  Link[index].Kc = value;
3841
4114
  if (hyd->Formflag == DW) Link[index].Kc /= (1000.0 * Ucf[ELEV]);
3842
- resistcoeff(p, index);
4115
+ if (p->hydraul.OpenHflag) resistcoeff(p, index);
4116
+ else Link[index].InitSetting = Link[index].Kc;
3843
4117
  }
3844
4118
  break;
3845
4119
 
3846
4120
  case EN_MINORLOSS:
3847
4121
  if (Link[index].Type != PUMP)
3848
4122
  {
3849
- if (value <= 0.0) return 211;
4123
+ if (value < 0.0) return 211;
3850
4124
  Link[index].Km = 0.02517 * value / SQR(Link[index].Diam) /
3851
4125
  SQR(Link[index].Diam);
3852
4126
  }
@@ -3857,29 +4131,31 @@ int DLLEXPORT EN_setlinkvalue(EN_Project p, int index, int property, double valu
3857
4131
  // Cannot set status for a check valve
3858
4132
  if (Link[index].Type == CVPIPE) return 207;
3859
4133
  s = (char)ROUND(value);
3860
- if (s < 0 || s > 1) return 211;
4134
+ if (s < 0 || s > 2) return 211;
4135
+ s = s + CLOSED;
3861
4136
  if (property == EN_INITSTATUS)
3862
4137
  {
3863
- setlinkstatus(p, index, s, &Link[index].Status, &Link[index].Kc);
4138
+ Link[index].InitStatus = s;
3864
4139
  }
3865
4140
  else
3866
4141
  {
3867
- setlinkstatus(p, index, s, &hyd->LinkStatus[index], &LinkSetting[index]);
4142
+ setlinkstatus(p, index, s, &hyd->LinkStatus[index], &hyd->LinkSetting[index]);
3868
4143
  }
3869
4144
  break;
3870
4145
 
3871
4146
  case EN_INITSETTING:
3872
4147
  case EN_SETTING:
3873
- if (value < 0.0) return 211;
3874
4148
  if (Link[index].Type == PIPE || Link[index].Type == CVPIPE)
3875
4149
  {
3876
- return EN_setlinkvalue(p, index, EN_ROUGHNESS, value);
4150
+ EN_setlinkvalue(p, index, EN_ROUGHNESS, value);
4151
+ if (property == EN_INITSETTING) Link[index].InitSetting = Link[index].Kc;
3877
4152
  }
3878
4153
  else
3879
4154
  {
3880
4155
  switch (Link[index].Type)
3881
4156
  {
3882
4157
  case PUMP:
4158
+ if (value < 0.0) return 211;
3883
4159
  break;
3884
4160
  case PRV:
3885
4161
  case PSV:
@@ -3890,6 +4166,7 @@ int DLLEXPORT EN_setlinkvalue(EN_Project p, int index, int property, double valu
3890
4166
  value /= Ucf[FLOW];
3891
4167
  break;
3892
4168
  case TCV:
4169
+ case PCV:
3893
4170
  break;
3894
4171
  case GPV:
3895
4172
  return 207; // Cannot modify setting for GPV
@@ -3898,12 +4175,13 @@ int DLLEXPORT EN_setlinkvalue(EN_Project p, int index, int property, double valu
3898
4175
  }
3899
4176
  if (property == EN_INITSETTING)
3900
4177
  {
3901
- setlinksetting(p, index, value, &Link[index].Status, &Link[index].Kc);
4178
+ Link[index].Kc = value;
4179
+ Link[index].InitSetting = value;
3902
4180
  }
3903
4181
  else
3904
4182
  {
3905
4183
  setlinksetting(p, index, value, &hyd->LinkStatus[index],
3906
- &LinkSetting[index]);
4184
+ &hyd->LinkSetting[index]);
3907
4185
  }
3908
4186
  }
3909
4187
  break;
@@ -3942,11 +4220,6 @@ int DLLEXPORT EN_setlinkvalue(EN_Project p, int index, int property, double valu
3942
4220
  net->Pump[pumpIndex].Ptype = CONST_HP;
3943
4221
  net->Pump[pumpIndex].Hcurve = 0;
3944
4222
  net->Link[index].Km = value;
3945
- updatepumpparams(p, pumpIndex);
3946
- net->Pump[pumpIndex].R /= Ucf[POWER];
3947
- net->Pump[pumpIndex].Q0 /= Ucf[FLOW];
3948
- net->Pump[pumpIndex].Qmax /= Ucf[FLOW];
3949
- net->Pump[pumpIndex].Hmax /= Ucf[HEAD];
3950
4223
  }
3951
4224
  break;
3952
4225
 
@@ -3985,6 +4258,43 @@ int DLLEXPORT EN_setlinkvalue(EN_Project p, int index, int property, double valu
3985
4258
  net->Pump[pumpIndex].Epat = patIndex;
3986
4259
  }
3987
4260
  break;
4261
+
4262
+ case EN_PCV_CURVE:
4263
+ if (Link[index].Type == PCV)
4264
+ {
4265
+ curveIndex = ROUND(value);
4266
+ if (curveIndex < 0 || curveIndex > net->Ncurves) return 206;
4267
+ net->Valve[findvalve(&p->network, index)].Curve = curveIndex;
4268
+ }
4269
+ break;
4270
+
4271
+ case EN_GPV_CURVE:
4272
+ if (Link[index].Type == GPV)
4273
+ {
4274
+ curveIndex = ROUND(value);
4275
+ if (curveIndex < 0 || curveIndex > net->Ncurves) return 206;
4276
+ Link[index].Kc = curveIndex;
4277
+ if (hyd->OpenHflag == FALSE) Link[index].InitSetting = curveIndex;
4278
+ }
4279
+ break;
4280
+
4281
+ case EN_LEAK_AREA: // leak area in sq mm per 100 pipe length units
4282
+ if (value < 0.0) return 211;
4283
+ Link[index].LeakArea = value / Ucf[LENGTH];
4284
+ break;
4285
+
4286
+ case EN_LEAK_EXPAN: // leak area expansion slope (sq mm per unit of head)
4287
+ if (value < 0.0) return 211;
4288
+ Link[index].LeakExpan = value / Ucf[LENGTH];
4289
+ break;
4290
+
4291
+ case EN_VALVE_TYPE:
4292
+ if (hyd->OpenHflag || qual->OpenQflag) return 262; //Solver is running
4293
+ if (Link[index].Type <= PUMP) return 264; //Link not a valve
4294
+ valveType = ROUND(value);
4295
+ if (valveType < PRV || valveType > PCV) return 213; //Invalid valve type
4296
+ if (valveType == Link[index].Type) return 0; //No type change
4297
+ return changevalvetype(p, index, valveType); //See project.c
3988
4298
 
3989
4299
  default:
3990
4300
  return 251;
@@ -4043,20 +4353,20 @@ int DLLEXPORT EN_getvertexcount(EN_Project p, int index, int *count)
4043
4353
  */
4044
4354
  {
4045
4355
  Network *net = &p->network;
4046
-
4356
+
4047
4357
  Slink *Link = net->Link;
4048
4358
  Pvertices vertices;
4049
-
4359
+
4050
4360
  // Check that link exists
4051
4361
  *count = 0;
4052
4362
  if (!p->Openflag) return 102;
4053
4363
  if (index <= 0 || index > net->Nlinks) return 204;
4054
-
4364
+
4055
4365
  // Set count to number of vertices
4056
4366
  vertices = Link[index].Vertices;
4057
4367
  if (vertices) *count = vertices->Npts;
4058
4368
  return 0;
4059
- }
4369
+ }
4060
4370
 
4061
4371
  int DLLEXPORT EN_getvertex(EN_Project p, int index, int vertex, double *x, double *y)
4062
4372
  /*----------------------------------------------------------------
@@ -4070,25 +4380,54 @@ int DLLEXPORT EN_getvertex(EN_Project p, int index, int vertex, double *x, doubl
4070
4380
  */
4071
4381
  {
4072
4382
  Network *net = &p->network;
4073
-
4383
+
4074
4384
  Slink *Link = net->Link;
4075
4385
  Pvertices vertices;
4076
-
4386
+
4077
4387
  // Check that link exists
4078
4388
  *x = MISSING;
4079
4389
  *y = MISSING;
4080
4390
  if (!p->Openflag) return 102;
4081
4391
  if (index <= 0 || index > net->Nlinks) return 204;
4082
-
4392
+
4083
4393
  // Check that vertex exists
4084
4394
  vertices = Link[index].Vertices;
4085
4395
  if (vertices == NULL) return 255;
4086
4396
  if (vertex <= 0 || vertex > vertices->Npts) return 255;
4087
4397
  *x = vertices->X[vertex - 1];
4088
- *y = vertices->Y[vertex - 1];
4398
+ *y = vertices->Y[vertex - 1];
4089
4399
  return 0;
4090
4400
  }
4091
-
4401
+
4402
+ int DLLEXPORT EN_setvertex(EN_Project p, int index, int vertex, double x, double y)
4403
+ /*----------------------------------------------------------------
4404
+ ** Input: index = link index
4405
+ ** vertex = index of a link vertex point
4406
+ ** x = vertex point's X-coordinate
4407
+ ** y = vertex point's Y-coordinate
4408
+ ** Returns: error code
4409
+ ** Purpose: sets the coordinates of a vertex point in a link
4410
+ **----------------------------------------------------------------
4411
+ */
4412
+ {
4413
+ Network *net = &p->network;
4414
+
4415
+ Slink *Link = net->Link;
4416
+ Pvertices vertices;
4417
+
4418
+ // Check that link exists
4419
+ if (!p->Openflag) return 102;
4420
+ if (index <= 0 || index > net->Nlinks) return 204;
4421
+
4422
+ // Check that vertex exists
4423
+ vertices = Link[index].Vertices;
4424
+ if (vertices == NULL) return 255;
4425
+ if (vertex <= 0 || vertex > vertices->Npts) return 255;
4426
+ vertices->X[vertex - 1] = x;
4427
+ vertices->Y[vertex - 1] = y;
4428
+ return 0;
4429
+ }
4430
+
4092
4431
  int DLLEXPORT EN_setvertices(EN_Project p, int index, double *x, double *y, int count)
4093
4432
  /*----------------------------------------------------------------
4094
4433
  ** Input: index = link index
@@ -4101,11 +4440,11 @@ int DLLEXPORT EN_setvertices(EN_Project p, int index, double *x, double *y, int
4101
4440
  */
4102
4441
  {
4103
4442
  Network *net = &p->network;
4104
-
4443
+
4105
4444
  Slink *link;
4106
4445
  int i;
4107
4446
  int err = 0;
4108
-
4447
+
4109
4448
  // Check that link exists
4110
4449
  if (!p->Openflag) return 102;
4111
4450
  if (index <= 0 || index > net->Nlinks) return 204;
@@ -4113,7 +4452,7 @@ int DLLEXPORT EN_setvertices(EN_Project p, int index, double *x, double *y, int
4113
4452
 
4114
4453
  // Delete existing set of vertices
4115
4454
  freelinkvertices(link);
4116
-
4455
+
4117
4456
  // Add each new vertex to the link
4118
4457
  for (i = 0; i < count; i++)
4119
4458
  {
@@ -4122,7 +4461,7 @@ int DLLEXPORT EN_setvertices(EN_Project p, int index, double *x, double *y, int
4122
4461
  }
4123
4462
  if (err) freelinkvertices(link);
4124
4463
  return err;
4125
- }
4464
+ }
4126
4465
 
4127
4466
  /********************************************************************
4128
4467
 
@@ -4188,10 +4527,7 @@ int DLLEXPORT EN_setheadcurveindex(EN_Project p, int linkIndex, int curveIndex)
4188
4527
  {
4189
4528
  Network *net = &p->network;
4190
4529
 
4191
- double *Ucf = p->Ucf;
4192
4530
  int pumpIndex;
4193
- int oldCurveIndex;
4194
- int newCurveType;
4195
4531
  int err = 0;
4196
4532
  Spump *pump;
4197
4533
 
@@ -4201,43 +4537,12 @@ int DLLEXPORT EN_setheadcurveindex(EN_Project p, int linkIndex, int curveIndex)
4201
4537
  if (PUMP != net->Link[linkIndex].Type) return 0;
4202
4538
  if (curveIndex < 0 || curveIndex > net->Ncurves) return 206;
4203
4539
 
4204
- // Save values that need to be restored in case new curve is invalid
4205
- pumpIndex = findpump(net, linkIndex);
4206
- pump = &p->network.Pump[pumpIndex];
4207
- oldCurveIndex = pump->Hcurve;
4208
- newCurveType = p->network.Curve[curveIndex].Type;
4209
-
4210
4540
  // Assign the new curve to the pump
4211
- pump->Ptype = NOCURVE;
4541
+ pumpIndex = findpump(net, linkIndex);
4542
+ pump = &net->Pump[pumpIndex];
4212
4543
  pump->Hcurve = curveIndex;
4213
- if (curveIndex == 0) return 0;
4214
-
4215
- // Update the pump's head curve parameters (which also changes
4216
- // the new curve's Type to PUMP_CURVE)
4217
- err = updatepumpparams(p, pumpIndex);
4218
-
4219
- // If the parameter updating failed (new curve was not a valid pump curve)
4220
- // restore the pump's original curve and its parameters
4221
- if (err > 0)
4222
- {
4223
- p->network.Curve[curveIndex].Type = newCurveType;
4224
- pump->Ptype = NOCURVE;
4225
- pump->Hcurve = oldCurveIndex;
4226
- if (oldCurveIndex == 0) return err;
4227
- updatepumpparams(p, pumpIndex);
4228
- }
4229
-
4230
- // Convert the units of the updated pump parameters to feet and cfs
4231
- if (pump->Ptype == POWER_FUNC)
4232
- {
4233
- pump->H0 /= Ucf[HEAD];
4234
- pump->R *= (pow(Ucf[FLOW], pump->N) / Ucf[HEAD]);
4235
- }
4236
- pump->Q0 /= Ucf[FLOW];
4237
- pump->Qmax /= Ucf[FLOW];
4238
- pump->Hmax /= Ucf[HEAD];
4239
-
4240
- return err;
4544
+ net->Link[linkIndex].Km = 0.0;
4545
+ return 0;
4241
4546
  }
4242
4547
 
4243
4548
  /********************************************************************
@@ -4246,7 +4551,7 @@ int DLLEXPORT EN_setheadcurveindex(EN_Project p, int linkIndex, int curveIndex)
4246
4551
 
4247
4552
  ********************************************************************/
4248
4553
 
4249
- int DLLEXPORT EN_addpattern(EN_Project p, char *id)
4554
+ int DLLEXPORT EN_addpattern(EN_Project p, const char *id)
4250
4555
  /*----------------------------------------------------------------
4251
4556
  ** Input: id = time pattern ID name
4252
4557
  ** Output: none
@@ -4294,6 +4599,70 @@ int DLLEXPORT EN_addpattern(EN_Project p, char *id)
4294
4599
  return 0;
4295
4600
  }
4296
4601
 
4602
+ int DLLEXPORT EN_loadpatternfile(EN_Project p, const char *filename, const char *id)
4603
+ /*----------------------------------------------------------------
4604
+ ** Input: filename = name of the file containing pattern data
4605
+ ** id = ID for the new pattern
4606
+ ** Output: none
4607
+ ** Returns: error code
4608
+ ** Purpose: loads time patterns from a file into a project under a specific pattern ID
4609
+ **----------------------------------------------------------------
4610
+ */
4611
+ {
4612
+ FILE *file;
4613
+ char line[MAXLINE+1];
4614
+ char *tok;
4615
+ int err = 0;
4616
+ int i;
4617
+ int len = 0;
4618
+ double value;
4619
+ double *values = NULL;
4620
+ int CHUNK = 50;
4621
+
4622
+ if (!p->Openflag) return 102;
4623
+
4624
+ file = fopen(filename, "r");
4625
+ if (file == NULL) return 302;
4626
+
4627
+ // Add a new pattern or use an existing pattern.
4628
+ err = EN_getpatternindex(p, id, &i);
4629
+ if (err == 205) {
4630
+ if ((err = EN_addpattern(p, id)) != 0) {
4631
+ fclose(file);
4632
+ return err;
4633
+ }
4634
+ i = p->network.Npats;
4635
+ }
4636
+
4637
+ // Read pattern values
4638
+ while (fgets(line, sizeof(line), file) != NULL) {
4639
+
4640
+ // Skip lines that don't contain valid numbers
4641
+ tok = strtok(line, SEPSTR);
4642
+ if (tok == NULL) continue;
4643
+ if (!getfloat(tok, &value)) continue;
4644
+
4645
+ // Resize multiplier array if it's full
4646
+ if (len % CHUNK == 0) {
4647
+ values = (double *) realloc(values, (len + CHUNK) * sizeof(double));
4648
+
4649
+ // Abort if memory allocation error
4650
+ if (values == NULL) {
4651
+ fclose(file);
4652
+ return 101;
4653
+ }
4654
+ }
4655
+ values[len] = value;
4656
+ len++;
4657
+ }
4658
+ fclose(file);
4659
+
4660
+ // Transfer multipliers to pattern
4661
+ err = EN_setpattern(p, i, values, len);
4662
+ free(values);
4663
+ return err;
4664
+ }
4665
+
4297
4666
  int DLLEXPORT EN_deletepattern(EN_Project p, int index)
4298
4667
  /*----------------------------------------------------------------
4299
4668
  ** Input: index = index of the pattern to delete
@@ -4323,6 +4692,10 @@ int DLLEXPORT EN_deletepattern(EN_Project p, int index)
4323
4692
  if (hyd->Epat == index) hyd->Epat = 0;
4324
4693
  else if (hyd->Epat > index) hyd->Epat--;
4325
4694
 
4695
+ // Modify global default demand pattern
4696
+ if (hyd->DefPat == index) hyd->DefPat = 0;
4697
+ else if (hyd->DefPat > index) hyd->DefPat--;
4698
+
4326
4699
  // Free the pattern's factor array
4327
4700
  FREE(net->Pattern[index].F);
4328
4701
  FREE(net->Pattern[index].Comment);
@@ -4334,7 +4707,7 @@ int DLLEXPORT EN_deletepattern(EN_Project p, int index)
4334
4707
  return 0;
4335
4708
  }
4336
4709
 
4337
- int DLLEXPORT EN_getpatternindex(EN_Project p, char *id, int *index)
4710
+ int DLLEXPORT EN_getpatternindex(EN_Project p, const char *id, int *index)
4338
4711
  /*----------------------------------------------------------------
4339
4712
  ** Input: id = time pattern name
4340
4713
  ** Output: index = time pattern index
@@ -4375,7 +4748,7 @@ int DLLEXPORT EN_getpatternid(EN_Project p, int index, char *id)
4375
4748
  return 0;
4376
4749
  }
4377
4750
 
4378
- int DLLEXPORT EN_setpatternid(EN_Project p, int index, char *id)
4751
+ int DLLEXPORT EN_setpatternid(EN_Project p, int index, const char *id)
4379
4752
  /*----------------------------------------------------------------
4380
4753
  ** Input: index = time pattern index
4381
4754
  ** id = time pattern ID name
@@ -4472,7 +4845,7 @@ int DLLEXPORT EN_getaveragepatternvalue(EN_Project p, int index, double *value)
4472
4845
 
4473
4846
  *value = 0.0;
4474
4847
  if (!p->Openflag) return 102;
4475
- if (index < 1 || index > net->Npats) return 205;
4848
+ if (index < 0 || index > net->Npats) return 205;
4476
4849
  for (i = 0; i < Pattern[index].Length; i++)
4477
4850
  {
4478
4851
  *value += (double)Pattern[index].F[i];
@@ -4519,7 +4892,7 @@ int DLLEXPORT EN_setpattern(EN_Project p, int index, double *values, int len)
4519
4892
 
4520
4893
  ********************************************************************/
4521
4894
 
4522
- int DLLEXPORT EN_addcurve(EN_Project p, char *id)
4895
+ int DLLEXPORT EN_addcurve(EN_Project p, const char *id)
4523
4896
  /*----------------------------------------------------------------
4524
4897
  ** Input: id = data curve ID name
4525
4898
  ** Output: none
@@ -4611,7 +4984,7 @@ int DLLEXPORT EN_deletecurve(EN_Project p, int index)
4611
4984
  return 0;
4612
4985
  }
4613
4986
 
4614
- int DLLEXPORT EN_getcurveindex(EN_Project p, char *id, int *index)
4987
+ int DLLEXPORT EN_getcurveindex(EN_Project p, const char *id, int *index)
4615
4988
  /*----------------------------------------------------------------
4616
4989
  ** Input: id = data curve name
4617
4990
  ** Output: index = data curve index
@@ -4643,7 +5016,7 @@ int DLLEXPORT EN_getcurveid(EN_Project p, int index, char *id)
4643
5016
  return 0;
4644
5017
  }
4645
5018
 
4646
- int DLLEXPORT EN_setcurveid(EN_Project p, int index, char *id)
5019
+ int DLLEXPORT EN_setcurveid(EN_Project p, int index, const char *id)
4647
5020
  /*----------------------------------------------------------------
4648
5021
  ** Input: index = data curve index
4649
5022
  ** id = data curve ID name
@@ -4699,6 +5072,23 @@ int DLLEXPORT EN_getcurvetype(EN_Project p, int index, int *type)
4699
5072
  return 0;
4700
5073
  }
4701
5074
 
5075
+ int DLLEXPORT EN_setcurvetype(EN_Project p, int index, int type)
5076
+ /*----------------------------------------------------------------
5077
+ ** Input: index = data curve index
5078
+ ** type = type of data curve (see EN_CurveType)
5079
+ ** Returns: error code
5080
+ ** Purpose: sets the type assigned to a data curve
5081
+ **----------------------------------------------------------------
5082
+ */
5083
+ {
5084
+ Network *net = &p->network;
5085
+ if (!p->Openflag) return 102;
5086
+ if (index < 1 || index > net->Ncurves) return 206;
5087
+ if (type < 0 || type > EN_VALVE_CURVE) return 251;
5088
+ net->Curve[index].Type = type;
5089
+ return 0;
5090
+ }
5091
+
4702
5092
  int DLLEXPORT EN_getcurvevalue(EN_Project p, int curveIndex, int pointIndex,
4703
5093
  double *x, double *y)
4704
5094
  /*----------------------------------------------------------------
@@ -4768,9 +5158,7 @@ int DLLEXPORT EN_setcurvevalue(EN_Project p, int curveIndex, int pointIndex,
4768
5158
  // Insert new point into curve
4769
5159
  curve->X[n] = x;
4770
5160
  curve->Y[n] = y;
4771
-
4772
- // Adjust parameters for pumps using curve as a head curve
4773
- return adjustpumpparams(p, curveIndex);
5161
+ return 0;
4774
5162
  }
4775
5163
 
4776
5164
  int DLLEXPORT EN_getcurve(EN_Project p, int index, char *id, int *nPoints,
@@ -4842,9 +5230,7 @@ int DLLEXPORT EN_setcurve(EN_Project p, int index, double *xValues,
4842
5230
  curve->X[j] = xValues[j];
4843
5231
  curve->Y[j] = yValues[j];
4844
5232
  }
4845
-
4846
- // Adjust parameters for pumps using curve as a head curve
4847
- return adjustpumpparams(p, index);
5233
+ return 0;
4848
5234
  }
4849
5235
 
4850
5236
  /********************************************************************
@@ -4870,84 +5256,30 @@ int DLLEXPORT EN_addcontrol(EN_Project p, int type, int linkIndex, double settin
4870
5256
  */
4871
5257
  {
4872
5258
  Network *net = &p->network;
4873
- Parser *parser = &p->parser;
4874
-
4875
- char status = ACTIVE;
4876
- int n;
4877
- long t = 0;
4878
- double s = setting, lvl = level;
4879
- double *Ucf = p->Ucf;
4880
- Scontrol *control;
4881
5259
 
5260
+ int err, n;
5261
+ Scontrol ctrl;
4882
5262
 
4883
5263
  // Check that project exists
4884
5264
  if (!p->Openflag) return 102;
4885
5265
 
4886
5266
  // Check that controlled link exists
4887
5267
  if (linkIndex <= 0 || linkIndex > net->Nlinks) return 204;
4888
-
4889
- // Cannot control check valve
4890
- if (net->Link[linkIndex].Type == CVPIPE) return 207;
4891
-
4892
- // Check for valid parameters
4893
- if (type < 0 || type > EN_TIMEOFDAY) return 251;
4894
- if (type == EN_LOWLEVEL || type == EN_HILEVEL)
4895
- {
4896
- if (nodeIndex < 1 || nodeIndex > net->Nnodes) return 203;
4897
- }
4898
- else nodeIndex = 0;
4899
- if (s < 0.0 || lvl < 0.0) return 202;
4900
-
4901
- // Adjust units of control parameters
4902
- switch (net->Link[linkIndex].Type)
4903
- {
4904
- case PRV:
4905
- case PSV:
4906
- case PBV:
4907
- s /= Ucf[PRESSURE];
4908
- break;
4909
- case FCV:
4910
- s /= Ucf[FLOW];
4911
- break;
4912
- case GPV:
4913
- if (s == 0.0) status = CLOSED;
4914
- else if (s == 1.0) status = OPEN;
4915
- else return 202;
4916
- s = net->Link[linkIndex].Kc;
4917
- break;
4918
- case PIPE:
4919
- case PUMP:
4920
- status = OPEN;
4921
- if (s == 0.0) status = CLOSED;
4922
- default:
4923
- break;
4924
- }
4925
-
4926
- if (type == LOWLEVEL || type == HILEVEL)
4927
- {
4928
- if (nodeIndex > net->Njuncs) lvl = net->Node[nodeIndex].El + level / Ucf[ELEV];
4929
- else lvl = net->Node[nodeIndex].El + level / Ucf[PRESSURE];
4930
- }
4931
- if (type == TIMER) t = (long)ROUND(lvl);
4932
- if (type == TIMEOFDAY) t = (long)ROUND(lvl) % SECperDAY;
5268
+
5269
+ // Insert control properties into a temporary struct
5270
+ err = setcontrol(p, type, linkIndex, setting, nodeIndex, level, &ctrl);
5271
+ if (err > 0) return err;
4933
5272
 
4934
5273
  // Expand project's array of controls
4935
5274
  n = net->Ncontrols + 1;
4936
5275
  net->Control = (Scontrol *)realloc(net->Control, (n + 1) * sizeof(Scontrol));
4937
5276
 
4938
5277
  // Set properties of the new control
4939
- control = &net->Control[n];
4940
- control->Type = (char)type;
4941
- control->Link = linkIndex;
4942
- control->Node = nodeIndex;
4943
- control->Status = status;
4944
- control->Setting = s;
4945
- control->Grade = lvl;
4946
- control->Time = t;
5278
+ net->Control[n] = ctrl;
4947
5279
 
4948
5280
  // Update number of controls
4949
5281
  net->Ncontrols = n;
4950
- parser->MaxControls = n;
5282
+ p->parser.MaxControls = n;
4951
5283
 
4952
5284
  // Replace the control's index
4953
5285
  *index = n;
@@ -5030,8 +5362,8 @@ int DLLEXPORT EN_getcontrol(EN_Project p, int index, int *type, int *linkIndex,
5030
5362
  break;
5031
5363
  }
5032
5364
  }
5033
- else if (control->Status == OPEN) s = 1.0;
5034
- else s = 0.0;
5365
+ else if (control->Status == OPEN) s = SET_OPEN;
5366
+ else s = SET_CLOSED;
5035
5367
 
5036
5368
  // Retrieve level value for a node level control
5037
5369
  *nodeIndex = control->Node;
@@ -5053,8 +5385,8 @@ int DLLEXPORT EN_getcontrol(EN_Project p, int index, int *type, int *linkIndex,
5053
5385
  {
5054
5386
  lvl = (double)control->Time;
5055
5387
  }
5056
- *setting = (double)s;
5057
- *level = (double)lvl;
5388
+ *setting = s;
5389
+ *level = lvl;
5058
5390
  return 0;
5059
5391
  }
5060
5392
 
@@ -5076,81 +5408,63 @@ int DLLEXPORT EN_setcontrol(EN_Project p, int index, int type, int linkIndex,
5076
5408
  {
5077
5409
  Network *net = &p->network;
5078
5410
 
5079
- char status = ACTIVE;
5080
- long t = 0;
5081
- double s = setting, lvl = level;
5082
- double *Ucf = p->Ucf;
5083
- Slink *link;
5084
- Scontrol *control;
5411
+ int err;
5412
+ Scontrol ctrl;
5085
5413
 
5086
5414
  // Check that project exists
5087
5415
  if (!p->Openflag) return 102;
5088
5416
 
5089
5417
  // Check that control exists
5090
5418
  if (index <= 0 || index > net->Ncontrols) return 241;
5091
- control = &net->Control[index];
5092
5419
 
5093
5420
  // Check that controlled link exists (0 index de-activates the control)
5094
5421
  if (linkIndex == 0)
5095
5422
  {
5096
- control->Link = 0;
5423
+ net->Control[index].Link = 0;
5097
5424
  return 0;
5098
5425
  }
5099
5426
  if (linkIndex < 0 || linkIndex > net->Nlinks) return 204;
5100
5427
 
5101
- // Cannot control check valve
5102
- if (net->Link[linkIndex].Type == CVPIPE) return 207;
5428
+ // Assign new set of properties to control
5429
+ err = setcontrol(p, type, linkIndex, setting, nodeIndex, level, &ctrl);
5430
+ if (err > 0) return err;
5431
+ net->Control[index] = ctrl;
5432
+ return 0;
5433
+ }
5103
5434
 
5104
- // Check for valid control properties
5105
- if (type < 0 || type > EN_TIMEOFDAY) return 251;
5106
- if (type == EN_LOWLEVEL || type == EN_HILEVEL)
5107
- {
5108
- if (nodeIndex < 1 || nodeIndex > net->Nnodes) return 203;
5109
- }
5110
- else nodeIndex = 0;
5111
- if (s < 0.0 || lvl < 0.0) return 202;
5112
5435
 
5113
- // Adjust units of control's properties
5114
- link = &net->Link[linkIndex];
5115
- switch (link->Type)
5116
- {
5117
- case PRV:
5118
- case PSV:
5119
- case PBV:
5120
- s /= Ucf[PRESSURE];
5121
- break;
5122
- case FCV:
5123
- s /= Ucf[FLOW];
5124
- break;
5125
- case GPV:
5126
- if (s == 0.0) status = CLOSED;
5127
- else if (s == 1.0) status = OPEN;
5128
- else return 202;
5129
- s = link->Kc;
5130
- break;
5131
- case PIPE:
5132
- case PUMP:
5133
- status = OPEN;
5134
- if (s == 0.0) status = CLOSED;
5135
- default:
5136
- break;
5137
- }
5138
- if (type == LOWLEVEL || type == HILEVEL)
5139
- {
5140
- if (nodeIndex > net->Njuncs) lvl = net->Node[nodeIndex].El + level / Ucf[ELEV];
5141
- else lvl = net->Node[nodeIndex].El + level / Ucf[PRESSURE];
5142
- }
5143
- if (type == TIMER) t = (long)ROUND(lvl);
5144
- if (type == TIMEOFDAY) t = (long)ROUND(lvl) % SECperDAY;
5145
-
5146
- /* Reset control's parameters */
5147
- control->Type = (char)type;
5148
- control->Link = linkIndex;
5149
- control->Node = nodeIndex;
5150
- control->Status = status;
5151
- control->Setting = s;
5152
- control->Grade = lvl;
5153
- control->Time = t;
5436
+ int DLLEXPORT EN_getcontrolenabled(EN_Project p, int index, int *enabled)
5437
+ {
5438
+ Network *net = &p->network;
5439
+ Scontrol *control;
5440
+
5441
+ // Check for valid arguments
5442
+ if (!p->Openflag)
5443
+ return 102;
5444
+ if (index <= 0 || index > net->Ncontrols)
5445
+ return 241;
5446
+
5447
+ control = &net->Control[index];
5448
+ *enabled = control->isEnabled;
5449
+ return 0;
5450
+ }
5451
+
5452
+
5453
+ int DLLEXPORT EN_setcontrolenabled(EN_Project p, int index, int enabled)
5454
+ {
5455
+ Network *net = &p->network;
5456
+ Scontrol *control;
5457
+
5458
+ // Check for valid arguments
5459
+ if (enabled != TRUE && enabled != FALSE)
5460
+ return 202; // illegal numeric value
5461
+ if (!p->Openflag)
5462
+ return 102;
5463
+ if (index <= 0 || index > net->Ncontrols)
5464
+ return 241;
5465
+
5466
+ control = &net->Control[index];
5467
+ control->isEnabled = enabled;
5154
5468
  return 0;
5155
5469
  }
5156
5470
 
@@ -5404,7 +5718,7 @@ int DLLEXPORT EN_setpremisestatus(EN_Project p, int ruleIndex, int premiseIndex,
5404
5718
  ** Input: ruleIndex = rule index
5405
5719
  ** premiseIndex = premise index
5406
5720
  ** status = object status being tested against
5407
- ** (see EN_RuleStatus))
5721
+ ** (see EN_RuleStatus)
5408
5722
  ** Output: none
5409
5723
  ** Returns: error code
5410
5724
  ** Purpose: sets the status of an object being tested against
@@ -5456,7 +5770,7 @@ int DLLEXPORT EN_getthenaction(EN_Project p, int ruleIndex, int actionIndex,
5456
5770
  ** Input: ruleIndex = rule index
5457
5771
  ** actionIndex = index of a rule's THEN actions
5458
5772
  ** Output: linkIndex = index of link appearing in the action
5459
- ** status = status assigned to the link (see EN_RuleStatus))
5773
+ ** status = status assigned to the link (see EN_RuleStatus)
5460
5774
  ** setting = setting assigned to the link
5461
5775
  ** Returns: error code
5462
5776
  ** Purpose: retrieves the properties of a rule's THEN action
@@ -5484,7 +5798,7 @@ int DLLEXPORT EN_setthenaction(EN_Project p, int ruleIndex, int actionIndex,
5484
5798
  ** Input: ruleIndex = rule index
5485
5799
  ** actionIndex = index of a rule's THEN actions
5486
5800
  ** linkIndex = index of link appearing in the action
5487
- ** status = status assigned to the link (see EN_RuleStatus))
5801
+ ** status = status assigned to the link (see EN_RuleStatus)
5488
5802
  ** setting = setting assigned to the link
5489
5803
  ** Returns: error code
5490
5804
  ** Purpose: sets the properties of a rule's THEN action
@@ -5512,7 +5826,7 @@ int DLLEXPORT EN_getelseaction(EN_Project p, int ruleIndex, int actionIndex,
5512
5826
  ** Input: ruleIndex = rule index
5513
5827
  ** actionIndex = index of a rule's ELSE actions
5514
5828
  ** Output: linkIndex = index of link appearing in the action
5515
- ** status = status assigned to the link (see EN_RuleStatus))
5829
+ ** status = status assigned to the link (see EN_RuleStatus)
5516
5830
  ** setting = setting assigned to the link
5517
5831
  ** Returns: error code
5518
5832
  ** Purpose: retrieves the properties of a rule's ELSE action
@@ -5540,7 +5854,7 @@ int DLLEXPORT EN_setelseaction(EN_Project p, int ruleIndex, int actionIndex,
5540
5854
  ** Input: ruleIndex = rule index
5541
5855
  ** actionIndex = index of a rule's ELSE actions
5542
5856
  ** linkIndex = index of link appearing in the action
5543
- ** status = status assigned to the link (see EN_RuleStatus))
5857
+ ** status = status assigned to the link (see EN_RuleStatus)
5544
5858
  ** setting = setting assigned to the link
5545
5859
  ** Returns: error code
5546
5860
  ** Purpose: sets the properties of a rule's ELSE action
@@ -5576,3 +5890,41 @@ int DLLEXPORT EN_setrulepriority(EN_Project p, int index, double priority)
5576
5890
  p->network.Rule[index].priority = priority;
5577
5891
  return 0;
5578
5892
  }
5893
+
5894
+
5895
+ int DLLEXPORT EN_getruleenabled(EN_Project p, int index, int *enabled)
5896
+ {
5897
+ Network *net = &p->network;
5898
+ Srule *rule;
5899
+
5900
+ // Check for valid arguments
5901
+ if (!p->Openflag)
5902
+ return 102;
5903
+ if (index <= 0 || index > net->Nrules)
5904
+ return 241;
5905
+
5906
+ rule = &net->Rule[index];
5907
+ *enabled = rule->isEnabled;
5908
+ return 0;
5909
+ }
5910
+
5911
+
5912
+ int DLLEXPORT EN_setruleenabled(EN_Project p, int index, int enabled)
5913
+ {
5914
+ Network *net = &p->network;
5915
+ Srule *rule;
5916
+
5917
+ // Check for valid arguments
5918
+ if (enabled != TRUE && enabled != FALSE)
5919
+ return 202; // illegal numeric value
5920
+ if (!p->Openflag)
5921
+ return 102;
5922
+ if (index <= 0 || index > net->Nrules)
5923
+ return 241;
5924
+
5925
+ rule = &net->Rule[index];
5926
+ rule->isEnabled = enabled;
5927
+ return 0;
5928
+ }
5929
+
5930
+