epyt-flow 0.13.1__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 (63) 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 +2 -2
  44. epyt_flow/VERSION +1 -1
  45. epyt_flow/__init__.py +1 -1
  46. epyt_flow/gym/scenario_control_env.py +26 -3
  47. epyt_flow/simulation/backend/my_epyt.py +58 -13
  48. epyt_flow/simulation/events/quality_events.py +6 -6
  49. epyt_flow/simulation/events/sensor_faults.py +24 -24
  50. epyt_flow/simulation/events/system_event.py +3 -3
  51. epyt_flow/simulation/scada/scada_data.py +10 -14
  52. epyt_flow/simulation/scenario_simulator.py +100 -20
  53. epyt_flow/topology.py +8 -1
  54. epyt_flow/uncertainty/model_uncertainty.py +292 -150
  55. epyt_flow/uncertainty/uncertainties.py +2 -2
  56. {epyt_flow-0.13.1.dist-info → epyt_flow-0.14.1.dist-info}/METADATA +4 -4
  57. {epyt_flow-0.13.1.dist-info → epyt_flow-0.14.1.dist-info}/RECORD +60 -54
  58. {epyt_flow-0.13.1.dist-info → epyt_flow-0.14.1.dist-info}/WHEEL +1 -1
  59. epyt_flow/EPANET/EPANET/SRC_engines/Readme_SRC_Engines.txt +0 -18
  60. epyt_flow/EPANET/EPANET/SRC_engines/epanet2.def +0 -131
  61. epyt_flow/EPANET/EPANET/SRC_engines/main.c +0 -93
  62. {epyt_flow-0.13.1.dist-info → epyt_flow-0.14.1.dist-info}/licenses/LICENSE +0 -0
  63. {epyt_flow-0.13.1.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: hydraul.c
6
6
  Description: implements EPANET's hydraulic engine
7
7
  Authors: see AUTHORS
8
8
  Copyright: see AUTHORS
9
9
  License: see LICENSE
10
- Last Updated: 12/05/2019
10
+ Last Updated: 04/19/2025
11
11
  ******************************************************************************
12
12
  */
13
13
 
@@ -23,6 +23,7 @@
23
23
  const double QZERO = 1.e-6; // Equivalent to zero flow in cfs
24
24
 
25
25
  // Imported functions
26
+ extern int validateproject(Project *);
26
27
  extern int createsparse(Project *);
27
28
  extern void freesparse(Project *);
28
29
  extern int hydsolve(Project *, int *, double *);
@@ -34,11 +35,11 @@ void initlinkflow(Project *, int, char, double);
34
35
  void demands(Project *);
35
36
  int controls(Project *);
36
37
  long timestep(Project *);
37
- void controltimestep(Project *, long *);
38
38
  void ruletimestep(Project *, long *);
39
39
  void addenergy(Project *, long);
40
40
  void tanklevels(Project *, long);
41
41
  void resetpumpflow(Project *, int);
42
+ void getallpumpsenergy(Project *);
42
43
 
43
44
  int openhyd(Project *pr)
44
45
  /*
@@ -52,33 +53,27 @@ int openhyd(Project *pr)
52
53
  int i;
53
54
  int errcode = 0;
54
55
  Slink *link;
55
-
56
- // Check for too few nodes & no fixed grade nodes
57
- if (pr->network.Nnodes < 2) errcode = 223;
58
- else if (pr->network.Ntanks == 0) errcode = 224;
56
+
57
+ // Check for valid project data (see VALIDATE.C)
58
+ errcode = validateproject(pr);
59
+ if (errcode > 0) return errcode;
59
60
 
60
61
  // Allocate memory for sparse matrix structures (see SMATRIX.C)
61
62
  ERRCODE(createsparse(pr));
62
63
 
63
64
  // Allocate memory for hydraulic variables
64
65
  ERRCODE(allocmatrix(pr));
65
-
66
+
66
67
  // Check for unconnected nodes
67
- if (!errcode) for (i = 1; i <= pr->network.Njuncs; i++)
68
- {
69
- if (pr->network.Adjlist[i] == NULL)
70
- {
71
- errcode = 233;
72
- break;
73
- }
74
- }
68
+ ERRCODE(unlinked(pr));
75
69
 
76
70
  // Initialize link flows
77
71
  if (!errcode) for (i = 1; i <= pr->network.Nlinks; i++)
78
72
  {
79
73
  link = &pr->network.Link[i];
80
- initlinkflow(pr, i, link->Status, link->Kc);
74
+ initlinkflow(pr, i, link->InitStatus, link->Kc);
81
75
  }
76
+ else closehyd(pr);
82
77
  return errcode;
83
78
  }
84
79
 
@@ -112,8 +107,10 @@ void inithyd(Project *pr, int initflag)
112
107
  hyd->OldStatus[net->Nlinks+i] = TEMPCLOSED;
113
108
  }
114
109
 
115
- // Initialize emitter flows
110
+ // Initialize node outflows
111
+ memset(hyd->DemandFlow,0,(net->Nnodes+1)*sizeof(double));
116
112
  memset(hyd->EmitterFlow,0,(net->Nnodes+1)*sizeof(double));
113
+ memset(hyd->LeakageFlow,0,(net->Nnodes+1)*sizeof(double));
117
114
  for (i = 1; i <= net->Nnodes; i++)
118
115
  {
119
116
  net->Node[i].ResultIndex = i;
@@ -127,9 +124,22 @@ void inithyd(Project *pr, int initflag)
127
124
  link->ResultIndex = i;
128
125
 
129
126
  // Initialize status and setting
130
- hyd->LinkStatus[i] = link->Status;
131
- hyd->LinkSetting[i] = link->Kc;
132
-
127
+ hyd->LinkStatus[i] = link->InitStatus;
128
+ hyd->LinkSetting[i] = link->InitSetting;
129
+
130
+ // Setting of non-ACTIVE FCV, PRV, PSV valves is "MISSING"
131
+ switch (link->Type)
132
+ {
133
+ case FCV:
134
+ case PRV:
135
+ case PSV:
136
+ if (link->InitStatus != ACTIVE)
137
+ {
138
+ link->Kc = MISSING;
139
+ hyd->LinkSetting[i] = MISSING;
140
+ }
141
+ }
142
+
133
143
  // Compute flow resistance
134
144
  resistcoeff(pr, i);
135
145
 
@@ -163,7 +173,12 @@ void inithyd(Project *pr, int initflag)
163
173
  pump->Energy.KwHrsPerFlow = 0.0;
164
174
  pump->Energy.MaxKwatts = 0.0;
165
175
  pump->Energy.TotalCost = 0.0;
176
+ pump->Energy.CurrentPower = 0.0;
177
+ pump->Energy.CurrentEffic = 0.0;
166
178
  }
179
+
180
+ // Initialize flow balance
181
+ startflowbalance(pr);
167
182
 
168
183
  // Re-position hydraulics file
169
184
  if (pr->outfile.Saveflag)
@@ -196,7 +211,7 @@ int runhyd(Project *pr, long *t)
196
211
  int iter; // Iteration count
197
212
  int errcode; // Error code
198
213
  double relerr; // Solution accuracy
199
-
214
+
200
215
  // Find new demands & control actions
201
216
  *t = time->Htime;
202
217
  demands(pr);
@@ -239,6 +254,9 @@ int nexthyd(Project *pr, long *tstep)
239
254
  long hydstep; // Actual time step
240
255
  int errcode = 0; // Error code
241
256
 
257
+ // Compute current power and efficiency of all pumps
258
+ getallpumpsenergy(pr);
259
+
242
260
  // Save current results to hydraulics file and
243
261
  // force end of simulation if Haltflag is active
244
262
  if (pr->outfile.Saveflag) errcode = savehyd(pr, &time->Htime);
@@ -250,9 +268,12 @@ int nexthyd(Project *pr, long *tstep)
250
268
  if (time->Htime < time->Dur) hydstep = timestep(pr);
251
269
  if (pr->outfile.Saveflag) errcode = savehydstep(pr,&hydstep);
252
270
 
253
- // Compute pumping energy
271
+ // Accumulate pumping energy
254
272
  if (time->Dur == 0) addenergy(pr,0);
255
273
  else if (time->Htime < time->Dur) addenergy(pr,hydstep);
274
+
275
+ // Update flow balance
276
+ updateflowbalance(pr, hydstep);
256
277
 
257
278
  // More time remains - update current time
258
279
  if (time->Htime < time->Dur)
@@ -267,6 +288,8 @@ int nexthyd(Project *pr, long *tstep)
267
288
  // No more time remains - force completion of analysis
268
289
  else
269
290
  {
291
+ endflowbalance(pr);
292
+ if (pr->report.Statflag) writeflowbalance(pr);
270
293
  time->Htime++;
271
294
  if (pr->quality.OpenQflag) time->Qtime++;
272
295
  }
@@ -286,6 +309,7 @@ void closehyd(Project *pr)
286
309
  {
287
310
  freesparse(pr);
288
311
  freematrix(pr);
312
+ freeadjlists(&pr->network);
289
313
  }
290
314
 
291
315
 
@@ -305,16 +329,12 @@ int allocmatrix(Project *pr)
305
329
 
306
330
  hyd->P = (double *) calloc(net->Nlinks+1,sizeof(double));
307
331
  hyd->Y = (double *) calloc(net->Nlinks+1,sizeof(double));
308
- hyd->DemandFlow = (double *) calloc(net->Nnodes + 1, sizeof(double));
309
- hyd->EmitterFlow = (double *) calloc(net->Nnodes+1, sizeof(double));
310
332
  hyd->Xflow = (double *) calloc(MAX((net->Nnodes+1), (net->Nlinks+1)),
311
333
  sizeof(double));
312
334
  hyd->OldStatus = (StatusType *) calloc(net->Nlinks+net->Ntanks+1,
313
335
  sizeof(StatusType));
314
336
  ERRCODE(MEMCHECK(hyd->P));
315
337
  ERRCODE(MEMCHECK(hyd->Y));
316
- ERRCODE(MEMCHECK(hyd->DemandFlow));
317
- ERRCODE(MEMCHECK(hyd->EmitterFlow));
318
338
  ERRCODE(MEMCHECK(hyd->Xflow));
319
339
  ERRCODE(MEMCHECK(hyd->OldStatus));
320
340
  return errcode;
@@ -334,8 +354,6 @@ void freematrix(Project *pr)
334
354
 
335
355
  free(hyd->P);
336
356
  free(hyd->Y);
337
- free(hyd->DemandFlow);
338
- free(hyd->EmitterFlow);
339
357
  free(hyd->Xflow);
340
358
  free(hyd->OldStatus);
341
359
  }
@@ -397,7 +415,7 @@ void setlinkstatus(Project *pr, int index, char value, StatusType *s, double *k
397
415
  if (t == PUMP)
398
416
  {
399
417
  *k = 1.0;
400
- // Check if a re-opened pump needs its flow reset
418
+ // Check if a re-opened pump needs its flow reset
401
419
  if (*s == CLOSED) resetpumpflow(pr, index);
402
420
  }
403
421
  if (t > PUMP && t != GPV) *k = MISSING;
@@ -457,6 +475,7 @@ void setlinksetting(Project *pr, int index, double value, StatusType *s,
457
475
  else
458
476
  {
459
477
  if (*k == MISSING && *s <= CLOSED) *s = OPEN;
478
+ if (t == PCV) link->R = pcvlosscoeff(pr, index, link->Kc);
460
479
  *k = value;
461
480
  }
462
481
  }
@@ -492,12 +511,14 @@ void demands(Project *pr)
492
511
  {
493
512
  // pattern period (k) = (elapsed periods) modulus (periods per pattern)
494
513
  j = demand->Pat;
514
+ if (j == 0)
515
+ j = hyd->DefPat;
495
516
  k = p % (long)net->Pattern[j].Length;
496
517
  djunc = (demand->Base) * net->Pattern[j].F[k] * hyd->Dmult;
497
518
  if (djunc > 0.0) hyd->Dsystem += djunc;
498
519
  sum += djunc;
499
520
  }
500
- hyd->NodeDemand[i] = sum;
521
+ hyd->FullDemand[i] = sum;
501
522
 
502
523
  // Initialize pressure dependent demand
503
524
  hyd->DemandFlow[i] = sum;
@@ -562,6 +583,10 @@ int controls(Project *pr)
562
583
  {
563
584
  // Make sure that link is defined
564
585
  control = &net->Control[i];
586
+ if (!control->isEnabled)
587
+ {
588
+ continue;
589
+ }
565
590
  reset = 0;
566
591
  if ( (k = control->Link) <= 0) continue;
567
592
  link = &net->Link[k];
@@ -601,15 +626,16 @@ int controls(Project *pr)
601
626
  k1 = hyd->LinkSetting[k];
602
627
  k2 = k1;
603
628
  if (link->Type > PIPE) k2 = control->Setting;
604
-
629
+
605
630
  // Check if a re-opened pump needs its flow reset
606
631
  if (link->Type == PUMP && s1 == CLOSED && s2 == OPEN)
607
632
  resetpumpflow(pr, k);
608
-
633
+
609
634
  if (s1 != s2 || k1 != k2)
610
635
  {
611
636
  hyd->LinkStatus[k] = s2;
612
637
  hyd->LinkSetting[k] = k2;
638
+ if (link->Type == PCV) link->R = pcvlosscoeff(pr, k, k2);
613
639
  if (pr->report.Statflag) writecontrolaction(pr,k,i);
614
640
  setsum++;
615
641
  }
@@ -673,7 +699,7 @@ int tanktimestep(Project *pr, long *tstep)
673
699
  Hydraul *hyd = &pr->hydraul;
674
700
 
675
701
  int i, n, tankIdx = 0;
676
- double h, q, v;
702
+ double h, q, v, xt;
677
703
  long t;
678
704
  Stank *tank;
679
705
 
@@ -696,7 +722,9 @@ int tanktimestep(Project *pr, long *tstep)
696
722
  else continue;
697
723
 
698
724
  // Find time to fill/drain tank
699
- t = (long)ROUND(v / q);
725
+ xt = v / q;
726
+ if (ABS(xt) > *tstep + 1) continue;
727
+ t = (long)ROUND(xt);
700
728
  if (t > 0 && t < *tstep)
701
729
  {
702
730
  *tstep = t;
@@ -707,7 +735,7 @@ int tanktimestep(Project *pr, long *tstep)
707
735
  }
708
736
 
709
737
 
710
- void controltimestep(Project *pr, long *tstep)
738
+ int controltimestep(Project *pr, long *tstep)
711
739
  /*
712
740
  **------------------------------------------------------------------
713
741
  ** Input: *tstep = current time step
@@ -720,7 +748,7 @@ void controltimestep(Project *pr, long *tstep)
720
748
  Network *net = &pr->network;
721
749
  Hydraul *hyd = &pr->hydraul;
722
750
 
723
- int i, j, k, n;
751
+ int i, j, k, n, controlIndex = 0;
724
752
  double h, q, v;
725
753
  long t, t1, t2;
726
754
  Slink *link;
@@ -731,7 +759,10 @@ void controltimestep(Project *pr, long *tstep)
731
759
  {
732
760
  t = 0;
733
761
  control = &net->Control[i];
734
-
762
+ if (!control->isEnabled)
763
+ {
764
+ continue;
765
+ }
735
766
  // Control depends on a tank level
736
767
  if ( (n = control->Node) > 0)
737
768
  {
@@ -777,9 +808,14 @@ void controltimestep(Project *pr, long *tstep)
777
808
  k = control->Link;
778
809
  link = &net->Link[k];
779
810
  if ( (link->Type > PIPE && hyd->LinkSetting[k] != control->Setting)
780
- || (hyd->LinkStatus[k] != control->Status) ) *tstep = t;
811
+ || (hyd->LinkStatus[k] != control->Status) )
812
+ {
813
+ *tstep = t;
814
+ controlIndex = i;
815
+ }
781
816
  }
782
817
  }
818
+ return controlIndex;
783
819
  }
784
820
 
785
821
 
@@ -902,7 +938,7 @@ void addenergy(Project *pr, long hstep)
902
938
  // Skip closed pumps
903
939
  pump = &net->Pump[j];
904
940
  k = pump->Link;
905
- if (hyd->LinkStatus[k] <= CLOSED) continue;
941
+ if (pump->Energy.CurrentEffic == 0.0) continue;
906
942
  q = MAX(QZERO, ABS(hyd->LinkFlow[k]));
907
943
 
908
944
  // Find pump-specific energy cost
@@ -915,11 +951,10 @@ void addenergy(Project *pr, long hstep)
915
951
  }
916
952
  else c *= f0;
917
953
 
918
- // Find pump energy & efficiency
919
- getenergy(pr, k, &p, &e);
920
- psum += p;
921
-
922
954
  // Update pump's cumulative statistics
955
+ p = pump->Energy.CurrentPower;
956
+ e = pump->Energy.CurrentEffic;
957
+ psum += p;
923
958
  pump->Energy.TimeOnLine += dt;
924
959
  pump->Energy.Efficiency += e * dt;
925
960
  pump->Energy.KwHrsPerFlow += p / q * dt;
@@ -995,6 +1030,27 @@ void getenergy(Project *pr, int k, double *kw, double *eff)
995
1030
  }
996
1031
 
997
1032
 
1033
+ void getallpumpsenergy(Project *pr)
1034
+ /*
1035
+ **-------------------------------------------------------------
1036
+ ** Input: none
1037
+ ** Output: none
1038
+ ** Purpose: finds the current power and efficiency for each pump.
1039
+ **-------------------------------------------------------------
1040
+ */
1041
+ {
1042
+ int j;
1043
+ Spump *pump;
1044
+
1045
+ for (j = 1; j <= pr->network.Npumps; j++)
1046
+ {
1047
+ pump = &(pr->network.Pump[j]);
1048
+ getenergy(pr, pump->Link, &(pump->Energy.CurrentPower),
1049
+ &(pump->Energy.CurrentEffic));
1050
+ }
1051
+ }
1052
+
1053
+
998
1054
  void tanklevels(Project *pr, long tstep)
999
1055
  /*
1000
1056
  **----------------------------------------------------------------
@@ -1112,6 +1168,5 @@ void resetpumpflow(Project *pr, int i)
1112
1168
  Network *net = &pr->network;
1113
1169
  Spump *pump = &net->Pump[findpump(net, i)];
1114
1170
  if (pump->Ptype == CONST_HP)
1115
- pr->hydraul.LinkFlow[i] = pump->Q0;
1171
+ pr->hydraul.LinkFlow[i] = pump->Q0;
1116
1172
  }
1117
-
@@ -1,14 +1,14 @@
1
1
  /*
2
2
  ******************************************************************************
3
3
  Project: OWA EPANET
4
- Version: 2.2
4
+ Version: 2.3
5
5
  Module: hydsolver.c
6
6
  Description: computes flows and pressures throughout a pipe network using
7
7
  Todini's Global Gradient Algorithm
8
8
  Authors: see AUTHORS
9
9
  Copyright: see AUTHORS
10
10
  License: see LICENSE
11
- Last Updated: 07/15/2019
11
+ Last Updated: 06/26/2024
12
12
  ******************************************************************************
13
13
  */
14
14
 
@@ -47,6 +47,7 @@ static double newflows(Project *, Hydbalance *);
47
47
  static void newlinkflows(Project *, Hydbalance *, double *, double *);
48
48
  static void newemitterflows(Project *, Hydbalance *, double *, double *);
49
49
  static void newdemandflows(Project *, Hydbalance *, double *, double *);
50
+ static void newleakageflows(Project *, Hydbalance *, double *, double *);
50
51
 
51
52
  static void checkhydbalance(Project *, Hydbalance *);
52
53
  static int hasconverged(Project *, double *, Hydbalance *);
@@ -93,7 +94,6 @@ int hydsolve(Project *pr, int *iter, double *relerr)
93
94
  int valveChange; // Valve status change flag
94
95
  int statChange; // Non-valve status change flag
95
96
  Hydbalance hydbal; // Hydraulic balance errors
96
- double fullDemand; // Full demand for a node (cfs)
97
97
 
98
98
  // Initialize status checking & relaxation factor
99
99
  nextcheck = hyd->CheckFreq;
@@ -178,7 +178,7 @@ int hydsolve(Project *pr, int *iter, double *relerr)
178
178
  nextcheck = *iter + hyd->CheckFreq;
179
179
  }
180
180
 
181
- // No convergence yet - see if its time for a periodic status
181
+ // No convergence yet - see if it's time for a periodic status
182
182
  // check on pumps, CV's, and pipes connected to tank
183
183
  else if (*iter <= hyd->MaxCheck && *iter == nextcheck)
184
184
  {
@@ -195,12 +195,12 @@ int hydsolve(Project *pr, int *iter, double *relerr)
195
195
  errcode = 110;
196
196
  }
197
197
 
198
- // Store actual junction outflow in NodeDemand & full demand in DemandFlow
198
+ // Save total outflow (NodeDemand) at each junction
199
199
  for (i = 1; i <= net->Njuncs; i++)
200
200
  {
201
- fullDemand = hyd->NodeDemand[i];
202
- hyd->NodeDemand[i] = hyd->DemandFlow[i] + hyd->EmitterFlow[i];
203
- hyd->DemandFlow[i] = fullDemand;
201
+ hyd->NodeDemand[i] = hyd->DemandFlow[i] +
202
+ hyd->EmitterFlow[i] +
203
+ hyd->LeakageFlow[i];
204
204
  }
205
205
 
206
206
  // Save convergence info
@@ -381,6 +381,7 @@ double newflows(Project *pr, Hydbalance *hbal)
381
381
  newlinkflows(pr, hbal, &qsum, &dqsum);
382
382
  newemitterflows(pr, hbal, &qsum, &dqsum);
383
383
  newdemandflows(pr, hbal, &qsum, &dqsum);
384
+ if (hyd->HasLeakage) newleakageflows(pr, hbal, &qsum, &dqsum);
384
385
 
385
386
  // Return ratio of total flow corrections to total flow
386
387
  if (qsum > hyd->Hacc) return (dqsum / qsum);
@@ -514,6 +515,45 @@ void newemitterflows(Project *pr, Hydbalance *hbal, double *qsum,
514
515
  }
515
516
 
516
517
 
518
+ void newleakageflows(Project *pr, Hydbalance *hbal, double *qsum,
519
+ double *dqsum)
520
+ /*
521
+ **----------------------------------------------------------------
522
+ ** Input: hbal = ptr. to hydraulic balance information
523
+ ** qsum = sum of current system flows
524
+ ** dqsum = sum of system flow changes
525
+ ** Output: updates hbal, qsum and dqsum
526
+ ** Purpose: updates nodal leakage flows after new nodal heads computed
527
+ **----------------------------------------------------------------
528
+ */
529
+ {
530
+ Network *net = &pr->network;
531
+ Hydraul *hyd = &pr->hydraul;
532
+
533
+ int i;
534
+ double dq;
535
+
536
+ for (i = 1; i <= net->Njuncs; i++)
537
+ {
538
+ // Update leakage flow at node i
539
+ dq = leakageflowchange(pr, i);
540
+ if (dq == 0.0) continue;
541
+
542
+ // Update system flow summation
543
+ *qsum += ABS(hyd->LeakageFlow[i]);
544
+ *dqsum += ABS(dq);
545
+
546
+ // Update identity of element with max. flow change
547
+ if (ABS(dq) > hbal->maxflowchange)
548
+ {
549
+ hbal->maxflowchange = ABS(dq);
550
+ hbal->maxflownode = i;
551
+ hbal->maxflowlink = -1;
552
+ }
553
+ }
554
+ }
555
+
556
+
517
557
  void newdemandflows(Project *pr, Hydbalance *hbal, double *qsum, double *dqsum)
518
558
  /*
519
559
  **----------------------------------------------------------------
@@ -546,13 +586,17 @@ void newdemandflows(Project *pr, Hydbalance *hbal, double *qsum, double *dqsum)
546
586
  for (i = 1; i <= net->Njuncs; i++)
547
587
  {
548
588
  // Skip junctions with no positive demand
549
- if (hyd->NodeDemand[i] <= 0.0) continue;
589
+ if (hyd->FullDemand[i] <= 0.0) continue;
550
590
 
551
591
  // Find change in demand flow (see hydcoeffs.c)
552
592
  demandheadloss(pr, i, dp, n, &hloss, &hgrad);
553
593
  dh = hyd->NodeHead[i] - net->Node[i].El - hyd->Pmin;
554
594
  dq = (hloss - dh) / hgrad;
555
595
  dq *= hyd->RelaxFactor;
596
+
597
+ // Prevent a flow change greater than full demand
598
+ if (fabs(dq) > 0.4 * hyd->FullDemand[i])
599
+ dq = 0.4 * SGN(dq) * hyd->FullDemand[i];
556
600
  hyd->DemandFlow[i] -= dq;
557
601
 
558
602
  // Update system flow summation
@@ -637,11 +681,15 @@ int hasconverged(Project *pr, double *relerr, Hydbalance *hbal)
637
681
  if (hyd->FlowChangeLimit > 0.0 &&
638
682
  hbal->maxflowchange > hyd->FlowChangeLimit) return 0;
639
683
 
684
+ // Check for node leakage convergence
685
+ if (hyd->HasLeakage && !leakagehasconverged(pr)) return 0;
686
+
640
687
  // Check for pressure driven analysis convergence
641
688
  if (hyd->DemandModel == PDA) return pdaconverged(pr);
642
689
  return 1;
643
690
  }
644
691
 
692
+
645
693
  int pdaconverged(Project *pr)
646
694
  /*
647
695
  **--------------------------------------------------------------
@@ -654,33 +702,46 @@ int pdaconverged(Project *pr)
654
702
  {
655
703
  Hydraul *hyd = &pr->hydraul;
656
704
 
657
- const double TOL = 0.001;
705
+ const double QTOL = 0.0001; // 0.0001 cfs ~= 0.05 gpm ~= 0.2 lpm)
658
706
  int i, converged = 1;
707
+
659
708
  double totalDemand = 0.0, totalReduction = 0.0;
660
-
709
+ double dp = hyd->Preq - hyd->Pmin;
710
+ double p, q, r;
711
+
661
712
  hyd->DeficientNodes = 0;
662
713
  hyd->DemandReduction = 0.0;
663
-
664
- // Add up number of junctions with demand deficits
714
+
715
+ // Examine each network junction
665
716
  for (i = 1; i <= pr->network.Njuncs; i++)
666
717
  {
667
718
  // Skip nodes whose required demand is non-positive
668
- if (hyd->NodeDemand[i] <= 0.0) continue;
719
+ if (hyd->FullDemand[i] <= 0.0) continue;
720
+
721
+ // Evaluate demand equation at current pressure solution
722
+ p = hyd->NodeHead[i] - pr->network.Node[i].El;
723
+ if (p <= hyd->Pmin)
724
+ q = 0.0;
725
+ else if (p >= hyd->Preq)
726
+ q = hyd->FullDemand[i];
727
+ else
728
+ {
729
+ r = (p - hyd->Pmin) / dp;
730
+ q = hyd->FullDemand[i] * pow(r, hyd->Pexp);
731
+ }
669
732
 
670
- // Check for negative demand flow or positive demand flow at negative pressure
671
- if (hyd->DemandFlow[i] < -TOL) converged = 0;
672
- if (hyd->DemandFlow[i] > TOL &&
673
- hyd->NodeHead[i] - pr->network.Node[i].El - hyd->Pmin < -TOL)
733
+ // Check if demand has not converged
734
+ if (fabs(q - hyd->DemandFlow[i]) > QTOL)
674
735
  converged = 0;
675
-
676
- // Accumulate total required demand and demand deficit
677
- if (hyd->DemandFlow[i] + 0.0001 < hyd->NodeDemand[i])
736
+
737
+ // Accumulate demand deficient node count and demand deficit
738
+ if (hyd->DemandFlow[i] + QTOL < hyd->FullDemand[i])
678
739
  {
679
740
  hyd->DeficientNodes++;
680
- totalDemand += hyd->NodeDemand[i];
681
- totalReduction += hyd->NodeDemand[i] - hyd->DemandFlow[i];
741
+ totalDemand += hyd->FullDemand[i];
742
+ totalReduction += hyd->FullDemand[i] - hyd->DemandFlow[i];
682
743
  }
683
- }
744
+ }
684
745
  if (totalDemand > 0.0)
685
746
  hyd->DemandReduction = totalReduction / totalDemand * 100.0;
686
747
  return converged;