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.
- epyt_flow/EPANET/EPANET/SRC_engines/AUTHORS +40 -8
- epyt_flow/EPANET/EPANET/SRC_engines/LICENSE +3 -3
- epyt_flow/EPANET/EPANET/SRC_engines/enumstxt.h +24 -7
- epyt_flow/EPANET/EPANET/SRC_engines/epanet.c +726 -374
- epyt_flow/EPANET/EPANET/SRC_engines/epanet2.c +128 -32
- epyt_flow/EPANET/EPANET/SRC_engines/errors.dat +7 -1
- epyt_flow/EPANET/EPANET/SRC_engines/flowbalance.c +186 -0
- epyt_flow/EPANET/EPANET/SRC_engines/funcs.h +40 -14
- epyt_flow/EPANET/EPANET/SRC_engines/hash.c +177 -177
- epyt_flow/EPANET/EPANET/SRC_engines/hash.h +28 -28
- epyt_flow/EPANET/EPANET/SRC_engines/hydcoeffs.c +192 -40
- epyt_flow/EPANET/EPANET/SRC_engines/hydraul.c +101 -46
- epyt_flow/EPANET/EPANET/SRC_engines/hydsolver.c +85 -24
- epyt_flow/EPANET/EPANET/SRC_engines/hydstatus.c +29 -63
- epyt_flow/EPANET/EPANET/SRC_engines/include/epanet2.h +70 -37
- epyt_flow/EPANET/EPANET/SRC_engines/include/epanet2_2.h +408 -234
- epyt_flow/EPANET/EPANET/SRC_engines/include/epanet2_enums.h +87 -37
- epyt_flow/EPANET/EPANET/SRC_engines/inpfile.c +153 -79
- epyt_flow/EPANET/EPANET/SRC_engines/input1.c +59 -94
- epyt_flow/EPANET/EPANET/SRC_engines/input2.c +73 -202
- epyt_flow/EPANET/EPANET/SRC_engines/input3.c +446 -351
- epyt_flow/EPANET/EPANET/SRC_engines/leakage.c +527 -0
- epyt_flow/EPANET/EPANET/SRC_engines/mempool.c +8 -4
- epyt_flow/EPANET/EPANET/SRC_engines/mempool.h +23 -23
- epyt_flow/EPANET/EPANET/SRC_engines/output.c +5 -4
- epyt_flow/EPANET/EPANET/SRC_engines/project.c +407 -75
- epyt_flow/EPANET/EPANET/SRC_engines/quality.c +12 -2
- epyt_flow/EPANET/EPANET/SRC_engines/qualreact.c +70 -13
- epyt_flow/EPANET/EPANET/SRC_engines/qualroute.c +7 -5
- epyt_flow/EPANET/EPANET/SRC_engines/report.c +88 -20
- epyt_flow/EPANET/EPANET/SRC_engines/rules.c +144 -6
- epyt_flow/EPANET/EPANET/SRC_engines/smatrix.c +19 -19
- epyt_flow/EPANET/EPANET/SRC_engines/text.h +16 -5
- epyt_flow/EPANET/EPANET/SRC_engines/types.h +73 -19
- epyt_flow/EPANET/EPANET/SRC_engines/util/cstr_helper.c +59 -0
- epyt_flow/EPANET/EPANET/SRC_engines/util/cstr_helper.h +38 -0
- epyt_flow/EPANET/EPANET/SRC_engines/util/errormanager.c +92 -0
- epyt_flow/EPANET/EPANET/SRC_engines/util/errormanager.h +39 -0
- epyt_flow/EPANET/EPANET/SRC_engines/util/filemanager.c +212 -0
- epyt_flow/EPANET/EPANET/SRC_engines/util/filemanager.h +81 -0
- epyt_flow/EPANET/EPANET/SRC_engines/validate.c +408 -0
- epyt_flow/EPANET/compile_linux.sh +1 -1
- epyt_flow/EPANET/compile_macos.sh +2 -2
- epyt_flow/VERSION +1 -1
- epyt_flow/__init__.py +1 -1
- epyt_flow/gym/scenario_control_env.py +26 -3
- epyt_flow/simulation/backend/my_epyt.py +58 -13
- epyt_flow/simulation/events/quality_events.py +6 -6
- epyt_flow/simulation/events/sensor_faults.py +24 -24
- epyt_flow/simulation/events/system_event.py +3 -3
- epyt_flow/simulation/scada/scada_data.py +10 -14
- epyt_flow/simulation/scenario_simulator.py +100 -20
- epyt_flow/topology.py +8 -1
- epyt_flow/uncertainty/model_uncertainty.py +292 -150
- epyt_flow/uncertainty/uncertainties.py +2 -2
- {epyt_flow-0.13.1.dist-info → epyt_flow-0.14.1.dist-info}/METADATA +4 -4
- {epyt_flow-0.13.1.dist-info → epyt_flow-0.14.1.dist-info}/RECORD +60 -54
- {epyt_flow-0.13.1.dist-info → epyt_flow-0.14.1.dist-info}/WHEEL +1 -1
- epyt_flow/EPANET/EPANET/SRC_engines/Readme_SRC_Engines.txt +0 -18
- epyt_flow/EPANET/EPANET/SRC_engines/epanet2.def +0 -131
- epyt_flow/EPANET/EPANET/SRC_engines/main.c +0 -93
- {epyt_flow-0.13.1.dist-info → epyt_flow-0.14.1.dist-info}/licenses/LICENSE +0 -0
- {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.
|
|
4
|
+
Version: 2.3
|
|
5
5
|
Module: quality.c
|
|
6
6
|
Description: implements EPANET's water quality engine
|
|
7
7
|
Authors: see AUTHORS
|
|
8
8
|
Copyright: see AUTHORS
|
|
9
9
|
License: see LICENSE
|
|
10
|
-
Last Updated:
|
|
10
|
+
Last Updated: 02/14/2025
|
|
11
11
|
******************************************************************************
|
|
12
12
|
*/
|
|
13
13
|
|
|
@@ -63,8 +63,16 @@ int openqual(Project *pr)
|
|
|
63
63
|
// Build nodal adjacency lists if they don't already exist
|
|
64
64
|
if (net->Adjlist == NULL)
|
|
65
65
|
{
|
|
66
|
+
// Check for too few nodes & no fixed grade nodes
|
|
67
|
+
if (net->Nnodes < 2) return 223;
|
|
68
|
+
if (net->Ntanks == 0) return 224;
|
|
69
|
+
|
|
70
|
+
// Build adjacency lists
|
|
66
71
|
errcode = buildadjlists(net);
|
|
67
72
|
if (errcode ) return errcode;
|
|
73
|
+
|
|
74
|
+
// Check for unconnected nodes
|
|
75
|
+
if (errcode = unlinked(pr)) return errcode;
|
|
68
76
|
}
|
|
69
77
|
|
|
70
78
|
// Create a memory pool for water quality segments
|
|
@@ -175,6 +183,7 @@ int initqual(Project *pr)
|
|
|
175
183
|
qual->MassBalance.reacted = 0.0;
|
|
176
184
|
qual->MassBalance.final = 0.0;
|
|
177
185
|
qual->MassBalance.ratio = 0.0;
|
|
186
|
+
qual->MassBalance.segCount = 0;
|
|
178
187
|
return errcode;
|
|
179
188
|
}
|
|
180
189
|
|
|
@@ -402,6 +411,7 @@ int closequal(Project *pr)
|
|
|
402
411
|
FREE(qual->FlowDir);
|
|
403
412
|
FREE(qual->SortedNodes);
|
|
404
413
|
}
|
|
414
|
+
freeadjlists(&pr->network);
|
|
405
415
|
return errcode;
|
|
406
416
|
}
|
|
407
417
|
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
/*
|
|
2
2
|
******************************************************************************
|
|
3
3
|
Project: OWA EPANET
|
|
4
|
-
Version: 2.
|
|
4
|
+
Version: 2.3
|
|
5
5
|
Module: qualreact.c
|
|
6
6
|
Description: computes water quality reactions within pipes and tanks
|
|
7
7
|
Authors: see AUTHORS
|
|
8
8
|
Copyright: see AUTHORS
|
|
9
9
|
License: see LICENSE
|
|
10
|
-
Last Updated:
|
|
10
|
+
Last Updated: 12/16/2024
|
|
11
11
|
******************************************************************************
|
|
12
12
|
*/
|
|
13
13
|
|
|
@@ -232,7 +232,7 @@ double piperate(Project *pr, int k)
|
|
|
232
232
|
}
|
|
233
233
|
|
|
234
234
|
// Compute Reynolds No.
|
|
235
|
-
// Flow rate made consistent with how
|
|
235
|
+
// Flow rate made consistent with how it's saved to hydraulics file
|
|
236
236
|
q = (hyd->LinkStatus[k] <= CLOSED) ? 0.0 : hyd->LinkFlow[k];
|
|
237
237
|
a = PI * d * d / 4.0; // pipe area
|
|
238
238
|
u = fabs(q) / a; // flow velocity
|
|
@@ -492,6 +492,13 @@ void tankmix1(Project *pr, int i, double vin, double win, double vnet)
|
|
|
492
492
|
seg->v += vnet;
|
|
493
493
|
seg->v = MAX(0.0, seg->v);
|
|
494
494
|
tank->C = seg->c;
|
|
495
|
+
|
|
496
|
+
// Account for mass lost in tank overflow
|
|
497
|
+
if (seg->v > tank->Vmax)
|
|
498
|
+
{
|
|
499
|
+
qual->MassBalance.outflow += ((seg->v) - tank->Vmax) * tank->C;
|
|
500
|
+
seg->v = tank->Vmax;
|
|
501
|
+
}
|
|
495
502
|
}
|
|
496
503
|
}
|
|
497
504
|
|
|
@@ -513,7 +520,8 @@ void tankmix2(Project *pr, int i, double vin, double win, double vnet)
|
|
|
513
520
|
|
|
514
521
|
int k;
|
|
515
522
|
double vt, // Transferred volume
|
|
516
|
-
vmz
|
|
523
|
+
vmz, // Full mixing zone volume
|
|
524
|
+
vsz; // Full stagnant zone volume
|
|
517
525
|
Pseg mixzone, // Mixing zone segment
|
|
518
526
|
stagzone; // Stagnant zone segment
|
|
519
527
|
Stank *tank = &pr->network.Tank[i];
|
|
@@ -525,7 +533,7 @@ void tankmix2(Project *pr, int i, double vin, double win, double vnet)
|
|
|
525
533
|
if (mixzone == NULL || stagzone == NULL) return;
|
|
526
534
|
|
|
527
535
|
// Full mixing zone volume
|
|
528
|
-
vmz = tank->
|
|
536
|
+
vmz = tank->V1frac * tank->Vmax;
|
|
529
537
|
|
|
530
538
|
// Tank is filling
|
|
531
539
|
vt = 0.0;
|
|
@@ -558,16 +566,31 @@ void tankmix2(Project *pr, int i, double vin, double win, double vnet)
|
|
|
558
566
|
// Update segment volumes
|
|
559
567
|
if (vt > 0.0)
|
|
560
568
|
{
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
569
|
+
if (vnet > 0.0)
|
|
570
|
+
{
|
|
571
|
+
mixzone->v = vmz;
|
|
572
|
+
stagzone->v += vt;
|
|
573
|
+
|
|
574
|
+
// Account for mass lost in overflow from stagnant zone
|
|
575
|
+
vsz = (tank->Vmax) - vmz;
|
|
576
|
+
if (stagzone->v > vsz)
|
|
577
|
+
{
|
|
578
|
+
qual->MassBalance.outflow += ((stagzone->v) - vsz) * stagzone->c;
|
|
579
|
+
stagzone->v = vsz;
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
else
|
|
583
|
+
{
|
|
584
|
+
stagzone->v = MAX(0.0, ((stagzone->v) - vt));
|
|
585
|
+
mixzone->v = vmz + vt + vnet;
|
|
586
|
+
}
|
|
564
587
|
}
|
|
565
588
|
else
|
|
566
589
|
{
|
|
567
590
|
mixzone->v += vnet;
|
|
568
591
|
mixzone->v = MIN(mixzone->v, vmz);
|
|
569
592
|
mixzone->v = MAX(0.0, mixzone->v);
|
|
570
|
-
stagzone->v = 0.0;
|
|
593
|
+
if (vmz - mixzone->v > 0.0) stagzone->v = 0.0;
|
|
571
594
|
}
|
|
572
595
|
|
|
573
596
|
// Use quality of mixing zone to represent quality of
|
|
@@ -612,10 +635,13 @@ void tankmix3(Project *pr, int i, double vin, double win, double vnet)
|
|
|
612
635
|
else addseg(pr, k, vin, cin);
|
|
613
636
|
}
|
|
614
637
|
|
|
615
|
-
//
|
|
638
|
+
// Find volume leaving tank, adjusted so its volume doesn't exceed Vmax
|
|
639
|
+
vout = vin - vnet;
|
|
640
|
+
if (tank->V >= tank->Vmax && vnet > 0.0) vout = vin;
|
|
641
|
+
|
|
642
|
+
// Withdraw outflow from first segment
|
|
616
643
|
vsum = 0.0;
|
|
617
644
|
wsum = 0.0;
|
|
618
|
-
vout = vin - vnet;
|
|
619
645
|
while (vout > 0.0)
|
|
620
646
|
{
|
|
621
647
|
seg = qual->FirstSeg[k];
|
|
@@ -643,6 +669,10 @@ void tankmix3(Project *pr, int i, double vin, double win, double vnet)
|
|
|
643
669
|
if (vsum > 0.0) tank->C = wsum / vsum;
|
|
644
670
|
else if (qual->FirstSeg[k] == NULL) tank->C = 0.0;
|
|
645
671
|
else tank->C = qual->FirstSeg[k]->c;
|
|
672
|
+
|
|
673
|
+
// Account for mass lost in overflow from 1st segment
|
|
674
|
+
if (tank->V >= tank->Vmax && vnet > 0.0)
|
|
675
|
+
qual->MassBalance.outflow += vnet * tank->C;
|
|
646
676
|
}
|
|
647
677
|
|
|
648
678
|
|
|
@@ -669,7 +699,7 @@ void tankmix4(Project *pr, int i, double vin, double win, double vnet)
|
|
|
669
699
|
k = net->Nlinks + i;
|
|
670
700
|
if (qual->LastSeg[k] == NULL || qual->FirstSeg[k] == NULL) return;
|
|
671
701
|
|
|
672
|
-
// Find
|
|
702
|
+
// Find inflow concentration
|
|
673
703
|
if (vin > 0.0) cin = win / vin;
|
|
674
704
|
else cin = 0.0;
|
|
675
705
|
|
|
@@ -687,6 +717,33 @@ void tankmix4(Project *pr, int i, double vin, double win, double vnet)
|
|
|
687
717
|
|
|
688
718
|
// Update reported tank quality
|
|
689
719
|
tank->C = qual->LastSeg[k]->c;
|
|
720
|
+
|
|
721
|
+
// If tank full then remove vnet from leading segments
|
|
722
|
+
if (tank->V >= tank->Vmax)
|
|
723
|
+
{
|
|
724
|
+
wsum = 0.0;
|
|
725
|
+
while (vnet > 0.0)
|
|
726
|
+
{
|
|
727
|
+
seg = qual->FirstSeg[k];
|
|
728
|
+
if (seg == NULL) break;
|
|
729
|
+
vseg = seg->v; // Flow volume from leading seg
|
|
730
|
+
vseg = MIN(vseg, vnet);
|
|
731
|
+
if (seg == qual->LastSeg[k]) vseg = vnet;
|
|
732
|
+
wsum += (seg->c) * vseg;
|
|
733
|
+
vnet -= vseg; // Remaining flow volume
|
|
734
|
+
if (vnet >= 0.0 && vseg >= seg->v) // Seg used up
|
|
735
|
+
{
|
|
736
|
+
if (seg->prev)
|
|
737
|
+
{
|
|
738
|
+
qual->FirstSeg[k] = seg->prev;
|
|
739
|
+
seg->prev = qual->FreeSeg;
|
|
740
|
+
qual->FreeSeg = seg;
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
else seg->v -= vseg; // Remaining volume in segment
|
|
744
|
+
}
|
|
745
|
+
qual->MassBalance.outflow += wsum;
|
|
746
|
+
}
|
|
690
747
|
}
|
|
691
748
|
|
|
692
749
|
// If tank emptying then remove last segments until vnet consumed
|
|
@@ -715,7 +772,7 @@ void tankmix4(Project *pr, int i, double vin, double win, double vnet)
|
|
|
715
772
|
vsum += vseg;
|
|
716
773
|
wsum += (seg->c) * vseg;
|
|
717
774
|
|
|
718
|
-
// ... update
|
|
775
|
+
// ... update remaining volume to remove
|
|
719
776
|
vnet -= vseg;
|
|
720
777
|
|
|
721
778
|
// ... if no more volume left in current segment
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
/*
|
|
2
2
|
******************************************************************************
|
|
3
3
|
Project: OWA EPANET
|
|
4
|
-
Version: 2.
|
|
4
|
+
Version: 2.3
|
|
5
5
|
Module: qualroute.c
|
|
6
6
|
Description: computes water quality transport over a single time step
|
|
7
7
|
Authors: see AUTHORS
|
|
8
8
|
Copyright: see AUTHORS
|
|
9
9
|
License: see LICENSE
|
|
10
|
-
Last Updated:
|
|
10
|
+
Last Updated: 02/14/2025
|
|
11
11
|
******************************************************************************
|
|
12
12
|
*/
|
|
13
13
|
|
|
@@ -183,6 +183,7 @@ void evalnodeinflow(Project *pr, int k, long tstep, double *volin,
|
|
|
183
183
|
// ... recycle the used up segment
|
|
184
184
|
seg->prev = qual->FreeSeg;
|
|
185
185
|
qual->FreeSeg = seg;
|
|
186
|
+
qual->MassBalance.segCount--;
|
|
186
187
|
}
|
|
187
188
|
|
|
188
189
|
// ... otherwise just reduce this segment's volume
|
|
@@ -246,7 +247,7 @@ double findnodequal(Project *pr, int n, double volin,
|
|
|
246
247
|
return qual->NodeQual[n];
|
|
247
248
|
}
|
|
248
249
|
|
|
249
|
-
// Find quality
|
|
250
|
+
// Find quality contributed by any external chemical source
|
|
250
251
|
else qual->SourceQual = findsourcequal(pr, n, volout, tstep);
|
|
251
252
|
if (qual->SourceQual == 0.0) return qual->NodeQual[n];
|
|
252
253
|
|
|
@@ -609,10 +610,10 @@ void initsegs(Project *pr)
|
|
|
609
610
|
addseg(pr, k, v, c);
|
|
610
611
|
|
|
611
612
|
// Create a 2nd segment for the 2-compartment tank model
|
|
612
|
-
if (net->Tank[j].MixModel == MIX2)
|
|
613
|
+
if (!qual->OutOfMemory && net->Tank[j].MixModel == MIX2)
|
|
613
614
|
{
|
|
614
615
|
// ... mixing zone segment
|
|
615
|
-
v1 = MAX(0, v - net->Tank[j].
|
|
616
|
+
v1 = MAX(0, v - net->Tank[j].V1frac * net->Tank[j].Vmax);
|
|
616
617
|
qual->FirstSeg[k]->v = v1;
|
|
617
618
|
|
|
618
619
|
// ... stagnant zone segment
|
|
@@ -691,4 +692,5 @@ void addseg(Project *pr, int k, double v, double c)
|
|
|
691
692
|
if (qual->FirstSeg[k] == NULL) qual->FirstSeg[k] = seg;
|
|
692
693
|
if (qual->LastSeg[k] != NULL) qual->LastSeg[k]->prev = seg;
|
|
693
694
|
qual->LastSeg[k] = seg;
|
|
695
|
+
qual->MassBalance.segCount++;
|
|
694
696
|
}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
/*
|
|
2
2
|
******************************************************************************
|
|
3
3
|
Project: OWA EPANET
|
|
4
|
-
Version: 2.
|
|
4
|
+
Version: 2.3
|
|
5
5
|
Module: report.c
|
|
6
6
|
Description: procedures for writing formatted text to a report file
|
|
7
7
|
Authors: see AUTHORS
|
|
8
8
|
Copyright: see AUTHORS
|
|
9
9
|
License: see LICENSE
|
|
10
|
-
Last Updated:
|
|
10
|
+
Last Updated: 02/14/2025
|
|
11
11
|
******************************************************************************
|
|
12
12
|
*/
|
|
13
13
|
|
|
@@ -45,7 +45,7 @@ static void writeenergy(Project *);
|
|
|
45
45
|
static int writeresults(Project *);
|
|
46
46
|
static int disconnected(Project *);
|
|
47
47
|
static void marknodes(Project *, int, int *, char *);
|
|
48
|
-
static void getclosedlink(Project *, int, char *);
|
|
48
|
+
static void getclosedlink(Project *, int, char *, int *);
|
|
49
49
|
static void writelimits(Project *, int, int);
|
|
50
50
|
static int checklimits(Report *, double *, int, int);
|
|
51
51
|
static char *fillstr(char *, char, int);
|
|
@@ -67,7 +67,7 @@ int clearreport(Project *pr)
|
|
|
67
67
|
return 0;
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
-
int copyreport(Project* pr, char *filename)
|
|
70
|
+
int copyreport(Project* pr, const char *filename)
|
|
71
71
|
/*
|
|
72
72
|
**------------------------------------------------------
|
|
73
73
|
** Input: filename = name of file to copy to
|
|
@@ -291,7 +291,7 @@ void writesummary(Project *pr)
|
|
|
291
291
|
if (qual->Qualflag == NONE || time->Dur == 0.0) sprintf(s, FMT29);
|
|
292
292
|
else if (qual->Qualflag == CHEM) sprintf(s, FMT30, qual->ChemName);
|
|
293
293
|
else if (qual->Qualflag == TRACE) sprintf(s, FMT31, net->Node[qual->TraceNode].ID);
|
|
294
|
-
else if (qual->Qualflag == AGE)
|
|
294
|
+
else if (qual->Qualflag == AGE) sprintf(s, FMT32);
|
|
295
295
|
writeline(pr, s);
|
|
296
296
|
if (qual->Qualflag != NONE && time->Dur > 0)
|
|
297
297
|
{
|
|
@@ -422,13 +422,51 @@ void writehydstat(Project *pr, int iter, double relerr)
|
|
|
422
422
|
writeline(pr, " ");
|
|
423
423
|
}
|
|
424
424
|
|
|
425
|
+
void writeflowbalance(Project *pr)
|
|
426
|
+
/*
|
|
427
|
+
**-------------------------------------------------------------
|
|
428
|
+
** Input: none
|
|
429
|
+
** Output: none
|
|
430
|
+
** Purpose: writes hydraulic flow balance ratio to report file.
|
|
431
|
+
**-------------------------------------------------------------
|
|
432
|
+
*/
|
|
433
|
+
{
|
|
434
|
+
Hydraul *hyd = &pr->hydraul;
|
|
435
|
+
Report *rpt = &pr->report;
|
|
436
|
+
char s1[MAXMSG+1];
|
|
437
|
+
double ucf = pr->Ucf[FLOW];
|
|
438
|
+
|
|
439
|
+
snprintf(s1, MAXMSG, "Hydraulic Flow Balance (%s)", rpt->Field[DEMAND].Units);
|
|
440
|
+
writeline(pr, s1);
|
|
441
|
+
snprintf(s1, MAXMSG, "================================");
|
|
442
|
+
writeline(pr, s1);
|
|
443
|
+
snprintf(s1, MAXMSG, "Total Inflow: %12.3f", hyd->FlowBalance.totalInflow*ucf);
|
|
444
|
+
writeline(pr, s1);
|
|
445
|
+
snprintf(s1, MAXMSG, "Consumer Demand: %12.3f", hyd->FlowBalance.consumerDemand*ucf);
|
|
446
|
+
writeline(pr, s1);
|
|
447
|
+
snprintf(s1, MAXMSG, "Demand Deficit: %12.3f", hyd->FlowBalance.deficitDemand*ucf);
|
|
448
|
+
writeline(pr, s1);
|
|
449
|
+
snprintf(s1, MAXMSG, "Emitter Flow: %12.3f", hyd->FlowBalance.emitterDemand*ucf);
|
|
450
|
+
writeline(pr, s1);
|
|
451
|
+
snprintf(s1, MAXMSG, "Leakage Flow: %12.3f", hyd->FlowBalance.leakageDemand*ucf);
|
|
452
|
+
writeline(pr, s1);
|
|
453
|
+
snprintf(s1, MAXMSG, "Total Outflow: %12.3f", hyd->FlowBalance.totalOutflow*ucf);
|
|
454
|
+
writeline(pr, s1);
|
|
455
|
+
snprintf(s1, MAXMSG, "Storage Flow: %12.3f", hyd->FlowBalance.storageDemand*ucf);
|
|
456
|
+
writeline(pr, s1);
|
|
457
|
+
snprintf(s1, MAXMSG, "Flow Ratio: %12.3f", hyd->FlowBalance.ratio);
|
|
458
|
+
writeline(pr, s1);
|
|
459
|
+
snprintf(s1, MAXMSG, "================================\n");
|
|
460
|
+
writeline(pr, s1);
|
|
461
|
+
}
|
|
462
|
+
|
|
425
463
|
void writemassbalance(Project *pr)
|
|
426
464
|
/*
|
|
427
465
|
**-------------------------------------------------------------
|
|
428
466
|
** Input: none
|
|
429
467
|
** Output: none
|
|
430
468
|
** Purpose: writes water quality mass balance ratio
|
|
431
|
-
** (Outflow + Final Storage) / Inflow + Initial Storage
|
|
469
|
+
** (Outflow + Final Storage) / Inflow + Initial Storage
|
|
432
470
|
** to report file.
|
|
433
471
|
**-------------------------------------------------------------
|
|
434
472
|
*/
|
|
@@ -463,6 +501,8 @@ void writemassbalance(Project *pr)
|
|
|
463
501
|
writeline(pr, s1);
|
|
464
502
|
snprintf(s1, MAXMSG, "Mass Ratio: %-.5f", qual->MassBalance.ratio);
|
|
465
503
|
writeline(pr, s1);
|
|
504
|
+
snprintf(s1, MAXMSG, "Total Segments: %d", qual->MassBalance.segCount);
|
|
505
|
+
writeline(pr, s1);
|
|
466
506
|
snprintf(s1, MAXMSG, "================================\n");
|
|
467
507
|
writeline(pr, s1);
|
|
468
508
|
}
|
|
@@ -876,7 +916,7 @@ void writeheader(Project *pr, int type, int contin)
|
|
|
876
916
|
}
|
|
877
917
|
}
|
|
878
918
|
|
|
879
|
-
void writeline(Project *pr, char *s)
|
|
919
|
+
void writeline(Project *pr, const char *s)
|
|
880
920
|
/*
|
|
881
921
|
**--------------------------------------------------------------
|
|
882
922
|
** Input: *s = text string
|
|
@@ -886,6 +926,12 @@ void writeline(Project *pr, char *s)
|
|
|
886
926
|
*/
|
|
887
927
|
{
|
|
888
928
|
Report *rpt = &pr->report;
|
|
929
|
+
|
|
930
|
+
if (pr->report.reportCallback != NULL)
|
|
931
|
+
{
|
|
932
|
+
pr->report.reportCallback(pr->report.reportCallbackUserData, pr, s);
|
|
933
|
+
return;
|
|
934
|
+
}
|
|
889
935
|
|
|
890
936
|
if (rpt->RptFile == NULL) return;
|
|
891
937
|
if (rpt->Rptflag)
|
|
@@ -1281,7 +1327,7 @@ int disconnected(Project *pr)
|
|
|
1281
1327
|
clocktime(rpt->Atime, time->Htime));
|
|
1282
1328
|
writeline(pr, pr->Msg);
|
|
1283
1329
|
}
|
|
1284
|
-
getclosedlink(pr, j, marked);
|
|
1330
|
+
getclosedlink(pr, j, marked, nodelist);
|
|
1285
1331
|
}
|
|
1286
1332
|
|
|
1287
1333
|
// Free allocated memory
|
|
@@ -1344,11 +1390,12 @@ void marknodes(Project *pr, int m, int *nodelist, char *marked)
|
|
|
1344
1390
|
}
|
|
1345
1391
|
}
|
|
1346
1392
|
|
|
1347
|
-
void getclosedlink(Project *pr, int i, char *marked)
|
|
1393
|
+
void getclosedlink(Project *pr, int i, char *marked, int *stack)
|
|
1348
1394
|
/*
|
|
1349
1395
|
**----------------------------------------------------------------
|
|
1350
1396
|
** Input: i = junction index
|
|
1351
1397
|
** marked[] = marks nodes already examined
|
|
1398
|
+
** stack[] = stack to hold nodes to examine
|
|
1352
1399
|
** Output: None.
|
|
1353
1400
|
** Purpose: Determines if a closed link connects to junction i.
|
|
1354
1401
|
**----------------------------------------------------------------
|
|
@@ -1359,20 +1406,41 @@ void getclosedlink(Project *pr, int i, char *marked)
|
|
|
1359
1406
|
int j, k;
|
|
1360
1407
|
Padjlist alink;
|
|
1361
1408
|
|
|
1409
|
+
int top = 0;
|
|
1410
|
+
|
|
1411
|
+
// Mark the current junction as examined and push onto stack
|
|
1362
1412
|
marked[i] = 2;
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1413
|
+
stack[top] = i;
|
|
1414
|
+
|
|
1415
|
+
while (top >= 0) {
|
|
1416
|
+
i = stack[top--];
|
|
1417
|
+
alink = net->Adjlist[i];
|
|
1418
|
+
|
|
1419
|
+
// Iterate through each link adjacent to the current node
|
|
1420
|
+
while (alink != NULL) {
|
|
1421
|
+
k = alink->link;
|
|
1422
|
+
j = alink->node;
|
|
1423
|
+
|
|
1424
|
+
// Skip nodes that have already been examined
|
|
1425
|
+
if (marked[j] == 2) {
|
|
1426
|
+
alink = alink->next;
|
|
1427
|
+
continue;
|
|
1428
|
+
}
|
|
1429
|
+
|
|
1430
|
+
// If a closed link is found, return and display a warning message
|
|
1431
|
+
if (marked[j] == 1) {
|
|
1432
|
+
sprintf(pr->Msg, WARN03c, net->Link[k].ID);
|
|
1433
|
+
writeline(pr, pr->Msg);
|
|
1434
|
+
return;
|
|
1435
|
+
}
|
|
1436
|
+
|
|
1437
|
+
// Mark the node as examined and push it onto the stack
|
|
1438
|
+
marked[j] = 2;
|
|
1439
|
+
stack[++top] = j;
|
|
1440
|
+
alink = alink->next;
|
|
1373
1441
|
}
|
|
1374
|
-
else getclosedlink(pr, j, marked);
|
|
1375
1442
|
}
|
|
1443
|
+
|
|
1376
1444
|
}
|
|
1377
1445
|
|
|
1378
1446
|
void writelimits(Project *pr, int j1, int j2)
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
/*
|
|
2
2
|
******************************************************************************
|
|
3
3
|
Project: OWA EPANET
|
|
4
|
-
Version: 2.
|
|
4
|
+
Version: 2.3
|
|
5
5
|
Module: rules.c
|
|
6
6
|
Description: implements rule-based controls
|
|
7
7
|
Authors: see AUTHORS
|
|
8
8
|
Copyright: see AUTHORS
|
|
9
9
|
License: see LICENSE
|
|
10
|
-
Last Updated:
|
|
10
|
+
Last Updated: 02/11/2025
|
|
11
11
|
******************************************************************************
|
|
12
12
|
*/
|
|
13
13
|
|
|
@@ -32,10 +32,11 @@ enum Rulewords {
|
|
|
32
32
|
r_THEN,
|
|
33
33
|
r_ELSE,
|
|
34
34
|
r_PRIORITY,
|
|
35
|
+
r_DISABLED,
|
|
35
36
|
r_ERROR
|
|
36
37
|
};
|
|
37
38
|
char *Ruleword[] = {w_RULE, w_IF, w_AND, w_OR,
|
|
38
|
-
w_THEN, w_ELSE, w_PRIORITY, NULL};
|
|
39
|
+
w_THEN, w_ELSE, w_PRIORITY, w_DISABLED, NULL};
|
|
39
40
|
|
|
40
41
|
enum Varwords {
|
|
41
42
|
r_DEMAND,
|
|
@@ -273,6 +274,16 @@ int ruledata(Project *pr)
|
|
|
273
274
|
err = newpriority(pr);
|
|
274
275
|
break;
|
|
275
276
|
|
|
277
|
+
case r_DISABLED:
|
|
278
|
+
if (rules->RuleState != r_THEN && rules->RuleState != r_ELSE &&
|
|
279
|
+
rules->RuleState != r_PRIORITY)
|
|
280
|
+
{
|
|
281
|
+
err = 221;
|
|
282
|
+
break;
|
|
283
|
+
}
|
|
284
|
+
net->Rule[net->Nrules].isEnabled = FALSE;
|
|
285
|
+
break;
|
|
286
|
+
|
|
276
287
|
default:
|
|
277
288
|
err = 201;
|
|
278
289
|
}
|
|
@@ -404,7 +415,7 @@ void adjustrules(Project *pr, int objtype, int index)
|
|
|
404
415
|
}
|
|
405
416
|
}
|
|
406
417
|
|
|
407
|
-
void adjusttankrules(Project *pr)
|
|
418
|
+
void adjusttankrules(Project *pr, int ndiff)
|
|
408
419
|
//-----------------------------------------------------------
|
|
409
420
|
// Adjusts tank indices in rule premises.
|
|
410
421
|
//-----------------------------------------------------------
|
|
@@ -420,7 +431,8 @@ void adjusttankrules(Project *pr)
|
|
|
420
431
|
p = net->Rule[i].Premises;
|
|
421
432
|
while (p != NULL)
|
|
422
433
|
{
|
|
423
|
-
if (p->object == r_NODE && p->index > njuncs)
|
|
434
|
+
if (p->object == r_NODE && p->index > njuncs)
|
|
435
|
+
p->index += ndiff;
|
|
424
436
|
p = p->next;
|
|
425
437
|
}
|
|
426
438
|
}
|
|
@@ -472,7 +484,7 @@ int writerule(Project *pr, FILE *f, int ruleIndex)
|
|
|
472
484
|
Srule *rule = &net->Rule[ruleIndex];
|
|
473
485
|
Spremise *p;
|
|
474
486
|
Saction *a;
|
|
475
|
-
|
|
487
|
+
|
|
476
488
|
// Write each premise clause to the file
|
|
477
489
|
p = rule->Premises;
|
|
478
490
|
fprintf(f, "\nIF ");
|
|
@@ -527,6 +539,11 @@ int checkrules(Project *pr, long dt)
|
|
|
527
539
|
rules->ActionList = NULL;
|
|
528
540
|
for (i = 1; i <= net->Nrules; i++)
|
|
529
541
|
{
|
|
542
|
+
// skip if the rule is disabled
|
|
543
|
+
if (!net->Rule[i].isEnabled)
|
|
544
|
+
{
|
|
545
|
+
continue;
|
|
546
|
+
}
|
|
530
547
|
// If premises true, add THEN clauses to action list
|
|
531
548
|
if (evalpremises(pr, i) == TRUE)
|
|
532
549
|
{
|
|
@@ -549,6 +566,126 @@ int checkrules(Project *pr, long dt)
|
|
|
549
566
|
return actionCount;
|
|
550
567
|
}
|
|
551
568
|
|
|
569
|
+
void updateruleunits(Project *pr, double dcf, double pcf, double hcf, double qcf)
|
|
570
|
+
//-----------------------------------------------------------
|
|
571
|
+
// Updates the units of a rule's premises and actions.
|
|
572
|
+
//-----------------------------------------------------------
|
|
573
|
+
{
|
|
574
|
+
Network *net = &pr->network;
|
|
575
|
+
Slink *Link = net->Link;
|
|
576
|
+
|
|
577
|
+
int i, k;
|
|
578
|
+
double x;
|
|
579
|
+
Spremise *p;
|
|
580
|
+
Saction *a;
|
|
581
|
+
|
|
582
|
+
for (i = 1; i <= net->Nrules; i++)
|
|
583
|
+
{
|
|
584
|
+
p = net->Rule[i].Premises;
|
|
585
|
+
while (p != NULL)
|
|
586
|
+
{
|
|
587
|
+
|
|
588
|
+
switch (p->variable)
|
|
589
|
+
{
|
|
590
|
+
case r_DEMAND:
|
|
591
|
+
p->value *= dcf;
|
|
592
|
+
break;
|
|
593
|
+
|
|
594
|
+
case r_HEAD:
|
|
595
|
+
case r_GRADE:
|
|
596
|
+
p->value *= hcf;
|
|
597
|
+
break;
|
|
598
|
+
|
|
599
|
+
case r_PRESSURE:
|
|
600
|
+
p->value *= pcf;
|
|
601
|
+
break;
|
|
602
|
+
|
|
603
|
+
case r_LEVEL:
|
|
604
|
+
p->value *= hcf;
|
|
605
|
+
break;
|
|
606
|
+
|
|
607
|
+
case r_FLOW:
|
|
608
|
+
p->value *= qcf;
|
|
609
|
+
break;
|
|
610
|
+
|
|
611
|
+
case r_SETTING:
|
|
612
|
+
|
|
613
|
+
switch (Link[p->index].Type)
|
|
614
|
+
{
|
|
615
|
+
case PRV:
|
|
616
|
+
case PSV:
|
|
617
|
+
case PBV:
|
|
618
|
+
p->value *= pcf;
|
|
619
|
+
break;
|
|
620
|
+
case FCV:
|
|
621
|
+
p->value *= qcf;
|
|
622
|
+
break;
|
|
623
|
+
default:
|
|
624
|
+
break;
|
|
625
|
+
}
|
|
626
|
+
break;
|
|
627
|
+
|
|
628
|
+
default:
|
|
629
|
+
break;
|
|
630
|
+
|
|
631
|
+
}
|
|
632
|
+
p = p->next;
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
a = net->Rule[i].ThenActions;
|
|
636
|
+
while (a != NULL)
|
|
637
|
+
{
|
|
638
|
+
k = a->link;
|
|
639
|
+
x = a->setting;
|
|
640
|
+
|
|
641
|
+
// Change link's setting
|
|
642
|
+
if (x != MISSING)
|
|
643
|
+
{
|
|
644
|
+
switch (net->Link[k].Type)
|
|
645
|
+
{
|
|
646
|
+
case PRV:
|
|
647
|
+
case PSV:
|
|
648
|
+
case PBV:
|
|
649
|
+
a->setting *= pcf;
|
|
650
|
+
break;
|
|
651
|
+
case FCV:
|
|
652
|
+
a->setting *= qcf;
|
|
653
|
+
break;
|
|
654
|
+
default:
|
|
655
|
+
break;
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
a = a->next;
|
|
659
|
+
}
|
|
660
|
+
a = net->Rule[i].ElseActions;
|
|
661
|
+
while (a != NULL)
|
|
662
|
+
{
|
|
663
|
+
k = a->link;
|
|
664
|
+
x = a->setting;
|
|
665
|
+
|
|
666
|
+
// Change link's setting
|
|
667
|
+
if (x != MISSING)
|
|
668
|
+
{
|
|
669
|
+
switch (net->Link[k].Type)
|
|
670
|
+
{
|
|
671
|
+
case PRV:
|
|
672
|
+
case PSV:
|
|
673
|
+
case PBV:
|
|
674
|
+
a->setting *= pcf;
|
|
675
|
+
break;
|
|
676
|
+
case FCV:
|
|
677
|
+
a->setting *= qcf;
|
|
678
|
+
break;
|
|
679
|
+
default:
|
|
680
|
+
break;
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
a = a->next;
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
|
|
552
689
|
void newrule(Project *pr)
|
|
553
690
|
//----------------------------------------------------------
|
|
554
691
|
// Adds a new rule to the project
|
|
@@ -564,6 +701,7 @@ void newrule(Project *pr)
|
|
|
564
701
|
rule->ThenActions = NULL;
|
|
565
702
|
rule->ElseActions = NULL;
|
|
566
703
|
rule->priority = 0.0;
|
|
704
|
+
rule->isEnabled = TRUE;
|
|
567
705
|
pr->rules.LastPremise = NULL;
|
|
568
706
|
pr->rules.LastThenAction = NULL;
|
|
569
707
|
pr->rules.LastElseAction = NULL;
|