Skip to content

# MIKE URBAN EPANET API¶

## Introduction¶

The purpose of this document is to describe API interface to selected procedures of “muEPANET” library that is used by DHI to provide hydraulic and water quality calculations based on EPANET engine. The library is developed in C# and it is using reference C** files. The library is available in 64-bit version. 32-bit version can be supplied upon request.

## Folders and files¶

Folder: WD.Simulation.Wrapper.64

• DHI..MuEpanet2(64).dll

• epanet2(64).dll

## Hydraulic and water quality simulation¶

Hydraulic and water quality simulation that includes DHI’s addition to the standard EPANET; the additional functions include e.g. variable speed drive pumps, restart of a water quality simulation from a hot start file, use of absolute date and time in control rules. In order to run the EPANET hydraulic and water quality analysis based on the input INP file, use MUEpanet2.MUEpanetTF function. The only input required for this simulation is the EPANET based INP file (standard EPANET or DHI’s modification to the file).

Definition:

internal static int EmsMUEpanetAllSimulations(string F1, string F2, string F3,
EpanetCallBack F4, Action\<double\> F5, CancellationToken token, string F6,
string F7)


Where:

• F1: full path to the INP file (this is the input file)

• F2: full path to the SUM file (this is the output file with summary messages)

• F3: full path to the RES file (this is the output file with simulation results)

• F4: pointer to a user-supplied call back function, which accepts a character string as its argument to provide progress of the simulation, use NULL when no progress is required

• F5: action delegate to provide a fraction (percentage) that can be used for the progress bar display

• CancellationToken: cancellation token that can be used to interrupt (cancel) the function execution

• F6: simulation keyword(s):

• DEMO: the program will run in a demonstration mode without a licence check and the maximum number of links will be limited to 100.

• RESULTSCORRECTION: results will be corrected for negative pressures (the minimum pressure set to the vapour cavity pressure); otherwise results will be reported as simulated

• PIPECRITICALITY: pipe criticality module will be activated based on the [PIPECRITICALITY] section in the INP file and the simulation will stop at the end of the pipe criticality analysis.

• FIREFLOW: fire flow module will be activated based on the [FIREFLOW] section in the INP file and the simulation will stop at the end of the fire flow analysis. Note, that it is possible to run the fire flow simulation without modifying the INP file – see the section Fire flow analysis, page 5.

• FLUSHING: pipe flushing module will be activated based on the [FLUSHING] section in the INP file and the simulation will stop at the end of the pipe flushing analysis.

• OPTIMIZATION: optimization module will be activated based on the [OPTIMIZATION] section in the INP file and the simulation will stop at the end of the optimization.

• F7: full path to the epanet2(32).dll or epanet2(64).dll

#### Example of use¶

using DHI.MuEpanet2;
string EPANETxDLL;
EPANETxDLL = Application.StartupPath + "\\epanet2(64).dll";
private static readonly EpanetCallBack PinnedCallBack = EpanetCallBackWithProgress;
public CancellationTokenSource cts;
cts = new CancellationTokenSource();
retCode = MUEpanet2.MUEpanetAllSimulations(tbInput.Text, tbReport.Text, tbOutput.Text, EpanetCallBack, EpanetProgressCallBack, cts.Token, " RESULTSCORRECTION", EPANETxDLL);


Remarks:

1. If you wish to stop the simulation during execution, use this: cts.Cancel();

2. Definition of the callback function:

private static void EpanetCallBack(string msg)

{

Application.DoEvents();

ListBox.Items.Add(msg);

ListBox.Refresh();

}

1. Definition of the progress function:
    private void EpanetProgressCallBack(double fraction)

{

RunProgressBar.Value = (int)Math.Ceiling(fraction \* 100);

}


## Fire flow analysis¶

The purpose of the fire flow analysis is to compute the available fire flow for a design residual pressure or to compute the residual pressure for a design fire flow. The simulation requires a EPANET based INP file (standard EPANET or DHI’s modification to the file) and fire flow analysis settings (as described below).

In a typical use, the user will select a node (in case of option b) or one or mode nodes (in case of option a) and then he can choose one of the fire flow options below:

1. Enter fire flow e.g. 6 l/s

2. Enter residual pressure e.g. 15m

The program will run the simulation and return:

1. Computed residual pressure e.g. 18m

2. Computed available flow e.g. 25 l/s

### Compute residual pressure for given flow¶

In order to run the fire flow analysis and compute the residual pressure at the selected node(s) for the given flow requirement, use the function below.

Definition:

internal static int EmsMUEpanetFF_P4Q(string F1, string F2, string F3,
EpanetCallBack F4, Action\<double\> F5, List\<string\> NodeList, List\<double\>
OutPressureList, double Flow, double Hour, string F6)


Where:

• F1: full path to the INP file

• F2: full path to the SUM file

• F3: full path to the RES file

• F4: pointer to a user-supplied call back function, which accepts a character string as its argument to provide progress of the simulation, use NULL when no progress is required

• F5: action delegate to provide a fraction (percentage) that can be used for the progress bar display

• NodeList: list of model node ID for which the residual pressure is simultaneously computed

• OutPressureList: list of residual pressures computed per node in the node list

• Flow: required flow

• Hour: hour (or fraction of hour) at which the fire flow simulation is computed.

• F6: full path to the epanet2(32).dll or epanet2(64).dll

Return codes (integer code with the simulation status):

• 0: OK, no errors

• -1: Exception encountered, simulation failed

• 1: Some node IDs do not exist and their residual pressure set to 0

#### Example of use¶

List\<string\> NodeList = new List\<string\>();

NodeList.Add(ffNodeID.Text);

List\<double\> PressureList = new List\<double\>();

PressureList.Add(0);

retCode = MUEpanet2.MUEpanetFireFlow_P4Q(tbInput.Text, tbReport.Text,
tbOutput.Text, PinnedCallBack, null, NodeList, PressureList, Flow, Hour,
EPANETxDLL);


### Compute available flow given pressure¶

In order to run the fire flow analysis and compute the available flow at the selected node(s) for the required residual pressure, use the function below.

Definition:

internal static int EmsMUEpanetFF_Q4P(string F1, string F2, string F3,
EpanetCallBack F4, Action\<double\> F5, string NodeID, double Pressure, out
double OutFlow, double Hour, double Accuracy, string F6)


Where:

• F1: full path to the INP file

• F2: full path to the SUM file

• F3: full path to the RES file

• F4: pointer to a user-supplied call back function, which accepts a character string as its argument to provide progress of the simulation, use NULL when no progress is required

• F5: fraction (percentage) that can be used for the progress bar display

• NodeID: model node ID for which the residual pressure is computed

• Pressure: required residual pressure

• OutFlow: computed available flow

• Hour: hour (or fraction of hour) at which the fire flow simulation is computed.

• Accuracy: simulation accuracy in pressure units, e.g. 0.1m

• F6: full path to the epanet2(32).dll or epanet2(64).dll

Return codes (integer code with the simulation status):

• 0: No errors

• -1: Exception encountered, simulation failed

• 1: Static pressure is already below the residual pressure, no fire flow available

• 2: Cannot find upper flow limit, no fire flow will be computed

• 3: Cannot iterate flow for pressure, no fire flow will be computed

• 4: No fire flow available at this residual pressure

• 5: Node does not exist, no fire flow will be computed

#### Example of use¶

Flow = 0;

retCode = MUEpanet2.MUEpanetFireFlow_Q4P(tbInput.Text, tbReport.Text,
tbOutput.Text, PinnedCallBack, null, NodeID, Pressure, out Flow, Hour, Accuracy,
EPANETxDLL);


## Network capacity analysis¶

The purpose of the network capacity analysis is to compute the amount of a remaining available flow (consumption) for a design value of a minimum required service pressure and maintain the velocity in the adjacent pipes below the maximum service value. The simulation is very similar to the fire flow analysis “flow for pressure”.

The simulation requires a EPANET based INP file (standard EPANET or DHI’s modification to the file) and fire flow analysis settings (as described below).

In a typical use, the user will select a node and then he will specify:

• Enter required service pressure e.g. 15m

• Enter required maximum velocity e.g. 1m/s

The program will run the simulation and return:

• Computed available remaining flow (consumption) e.g. 8.5 l/s

• Computed actual service pressure e.g. 14.95m

• Computed actual velocity in adjacent pipe(s) e.g. 0.95 m/s

In order to run the network capacity and compute the available flow at the selected node for the required residual pressure and maximum allowable velocity, use the function below.

Definition:

internal static int EmsMUEpanetFF_Q4P_V(string F1, string F2, string F3,
EpanetCallBack F4, Action\<double\> F5, string NodeID, double Pressure, double
MaxVelocity, out double OutPressure, out double OutVelocity, out double
ComputedDemand, out double BaseDemand, double Hour, double Accuracy, string F6)


Where:

• F1: full path to the INP file

• F2: full path to the SUM file

• F3: full path to the RES file

• F4: pointer to a user-supplied call back function, which accepts a character string as its argument to provide progress of the simulation, use NULL when no progress is required

• F5: fraction (percentage) that can be used for the progress bar display

• NodeID: model node ID for which the residual pressure is computed

• Pressure: required residual pressure

• MaxVelocity: maximum allowable velocity; default value of 1.5 m/s for SI units and 5.0 ft/s for English units

• OutPressure: actual residual pressure (computed)

• OutVelocity: actual velocity as computed

• ComputedDemand: computed available flow

• BaseDemand: baseline node demand

• Hour: hour (or fraction of hour) at which the fire flow simulation is computed.

• Accuracy: simulation accuracy in pressure units, e.g. 0.1m

• F6: full path to the epanet2(32).dll or epanet2(64).dll

Return codes (integer code with the simulation status):

• 0: No errors

• -1: Exception encountered, simulation failed

• 1: Static pressure is already below the residual pressure, no flow available

• 2: Cannot find upper flow limit, no flow will be computed

• 3: Cannot iterate flow for pressure, no flow will be computed

• 4: No flow available at this residual pressure

• 5: Node does not exist, no flow will be computed

• 6: No flow available at this residual pressure and velocity

#### Example of use¶

string NodeID = ffNodeID.Text;

double OutFlow = Convert.ToDouble(ffQ.Text);

double MaxVelocity = Convert.ToDouble(ffMaxV.Text);

double Pressure = Convert.ToDouble(ffP.Text);

double Hour = Convert.ToDouble(ffHour.Text);

double Accuracy = Convert.ToDouble(ffAccuracy.Text);

double OutVelocity = 0;

double OutPressure = 0;

double OutBaseDemand = 0;

OutFlow = 0;

retCode = MUEpanet2.MUEpanetFireFlow_Q4P_V(tbInput.Text, tbReport.Text,
tbOutput.Text, PinnedCallBack, null, NodeID, Pressure, MaxVelocity, out
OutPressure, out OutVelocity, out OutFlow, out OutBaseDemand, Hour, Accuracy,
EPANETxDLL);


In addition to this, the program creates a LOG file with results summary and with some additional information about the simulation.

## Pipe criticality analysis¶

Pipe criticality analysis is included in “All Simulations”.

Pipe criticality modelling is required to predict the water distribution system response to pipe breaks situations, planned reconstructions, and other scenarios of limited water supply. Pipe criticality allows also the develop a pipe ranking based on the importance for the water supply and such importance can be then taken into account for the planning of pipe rehabilitation and reconstructions.

The pipe criticality is determined based on evaluation of several (four) performance indicators including:

• Water demand criteria (PI-1)

• Service pressure criteria (PI-2)

• Pipe flow criteria (PI-3)

• Pipe length criteria (PI-4)

The combined pipe criticality is computed as an average of all above performance indicators, i.e.

C (pipe i) = Average (PI-1+PI-2+PI-3+PI-4) (pipe i)

• Pipe flow criteria (PI-1) is computed as water (in flow units) that cannot be delivered through the pipe. The value of 1 corresponds to the total flow.

• Service pressure criteria (PI-2) is computed as number of nodes, where the service pressure is below the required level e.g. 15 m or 20 psi, for example. The value of 1 corresponds to the total number of nodes.

• Water demand criteria (PI-3) is computed as the value of water demand that cannot be delivered due to insufficient service pressure, the value of 1 corresponds to the total water demand consumption.

• Pipe length criteria (PI-4) is computed as a total length of pipes affected by the respective pipe. The value of 1 corresponds to the total pipe length. Similarly, it is possible to use the number of population disconnected from the water supply or number of disconnected residences or houses.

The Pipe Criticality Analysis dialog box is reached by selecting Modules from General Settings from the Table of Contents and the by selecting Advanced Analysis from the Table of Contents. Note that to run the pipe criticality analysis, you need to select Run from within the Pipe Criticality Analysis dialog box.

string SimulationKeywords;

SimulationKeywords = "PIPECRITICALITY";

retCode = MUEpanet2.MUEpanetAllSimulations(tbInput.Text, tbReport.Text,
tbOutput.Text, EpanetCallBack, EpanetProgressCallBack, cts.Token,
SimulationKeywords, EPANETxDLL);


#### Example of use¶

[PIPECRITICALITY]

0 ; Time level in hours for the simulation (use -1 for all time levels)

5 ; Minimum service pressure

0 ; Use zones (0: No, 1: Yes) If No, leave the rest of the data empty

%ZONES

"Red" ; Here is the list of all zones used for computation, line by line

"Blue" ; Here is the list of all zones used for computation, line by line

%PIPES

1 "Red"

1_10 "Red"

1_101 "Red"

1_102 "Red"

1_103 "Red"

1_1032 "Red"

1_1033 "Red"

1_1041 "Red"

1_1045 "Red"

1_1046 "Red"

1_1047 "Red"

;

1_3300 "Blue"

1_3307 "Blue"

1_3308 "Blue"

1_3309 "Blue"

1_3310 "Blue"

1_3311 "Blue"

1_3312 "Blue"

1_3313 "Blue"

1_3316 "Blue"

1_3317 "Blue"

1_3328 "Blue"

1_3346 "Blue"

1_3354 "Blue"

1_3356 "Blue"

1_3358 "Blue"

1_3359 "Blue"


Note, that selecting “-1” for running the pipe criticality in the EPS extended period simulation time (all time levels) will result in large simulation times (tens of minutes of hours for large setups).

The output is written into a comma separated file with the name as the INP file but CVS extension.

List of available pipe criticality result items:

• Q: flow per pipe that was not delivered (flow units or volume units in case of extended period simulation for all time levels)

• P1: performance indicator P1 (-)

• SumNodes: number of nodes where the service pressure is insufficient

• P2: performance indicator P2 (-)

• SumDemand: demand or total water volume in case of extended period simulation for all time levels)

• P3: performance indicator P3 (-)

• SumLength: total pipe length where the service pressure is insufficient

• P4: performance indicator P4 (-)

• C: performance indicator C (-)

## Fire flow analysis¶

Fire flow analysis is included in “All Simulations”. Note, that it is possible to run the fire flow simulation without modifying the INP file – see the section Fire flow analysis, page 5.

string SimulationKeywords;

SimulationKeywords = "FIREFLOW";

retCode = MUEpanet2.MUEpanetAllSimulations(tbInput.Text, tbReport.Text,
tbOutput.Text, EpanetCallBack, EpanetProgressCallBack, cts.Token,
SimulationKeywords, EPANETxDLL);


#### Example of use¶

Example setup for available flow for design pressure

[FIREFLOW]

Q4P ; Fire flow mode (P4Q, Q4P, Q-H)

9 ; StartHour

20 ; Design criteria (pressure or flow) based on the fire flow mode, skip in
case of QH

%BATCHNODELIST

60897

5704

5691

5743

6111

6028

6034

31438

6498

58048

33408

7394

57659

63414

2891

31374

7191

7394

28

34739

249

%HYDRANTLATERAL

1 ; Use hydrant lateral (0, 1)

100 ; Hydrant.Diameter

1.5 ; Hydrant.Length

110 ; Hydrant.Rcoeff

10 ; Hydrant.Lcoeff


Example setup for residual pressure for design flow

[FIREFLOW]

P4Q ; Fire flow mode (P4Q, Q4P, Q-H)

9 ; StartHour

20 ; Design criteria (pressure or flow) based on the fire flow mode, skip in
case of QH

0 10 ; UseFlowMultiplier (0, 1), FlowMultiplier, skip this line in case of Q4P
or QH

%BATCHNODELIST

60897

5704

5691

5743

6111

6028

6034

31438

6498

58048

33408

7394

57659

63414

2891

31374

7191

7394

28

34739

4859

%HYDRANTLATERAL

1 ; Use hydrant lateral (0, 1)

100 ; Hydrant.Diameter

1.5 ; Hydrant.Length

110 ; Hydrant.Rcoeff

10 ; Hydrant.Lcoeff

*Example setup fire hydrant curve*

[FIREFLOW]

Q-H ; Fire flow mode (P4Q, Q4P, Q-H)

9 ; StartHour

%BATCHNODELIST

60897

5704

5691

5743

6111

6028

6034

31438

6498

58048

33408

7394

57659

63414

2891

31374

7191

7394

28

34739

249

%HYDRANTLATERAL

1 ; Use hydrant lateral (0, 1)

100 ; Hydrant.Diameter

1.5 ; Hydrant.Length

110 ; Hydrant.Rcoeff

0 ; Hydrant.Lcoeff


## Flushing analysis¶

Pipe flushing analysis is included in “All Simulations”.

Flushing of pipelines is an old practice, probably done since the early days of municipal water systems. The conventional way to flush pipelines was just to open selected fire hydrants successively and let them flow until the flowing water appeared clean. This is still an effective strategy in many cases. However, these days many water utilities do unidirectional flushing (UDF), which is a more engineered and effective way to flush pipelines. UDF involves closing or opening selected valves to direct flow through target pipes in order to achieve higher velocities for the same hydrant flows. The set of valves that need to be operated and hydrant that is opened is called a flushing sequence. A UDF design consists of a series of flushing sequences that are run in a particular order so that water is always being drawn from clean parts of the system.

The flushing analysis will be implemented in two modes:

A. Conventional flushing

B. Unidirectional flushing

Conventional flushing will allow the user to select multiple outlet nodes for the batch operation in which the outlet nodes will be open and closed in sequential manner (one by one) and they will be kept open for the minimum required time to replace the volume of water before going to open another outlet node. The actual flushing time can be extended (prolonged) by a safety factor that multiplies the minimum required flushing time. The idle time in between switching the outlet nodes can also be defined by the user (delay in hours).

Unidirectional flushing will not run in a batch node, it will run for one specific outlet node but it will allow the user to close additional number of valves (pipes) in order to maximize the flushing result. The user interface of the program could allow the user to find sections valves that could be closed and then determine pipes that need to be closed for the simulation. From a software perspective, there are different ways one can approach UDF design:

• One approach is to allow the user to define the flushing sequences and the order in which they should be run. The software can then determine the required hydrant flows and whether or not any user-specified constraints are violated.

• Another way to approach UDF design is to automatically determine appropriate flushing sequences and the order in which they should be run. This, of course, is a much harder problem to solve from a software perspective. But this approach has the potential to save many hours of work for an engineer. This automatic approach may also be useful if the field crew is not able to flush in the way originally intended because of inoperable or inaccessible valves or hydrants. In such cases, the automatic approach can provide more flexibility for adjusting the UDF design.

UDF removes more mineral and biological deposits in water distribution lines than traditional flushing methods. It cleans piping more efficiently because it uses higher velocities with less water through systematically opening and closing valves. A minimum velocity of 1.5 m/s (5 ft/s) is recommended by AWWA to ensure effective removal of sediment and biological deposits. For this reason, water mains greater than 300 mm diameter are not included in UDF programs, since the required velocity can only be achieved by opening more than two hydrants.

In both cases, A and B, the program will run a full extended period simulation so that it would be possible to display the hydraulic and energy costs results during the flushing operation and compare it with a standard operation. The user interface will also allow the user to select additional watches such as tanks, pumps where the program will report certain specific details e.g. tank levels and volume percentage, pump energy cost detail and hydraulic performance (deviance from the best operating point) and similar.

There are a number of operational guidelines that should be followed to maximize the benefits of the flushing plan and reduce potential problems. These guidelines should be followed at all times while executing the UDF program.

• Flushing should originate at the source and progress outward towards the periphery pipes. This will allow water used to flush a main to originate from a segment that has already been flushed and eliminates flow reversal.

• A larger water main should generally not be flushed from a smaller water main. The velocity reduction in the larger water main will decrease the effectiveness of the scour. This may necessitate redirecting the flow by closing and reopening valves.

• During flushing, care must be taken to avoid reducing water distribution system pressures below 20 psi.

• Before beginning each sequence, verify that there is adequate source water for flushing as either adequate storage volume or operating pumps to provide the required flow rate and duration.

• The time required to flush each hydrant varies, as each hydrant should be flushed until the water clears.

Input for the engine

• List of pipes for reporting flushing results

• List of outlets (nodes or hydrants) (they will be used in a sequential manner, one by one)

• Emitter coefficient or flushing-flowing demand per outlet node (also to be written into the INP file)

• Minimum required service pressure (it will be applied to nodes related to the list of pipes)

• Start flushing node (water source, junction node or a tank, or a just a start node for the tracing)

• Start flushing hour for the first outlet node

• Safety factor, multiplies the minimum required flushing time

• Idle interval, delay in hours in between switching outlet nodes

string SimulationKeywords;

SimulationKeywords = "FLUSHING";

retCode = MUEpanet2.MUEpanetAllSimulations(tbInput.Text, tbReport.Text,
tbOutput.Text, EpanetCallBack, EpanetProgressCallBack, cts.Token,
SimulationKeywords, EPANETxDLL);


#### Example of use¶

Example setup for conventional flushing

[FLUSHING] ; This is the new section for the INP file

0 ; 0: conventional flushing, 1: unidirectional flushing

15 ; Minimum required service pressure

Velocity 1.5 ; or ShearStress 2.5 ; keyword and the value to define the criteria
for successful flushing

11 ; Flushing start hour

3 ; Safety factor, multiplies the minimum required flushing time

0.5 ; Delay in hours in between switching outlet nodes

2 ; Maximum flushing event time in hours

J-xx ; Fresh water source

%OUTLET_NODES ; This is the list of outlet nodes (flushing nodes)

; NodeID Demand/Emitter Value

J-1 Demand 5.0

J-8 Emitter 180

J-25 Emitter 180

%REPORT_PIPES ; This is the list of pipes where the results will be checked and
reported

P-1

P-2

P-3

P-23

P-34

P-56

P-89

P-48

P-32

*Example setup for unidirectional flushing*

[FLUSHING] ; This is a new section for the INP file

1 ; 0: conventional flushing, 1: unidirectional flushing

15 ; Minimum required service pressure (m or psi)

11 ; Flushing start hour

3 ; Safety factor, multiplies the minimum required flushing time

0.5 ; Delay in hours in between switching outlet nodes

2 ; Maximum flushing event time in hours

J-xx ; Fresh water source

J-yy ; Fresh water source

... any number of water sources

%OUTLET_NODES ; This is a list of outlet nodes (flushing nodes)

; NodeID Demand/Emitter Value

J-1 Demand 5.0 ; there will be only one outlet in case of unidirectional
flushing

%REPORT_PIPES ; This is a list of pipes where the results will be checked and
reported

P-1

P-2

P-3

P-23

P-34

P-56

P-89

P-48

P-32

&CLOSED_PIPES ; This is a list of pipes to be closed during the unidirectional
flushing

P-102

P-108

P-110


Output from the engine

• Velocity per pipe (minimum 2.5ft/s or 0.7m/s, target 5ft/s or 1.5m/s)

• Change in velocity per pipe (ft/s, m/s, percentage)

• Shear stress per pipe (lbs/fts2 or kg/ms2)

• Minimum flushing time per pipe (recommended time is 3x more)

• Flow rate per outflow node (hydrant)

• Flow volume per outlet node (hydrant)

• Residual pressure per node (warning if below service pressure)

• Flushing success per pipe (= percentage of how the pipe is clean)

• Outflow from the source node or total demand from the flowing nodes (= water lost or volume unaccounted)

• Volume percentage per storage tank

The output is written into a comma separated file with the name as the INP file but CVS extension.

Example of the output file:

[FLUSHING]

UNITS-LPS

PipeID , Velocity(max) , VelocityChange , ShearStress , CriteriaType , CriteriaValue , CriteriaPct(%) , FlushingTime(min) , FlushingPct(%)

J_2568.J_3608.1 , 0.473 , -0.087 , 2.52E-05 , Velocity , 1.500 , 31 , -1 , 71

J_2568.J_3610.1 , 1.578 , 1.519 , 0.000253 , Velocity , 1.500 , 100 , -1 , 45

J_3606.J_7906.1 , 0.572 , 0.519 , 2.29E-05 , Velocity , 1.500 , 38 , 6 , 100

J_3607.J_3608.1 , 1.078 , 0.837 , 8.71E-05 , Velocity , 1.500 , 71 , 30 , 99

J_3607.J_7909.1 , 0.690 , 0.565 , 2.76E-05 , Velocity , 1.500 , 46 , -1 , 89

J_3609.J_2568.1 , 0.557 , 0.433 , 2.97E-05 , Velocity , 1.500 , 37 , -1 , 22

J_361.J_3608.1 , 0.166 , -0.021 , 2.21E-05 , Velocity , 1.500 , 11 , -1 , 49

J_3610.J_7968.1 , 1.528 , 1.402 , 0.000245 , Velocity , 1.500 , 100 , -1 , 45

J_3612.J_3609.1 , 1.324 , 1.241 , 0.000107 , Velocity , 1.500 , 88 , -1 , 0

J_3615.J_3612.1 , 0.578 , 0.448 , 3.08E-05 , Velocity , 1.500 , 38 , -1 , 0

J_7890.J_3607.1 , 0.572 , 0.549 , 2.29E-05 , Velocity , 1.500 , 38 , 24 , 100

J_7906.J_7890.1 , 0.572 , 0.470 , 2.29E-05 , Velocity , 1.500 , 38 , 18 , 100

J_7909.J_7949.1 , 0.690 , 0.588 , 2.76E-05 , Velocity , 1.500 , 46 , -1 , 85

J_7949.J_3772.1 , 0.690 , 0.690 , 2.76E-05 , Velocity , 1.500 , 46 , -1 , 85

J_7968.J_3611.1 , 1.501 , 1.399 , 0.00024 , Velocity , 1.500 , 100 , -1 , 44

OutletID , Start(hrs:min) , End(hrs:min) , Duration(hrs:min) , AvgDischarge(lps) , WaterVolume(m3) , AvgFlushSuccess(%) , AvgFlushVelocity(%)

J_2568 , 09:00 , 11:00 , 02:00 , 18.000 , 129.600 , 44 , 27

J_3611 , 11:30 , 13:30 , 02:00 , 2.787 , 20.065 , 46 , 53

J_3607 , 14:00 , 16:00 , 02:00 , 22.001 , 158.406 , 46 , 56

J_3772 , 16:30 , 18:30 , 02:00 , 21.226 , 152.825 , 52 , 60

Where:

• CriteriaPct(%): the value indicates how well the flushing criteria was fulfilled during the simulation. Value of 75%, for example, would mean that if the required velocity was e.g.1.5 m/s then the actual maximum velocity reached during the flushing was 75% of that value, i.e. 0.75 * 1.5 = 1.125 m/s.

• FlushingTime (min): the program computes the minimum time required to fully replace the pipeline volume be a fresh water from the flushing source. This time can only be computed in case that it was actually possible to replace 100% of the pipeline volume. In case that the volume of replaced water in the pipeline was not 100%, the minimum flushing time is not computed and the value is set to “-1”.

• FlushingPct(%): the value represents the % of water the water that was replaced in the pipeline during the flushing. Value of 85%, for example, would mean that 85% of the pipeline volume was replaced by a fresh water originating from the source of flushing.

• Comment: indicates the flushing success e.g. pipeline flushed, pipeline flushed but criteria not reached, pipeline not flushed

• Outlets: AvgFlushSucess Average flushing success from pipes (= percentage of how the pipe is clean) weighted by pipe length

## Optimization¶

Optimization analysis is currently under development.

## Results file structures (RES file)¶

The hydraulic engine uses an unformatted binary output file to store both hydraulic and water quality results at uniform reporting intervals. Data written to the file is either 4-byte integers, 4-byte floats, or fixed-size strings whose size is a multiple of 4 bytes. This allows the file to be divided conveniently into 4-byte records.

Nnodes = number of nodes (junctions, reservoirs, tanks)

Nlinks = number of links (pipes, pumps, tanks)

Ntanks = number (reservoirs and tanks)

PROLOGUE

The prologue section contains a static information about the network and setup. Note the magic number that is stored in the prologue section. It is a value that is repeated again in the epilogue section and these two number must match in case that the results file is correctly created (written) or read.

Item Type Number of Bytes
Magic Number Integer 4
Version Integer 4
Number of Nodes (Junctions + Reservoirs + Tanks) Integer 4
Number of Reservoirs & Tanks Integer 4
Number of Links (Pipes + Pumps + Valves) Integer 4
Number of Pumps Integer 4
Number of Valves Integer 4
Water Quality Option Integer 4
Index of Node for Source Tracing Integer 4
Flow Units Option Integer 4
Pressure Units Option Integer 4
Time Statistics Integer 4
Reporting Start Time (seconds) Integer 4
Reporting Time Step (seconds) Integer 4
Simulation Duration (seconds) Integer 4
Problem Title (1st line) Char 80
Problem Title (2nd line) Char 80
Problem Title (3rd line) Char 80
Name of Input File Char 260
Name of Report File Char 260
Name of Chemical Char 41
Chemical Concentration Units Char 41
ID String of Each Node Char 41*Nnodes
ID String of Each Link Char 41*Nlinks
Index of Head Node of Each Link Integer 4xNlinks
Index of Tail Node of Each Link Integer 4xNlinks
Type Code of Each Link Integer 4xNlinks
Node Index of Each Tank Integer 4*Ntanks
Cross-Sectional Area of Each Tank (value of 0 denotes a Reservoir) Float 4*Ntanks
Elevation of Each Node Float 4*Nnodes
Length of Each Link Float 4*Nlinks
Diameter of Each Link Float 4*Nlinks
• 0 = none

• 1 = chemical

• 2 = age

• 3 = source trace

• 0 = cfs

• 1 = gpm

• 2 = mgd

• 3 = Imperial mgd

• 4 = acre-ft/day

• 5 = liters/second

• 6 = liters/minute

• 7 = megaliters/day

• 8 = cubic meters/hour

• 9 = cubic meters/day

• 0 = psi

• 1 = meters

• 2 = kPa

• 0 = none (report time series)

• 1 = report time-averaged values

• 2 = report minimum values

• 3 = report maximum values

• 4 = report ranges

• 0 = Pipe with CV

• 1 = Pipe

• 2 = Pump

• 3 = PRV

• 4 = PSV

• 5 = PBV

• 6 = FCV

• 7 = TCV

• 8 = GPV

ENERGY USE

The Energy use section reports statistical results related to pump energy costs and the overall peak demand cost for the whole system.

Item Type Number of Bytes
Repeated for Each Pump:
Pump Index in list of links Integer 4
Pump Utilization (%) Float 4
Average Efficiency (%) Float 4
Average kwatts/MGal (or kwatts/cu m) Float 4
Average kwatts Float 4
Peak kwatts Float 4
Average Cost per Day Float 4
Peak Demand Cost Float 4

DYNAMIC RESULTS

The Dynamic Results section of the binary Output File contains the following set of data for each reporting period (the reporting time step is written to the Output File's prologue section and the number of such steps is written to the epilogue section):

Item Type Number of Bytes
Demand at Each Node Float 4*Nnodes
Head (Grade) at Each Node Float 4*Nnodes
Pressure at Each Node Float 4*Nnodes
Water Quality at Each Node Float 4*Nnodes
Flow in Each Link (negative for reverse flow) Float 4*Nlinks
Velocity in Each Link Float 4*Nlinks
Headloss per 1000 Units of Length for Each Link (Total head for pumps and head loss for valves) Float 4*Nlinks
Avg. Water Quality in Each Link Float 4*Nlinks
Status Code for Each Link Float 4*Nlinks
Setting for Each Link Float 4*Nlinks
Reaction Rate for Each Link (mass/L/day) Float 4*Nlinks
Friction Factor for Each Link Float 4*Nlinks
• 0 = closed (max. head exceeded)

• 1 = temporarily closed

• 2 = closed

• 3 = open

• 4 = active (partially open)

• 5 = open (max. flow exceeded)

• 6 = open (flow setting not met)

• 7 = open (pressure setting not met)

• Roughness coefficient for Pipes,

• Speed for Pump,

• Setting for Valves

EPILOGUE

The epilogue section contains some statistical information on the water quality parameters and the entire simulation.

Item Type Number of Bytes
Average bulk reaction rate (mass/hr) Float 4
Average wall reaction rate (mass/hr) Float 4
Average tank reaction rate (mass/hr) Float 4
Average source inflow rate (mass/hr) Float 4
Number of Reporting Periods Integer 4
Warning Flag Integer 4
Magic Number Integer 4
• 0 = no warnings

• 1 = warnings were generated

#### Example of use¶

Below is a source code for a function that reads the RES file in an “item-by-item” manner; you can use this code to read the RES file and store the data as necessary.

\#region ResFile

// Return codes:

// 0: OK, file reading was successful

// -1: file reading failed

// -2: Magic numbers do not match, file content not read correctly

internal static int RES_Reader(string ResFile, EpanetCallBack F4,
Action\<double\> F5, CancellationToken token)

{

int result = -1;

if (File.Exists(ResFile))

{

try

{

using (var reader = new FileStream(ResFile, FileMode.Open))

{

var b = new byte[4];

var s80 = new byte[80];

var s260 = new byte[260];

var s41 = new byte[41];

string \_string;

int zeroIndex = 0;

int i = 0;

int k = 0;

int MagicNumberStart = 0;

int MagicNumberEnd = 0;

int NumberOfNodes = 0;

int NumberOfTanks = 0;

int NumberOfLinks = 0;

int NumberOfPumps = 0;

int NumberOfLevels = 0;

int ReportStartTime = 0;

int ReportTimeStep = 0;

int SimulationDuration = 0;

int \_int = 0;

float \_float = 0;

System.Text.Encoding enc = new System.Text.UTF8Encoding(false);

// [PROLOGUE]

\# region Prologue

// Magic number (516110000)

var bytesRead = reader.Read(b, 0, 4);

\_int = BitConverter.ToInt32(b, 0);

MagicNumberStart = \_int;

// Version

bytesRead = reader.Read(b, 0, 4);

\_int = BitConverter.ToInt32(b, 0);

// Nodes

bytesRead = reader.Read(b, 0, 4);

\_int = BitConverter.ToInt32(b, 0);

NumberOfNodes = \_int;

// Tanks and reservoirs

bytesRead = reader.Read(b, 0, 4);

\_int = BitConverter.ToInt32(b, 0);

NumberOfTanks = \_int;

// Links

bytesRead = reader.Read(b, 0, 4);

\_int = BitConverter.ToInt32(b, 0);

NumberOfLinks = \_int;

// Pumps

bytesRead = reader.Read(b, 0, 4);

\_int = BitConverter.ToInt32(b, 0);

NumberOfPumps = \_int;

// Valves

bytesRead = reader.Read(b, 0, 4);

\_int = BitConverter.ToInt32(b, 0);

// Water quality option, 0 = none 1 = chemical 2 = age 3 = source trace

bytesRead = reader.Read(b, 0, 4);

\_int = BitConverter.ToInt32(b, 0);

// Index of source node

bytesRead = reader.Read(b, 0, 4);

\_int = BitConverter.ToInt32(b, 0);

// Flow units option, 0 = cfs 1 = gpm 2 = ac-ft/day 3 = mgd 4 = Liters/sec 5 =
Liters/min 6 = cubic meters/hr 7 = MegaLiters/day

bytesRead = reader.Read(b, 0, 4);

\_int = BitConverter.ToInt32(b, 0);

// Pressure units option, 0 = psi 1 = meters 2 = kPa

bytesRead = reader.Read(b, 0, 4);

\_int = BitConverter.ToInt32(b, 0);

// Time statistics, 0 = no time averaging 1 = results are time-averaged

bytesRead = reader.Read(b, 0, 4);

\_int = BitConverter.ToInt32(b, 0);

// Report start time in seconds

bytesRead = reader.Read(b, 0, 4);

\_int = BitConverter.ToInt32(b, 0);

ReportStartTime = \_int;

// Reporting time step in seconds

bytesRead = reader.Read(b, 0, 4);

\_int = BitConverter.ToInt32(b, 0);

ReportTimeStep = \_int;

// Duration in seconds

bytesRead = reader.Read(b, 0, 4);

\_int = BitConverter.ToInt32(b, 0);

SimulationDuration = \_int;

// Title 1st line (80)

bytesRead = reader.Read(s80, 0, 80);

zeroIndex = Array.IndexOf\<byte\>(s80, 0);

\_string = enc.GetString(s80, 0, zeroIndex \>= 0 ? zeroIndex : s80.Length);

// Title 2nd line (80)

bytesRead = reader.Read(s80, 0, 80);

zeroIndex = Array.IndexOf\<byte\>(s80, 0);

\_string = enc.GetString(s80, 0, zeroIndex \>= 0 ? zeroIndex : s80.Length);

// Title 3rd line (80)

bytesRead = reader.Read(s80, 0, 80);

zeroIndex = Array.IndexOf\<byte\>(s80, 0);

\_string = enc.GetString(s80, 0, zeroIndex \>= 0 ? zeroIndex : s80.Length);

// Name of input file (260)

bytesRead = reader.Read(s260, 0, 260);

zeroIndex = Array.IndexOf\<byte\>(s260, 0);

\_string = enc.GetString(s260, 0, zeroIndex \>= 0 ? zeroIndex : s260.Length);

// Name of report file (260)

bytesRead = reader.Read(s260, i, 260);

zeroIndex = Array.IndexOf\<byte\>(s260, 0);

\_string = enc.GetString(s260, 0, zeroIndex \>= 0 ? zeroIndex : s260.Length);

// Name of chemical (41)

bytesRead = reader.Read(s41, 0, 41);

zeroIndex = Array.IndexOf\<byte\>(s41, 0);

\_string = enc.GetString(s41, 0, zeroIndex \>= 0 ? zeroIndex : s41.Length);

// Chemical concentration units (41)

bytesRead = reader.Read(s41, 0, 41);

zeroIndex = Array.IndexOf\<byte\>(s41, 0);

\_string = enc.GetString(s41, 0, zeroIndex \>= 0 ? zeroIndex : s41.Length);

// ID string of each node (41)

for (i = 0; i \< NumberOfNodes; i += 1)

{

bytesRead = reader.Read(s41, 0, 41);

zeroIndex = Array.IndexOf\<byte\>(s41, 0);

\_string = enc.GetString(s41, 0, zeroIndex \>= 0 ? zeroIndex : s41.Length);

}

// ID string of each link (41)

for (i = 0; i \< NumberOfLinks; i += 1)

{

bytesRead = reader.Read(s41, 0, 41);

zeroIndex = Array.IndexOf\<byte\>(s41, 0);

\_string = enc.GetString(s41, 0, zeroIndex \>= 0 ? zeroIndex : s41.Length);

}

// Index of head node of each link

for (i = 0; i \< NumberOfLinks; i += 1)

{

bytesRead = reader.Read(b, 0, 4);

\_int = BitConverter.ToInt32(b, 0);

}

// Index of tail node of each link

for (i = 0; i \< NumberOfLinks; i += 1)

{

bytesRead = reader.Read(b, 0, 4);

\_int = BitConverter.ToInt32(b, 0);

}

// Type code of each link, 0 = Pipe with CV 1 = Pipe 2 = Pump 3 = PRV 4 = PSV 5
= PBV 6 = FCV 7 = TCV 8 = GPV

for (i = 0; i \< NumberOfLinks; i += 1)

{

bytesRead = reader.Read(b, 0, 4);

\_int = BitConverter.ToInt32(b, 0);

}

// Node index of each tank

for (i = 0; i \< NumberOfTanks; i += 1)

{

bytesRead = reader.Read(b, 0, 4);

\_int = BitConverter.ToInt32(b, 0);

}

// Cross-sectional area of each tank, value 0 denotes a reservoir

for (i = 0; i \< NumberOfTanks; i += 1)

{

bytesRead = reader.Read(b, 0, 4);

\_int = BitConverter.ToInt32(b, 0);

}

// Elevation of each node

for (i = 0; i \< NumberOfNodes; i += 1)

{

bytesRead = reader.Read(b, 0, 4);

\_float = BitConverter.ToSingle(b, 0);

}

// length of each link

for (i = 0; i \< NumberOfLinks; i += 1)

{

bytesRead = reader.Read(b, 0, 4);

\_float = BitConverter.ToSingle(b, 0);

}

// Diameter of each link

for (i = 0; i \< NumberOfLinks; i += 1)

{

bytesRead = reader.Read(b, 0, 4);

\_float = BitConverter.ToSingle(b, 0);

}

\# endregion

// [ENERGY USE]

\# region Energy

for (i = 0; i \< NumberOfPumps; i += 1)

{

// Pump index in list of links

bytesRead = reader.Read(b, 0, 4);

\_int = BitConverter.ToInt32(b, 0);

// Pump utilization (%)

bytesRead = reader.Read(b, 0, 4);

\_float = BitConverter.ToSingle(b, 0);

// Average efficiency (%)

bytesRead = reader.Read(b, 0, 4);

\_float = BitConverter.ToSingle(b, 0);

// Average kwats/MGal or kwats/m3

bytesRead = reader.Read(b, 0, 4);

\_float = BitConverter.ToSingle(b, 0);

// Average kwats

bytesRead = reader.Read(b, 0, 4);

\_float = BitConverter.ToSingle(b, 0);

// Peak kwats

bytesRead = reader.Read(b, 0, 4);

\_float = BitConverter.ToSingle(b, 0);

// Average cost per day

bytesRead = reader.Read(b, 0, 4);

\_float = BitConverter.ToSingle(b, 0);

}

// Peak demand cost

bytesRead = reader.Read(b, 0, 4);

\_float = BitConverter.ToSingle(b, 0);

\# endregion

\# region Dynamic

// [DYNAMIC RESULTS]

NumberOfLevels = (SimulationDuration-ReportStartTime)/ReportTimeStep;

NumberOfLevels = NumberOfLevels + 1;

for (k = 0; k \< NumberOfLevels; k += 1)

{

if (F4 != null)

{

F4(EMS_Utility.EmsClockTimeFormat(k, " time level"));

}

if (F5 != null)

{

double progFraction = (double)k / NumberOfLevels;

F5(progFraction);

}

if (token.IsCancellationRequested)

{

if (F4 != null)

{

F4("Stopped");

}

return -3;

}

for (i = 0; i \< NumberOfNodes; i += 1)

{

// Demand at each node

bytesRead = reader.Read(b, 0, 4);

\_float = BitConverter.ToSingle(b, 0);

}

for (i = 0; i \< NumberOfNodes; i += 1)

{

// Head (grade) at each node

bytesRead = reader.Read(b, 0, 4);

\_float = BitConverter.ToSingle(b, 0);

}

for (i = 0; i \< NumberOfNodes; i += 1)

{

// Pressure at each node

bytesRead = reader.Read(b, 0, 4);

\_float = BitConverter.ToSingle(b, 0);

}

for (i = 0; i \< NumberOfNodes; i += 1)

{

// Water quality at each node

bytesRead = reader.Read(b, 0, 4);

\_float = BitConverter.ToSingle(b, 0);

}

for (i = 0; i \< NumberOfLinks; i += 1)

{

// Flow in each link (negative for reverse flow)

bytesRead = reader.Read(b, 0, 4);

\_float = BitConverter.ToSingle(b, 0);

}

for (i = 0; i \< NumberOfLinks; i += 1)

{

// Velocity in each link

bytesRead = reader.Read(b, 0, 4);

\_float = BitConverter.ToSingle(b, 0);

}

for (i = 0; i \< NumberOfLinks; i += 1)

{

// Headloss per 1000 units of length for each link (total head for pumps and
head loss for valves)

bytesRead = reader.Read(b, 0, 4);

\_float = BitConverter.ToSingle(b, 0);

}

for (i = 0; i \< NumberOfLinks; i += 1)

{

// Average water quality in each link

bytesRead = reader.Read(b, 0, 4);

\_float = BitConverter.ToSingle(b, 0);

}

for (i = 0; i \< NumberOfLinks; i += 1)

{

// Status code for each link, 0 = closed (cannot deliver head) 1 = temporarily
closed 2 = closed 3 = open 4 = active (partially open) 5 = open (max flow
exceeded) 6 = open (flow setting not met) 7 = open (pressure setting not met)

bytesRead = reader.Read(b, 0, 4);

\_float = BitConverter.ToSingle(b, 0);

}

for (i = 0; i \< NumberOfLinks; i += 1)

{

// Setting for each link, roughness coefficient for pipes, speed for pumps,
setting for valves

bytesRead = reader.Read(b, 0, 4);

\_float = BitConverter.ToSingle(b, 0);

}

for (i = 0; i \< NumberOfLinks; i += 1)

{

// Reaction rate for each link

bytesRead = reader.Read(b, 0, 4);

\_float = BitConverter.ToSingle(b, 0);

}

for (i = 0; i \< NumberOfLinks; i += 1)

{

// Friction factor for each link

bytesRead = reader.Read(b, 0, 4);

\_float = BitConverter.ToSingle(b, 0);

}

}

\# endregion

\# region Epilogue

// [EPILOGUE]

// Average bulk reaction rate (mass/hr)

bytesRead = reader.Read(b, 0, 4);

\_float = BitConverter.ToSingle(b, 0);

// Average wall reaction rate (mass/hr)

bytesRead = reader.Read(b, 0, 4);

\_float = BitConverter.ToSingle(b, 0);

// Average tank reaction rate (mass/hr)

bytesRead = reader.Read(b, 0, 4);

\_float = BitConverter.ToSingle(b, 0);

// Average source inflow rate (mass/hr)

bytesRead = reader.Read(b, 0, 4);

\_float = BitConverter.ToSingle(b, 0);

// Number of reporting periods

bytesRead = reader.Read(b, 0, 4);

\_int = BitConverter.ToInt32(b, 0);

// Warning flag, 0: no warnings, 1: warnings were generated

bytesRead = reader.Read(b, 0, 4);

\_int = BitConverter.ToInt32(b, 0);

// Magic number (516110000)

bytesRead = reader.Read(b, 0, 4);

\_int = BitConverter.ToInt32(b, 0);

MagicNumberStart = \_int;

\# endregion

if (MagicNumberStart == MagicNumberEnd)

{

result = 0; // Magic numbers do match

}

else result = -2; // Magic numbers do not match, the file was not read correctly

}

}

catch (Exception e)

{

return -1;

}

}

else

{

}

return result;

}

\#endregion

}


## Extensions to EPANET INP file valid for all simulations¶

This section describes DHI’s addition to the standard EPANET INP file sections. Note, that these features are supported when you use any functions of ”muEPANET” library.

### Variable speed pumps¶

Variable speed pump options allows you to define a set point pressure (constant) that the pump is going to maintain by adjusting its speed. Two different computational algorithms are employed based on the selection of the pump control:

• Downstream node control: this control can be used only when the pump-controlled node is pump downstream node i.e. pump end node. The hydraulic solution is built into the main coefficient matrix and it gives exact results regardless the actual pump speed. Note that the minimum and maximum speed limits of a pump are not taken into account in this case. The speed of the simulation with this option is practically identical to the speed without the use of a VSD control.

• Remote node control: this control can be used to control the pump based on any node in the network (i.e. including the downstream node). In this case, the algorithm iterates the hydraulic solution by varying the relative pump speed within the minimum and maximum speed limits. Note that the exact solution might not be always found such as when the actual pump speed reached the speed limits or when there is a conflict with another variable speed pumps within the same model. The speed of the simulation with this option can be significantly slower than without the use of a VSD control.

#### Example¶

[VSD_PUMPS]

;------------------------------------------------------------------------------------------------------------------------------

; Pump Node Pressure Speed Speed ControlType

; ID ID min max 0/1 (downstream, remote)

;------------------------------------------------------------------------------------------------------------------------------

2010_PUMP3 J-1160 47.0 0.6 1.0 0


### Real-time control analysis¶

Real-time control analysis is part of any hydraulic simulation if respective data input is provided in the [RTC] section of the INP file.

Real time control provides the following operations:

• Variable pump speed to maintain pressure or level or flow/velocity set-points

• Movement of valves to maintain pressure or level or flow/velocity set-points

The purpose of this kind of control is to provide generic way of moving valves and changing pump speed other than what is done using IF-THEN-ELSE rules or VSD pump control. IF-THEN-ELSE rules control valves and pumps instantly (at the time step) and may result, in some cases, in oscillating solutions in between time steps. Or, in case of VSD pump control, they may not be able to operate more than 1 pump within the same zone due to interference of the algorithm with other pumps. The presented real-time control provides an independent mechanism that can be used to determine or simulate pump or valve operations in a physical system.

The real-time control provides two algorithms:

• Linear control

• PID (Proportional – Integral – Derivative) control

Linear control is a mechanism that will increase or decrease the control setting based on the actual value of the measured process variable versus the set-point. The position of a control valve will be increased or decreased and the pump speed will be increased or decreased. The increase and decrease rates as well as the maximum and minimum settings are pre-defined.

PID (Proportional – Integral – Derivative) control is a control loop feedback mechanism (controller) commonly used in industrial control systems. A PID controller continuously calculates an error value e(t) as the difference between a desired set point and a measured process variable. The controller attempts to minimize the error over time by adjustment of a control variable u(t), such as the position of a control valve or a pump speed to a new value determined by a weighted sum:

Real time control provides the following operations:

• Variable pump speed to maintain pressure or level or flow/velocity set-points

• Movement of valves to maintain pressure or level or flow/velocity set-points

The purpose of this kind of control is to provide generic way of moving valves and changing pump speed other than what is done using IF-THEN-ELSE rules or VSD pump control. IF-THEN-ELSE rules control valves and pumps instantly (at the time step) and may result, in some cases, in oscillating solutions in between time steps. Or, in case of VSD pump control, they may not be able to operate more than 1 pump within the same zone due to interference of the algorithm with other pumps. The presented real-time control provides an independent mechanism that can be used to determine or simulate pump or valve operations in a physical system.

The real-time control provides two algorithms:

• Linear control

• PID (Proportional – Integral – Derivative) control

Linear control is a mechanism that will increase or decrease the control setting based on the actual value of the measured process variable versus the set-point. The position of a control valve will be increased or decreased and the pump speed will be increased or decreased. The increase and decrease rates as well as the maximum and minimum settings are pre-defined.

PID (Proportional – Integral – Derivative) control is a control loop feedback mechanism (controller) commonly used in industrial control systems. A PID controller continuously calculates an error value e(t) as the difference between a desired set point and a measured process variable. The controller attempts to minimize the error over time by adjustment of a control variable u(t), such as the position of a control valve or a pump speed to a new value determined by a weighted sum:

u\left( t \right) = \ K_{p}e\left( t \right) + \ K_{i}\int_{0}^{t}{e\left( t \right)t + \ K_{d}}\frac{\Delta e(t)}{\Delta t}

Where Kp, Ki, and Kd are all non-negative coefficients for the proportional, integral, and derivative terms. In this model:

• Kp accounts for present values of the error. For example, if the error is large and positive, the control output will also be large and positive.

• Ki accounts for past values of the error. For example, if the current output is not sufficiently strong, error will accumulate over time, and the controller will respond by applying a stronger action.

• Kd accounts for possible future values of the error, based on its current rate of change.

[RTC]

### Pressure dependent demand analysis¶

Pressure dependent demand analysis is part of any hydraulic simulation if respective data input is provided in the [PDD] section of the INP file.

Traditionally, water demands are defined prior to the simulation and thus independent of the actual pressure. With the Pressure Dependent Demands, the Wagner equation can be used to adjust the node demands based on the available pressure.

Pressure Dependent Demands Analysis is an alternative computational method based on pressure driven analysis comparing to the traditional demand driven analysis. Node demands are automatically adjusted based on the available pressure. This approach can be used to model intermittent water supply, low pressure situations, and it is also suitable for modelling system shut- down and maintenance.

There are three formulations of the demand versus pressure relation that can be used in computation: Wagner, Tucciarelli, and Fujiwara equation. They all adjust the node demand based on the available pressure.

Wagner equation [1]:

Q_{\text{new}} = \ Q_{\text{original}}\left( \frac{P_{\text{actual}} - P_{\text{minimum}}}{P_{\text{required}} - P_{\text{minimum}}} \right)^{1/n}

Tucciarelli equation [2]:

Q_{\text{new}} = \ Q_{\text{original}}\sin^{2}\left( \pi\frac{P_{\text{actual}}}{2\ P_{\text{required}}} \right)

Fujiwara equation [3]:

Q_{\text{new}} = \ Q_{\text{original}}\frac{\left( P_{\text{actual}} - P_{\text{minimum}} \right)^{2}\left( {3P}_{\text{required}}{- 2P}_{\text{actual}} - P_{\text{minimum}} \right)}{\left( P_{\text{required}} - P_{\text{minimum}} \right)^{3}}

where:

• Qnew = adjusted node demand

• Pactual = actual pressure

• Prequired = required pressure (such as e.g. 15 m), node demand is equal to the original demand if the pressure is above the required pressure

• Pminimum = minimum pressure (such as e.g. 5 m), node demand is 0 if the pressure drops below the minimum pressure

• n = coefficient with recommended values between 1.5 and 2.0 (2.0 is recommended by Wagner)

Note that nodes with negative demand i.e. inflow nodes are excluded from the above equation.

#### Example¶

[PDD]

Results of the Pressure dependent demands simulations are written into RESX results file for nodes that were selected as “pressure dependent” and the following results items are stored in the file:

• Demand (pressure depended requested)

• Demand (pressure depended supplied)

• Demand (pressure depended deficit)

• Demand (pressure depended supplied percentage)

Note, that the standard RES file contains all usual simulation result items and that the RESX file provided only optional or additional output.

### Cumulative time analysis¶

Cumulative contact time analysis helps to determine time that the water particles spent in pipes of a particular material, e.g. PVC. Cumulative contact time is recommended in case that there is a risk that the water might be contaminated when in contact with the pipe material for certain number of hours. Evidence was provided that glued PVC pipes joints were subjected to a bad polymerization in which case the no-risk contact time with water should not to exceed 48 hours.

In case of “Cumulative contact time” analysis, the list of pipes (of a specific pipe material) for which the cumulative resident (contact) time is accounted for is provided into the [CCT] section of the INP file.

#### Example of use¶

[CCT]

1_1000 ; Pipe ID that has the same material as selected for the CCT analysis

1_1009

1_1010

1_858

1_872

1_1195

; etc. for all pipes that have the pipe material selected for the CCT analysis


The results of the CCT analysis are written into the standard RES file as any other hydraulics and water quality results.

### Absolute date and time¶

EPANET allows you to use TIME and CLOCKTIME keywords to work with the time during the simulation; TIME is a time in hours since the simulation start and CLOCKTIME is the repeating time based on hours of a day e.g. “every day at 6AM”. However, in some cases when the user needs to run a very long simulation covering several months or even years in duration the use of TIME starts to be cumbersome. Not only that is becomes inconvenient to use hours for days or months but mostly that it is not possible to set the availability of certain infrastructure such as pumps to ON or OFF based on their physical in-service date or maintenance period. A new keyword “ABSDATETIME” was added into the program in order to allow for using absolute date and time in [RULES].

Syntax: "2015-02-01 00:00" where the date and time is defined as “year-month-day hour:minute” regardless the particular local settings in Windows/

#### Example¶

RULE KATHLEEN_PS_CHANGE

IF SYSTEM ABSDATETIME \>= "2015-02-01 00:00"THEN LINK KATHLEEN-PS3 STATUS IS
CLOSEDPRIORITY 2

RULE BC-BCP

IF SYSTEM ABSDATETIME \>= "2015-03-06 00:00"AND SYSTEM ABSDATETIME \<
"2015-03-10 00:00"THEN LINK WCK_C3-PS1 STATUS IS CLOSEDAND LINK WCK_C3-PS2
STATUS IS CLOSEDAND LINK WCK_C3-PS3 STATUS IS CLOSEDAND LINK WCK_C3-PS4 STATUS
IS CLOSEDAND LINK WCK_C3-PS5 STATUS IS CLOSEDAND LINK WCK_C3-PS6 STATUS IS
CLOSEDPRIORITY 5

By default, the program assumes that the start date and time for the rules is
the same as the current computer clock date and time. If the user wants to set
the start date and time to certain fixed date, it can be done using this
additional control statement:

RULE SET SYSTEM ABSDATETIME = "2015-01-01 00:00"

The default setting is like this (does not need to be entered):

RULE SET SYSTEM ABSDATETIME = "NOW”


### Reaction bulk coefficient change¶

EPANET uses global bulk reaction rate coefficient in simulating chemical reaction e.g. residual chlorine analysis. The bulk coefficient is a constant value in time. An extension was made in order to set the initial value of the bulk reaction rate coefficient and then change it to another value at certain time step. This modification was made in order to allow for simulating e.g. fast chlorine decay during the first 5 hours and then change to a slow decay for the rest of the simulation. The new keyword entered into the REACTIONS section is GLOBAL NEWBULK with two value, the first value is the bulk reaction rate and the second value is the time in hours when the change is made.

#### Example¶

[REACTIONS]

;------------------------------------------------------------------

ORDER BULK 1.0

ORDER WALL 1.0

GLOBAL BULK -0.5

GLOBAL WALL 0.0

GLOBAL NEWBULK -0.1 5.0 ; “-0.1” is the new value of the bulk coefficient at the
time of 5 hrs


### Anomalies¶

Anomalies allow you to override model settings or operation for tanks, valves, and pumps in case that yu want to introduce “exceptions” to the standard model behaviour. They can be used to e.g. disable a water source for a certain duration of time, disable a pump, set the valve setting to a fixed value. Types of anomalies: - Source (reservoir, tank) (e.g. Dardennes); Keyword=OUT-OF-SERVICE TANK, OUT-OF-SERVICE RESERVOIR - Pump/valve/pipe availability (e.g. VF18), Keywors=OUT-O-FSERVICE PUMP, OUT-OF-SERVICE VALVE, - Valve fixed setting (% opening for TCV valves), Keyword=FIXED VALVE TCV - Valve maximum flow setting (for FCV valves), Keyword=FIXED VALVE FCV “Start Date” is when the anomaly starts (e.g. 2018-06-23-10-00) it can be any past or future date and the program will handle it properly, you can leave past exception in the list). “End Date” is optional; if it is not provided the program will assume the same setting till the end of simulation Note, that the date/time format is fixed to "year-month-day-hour-min"

Syntax: - In case of tank/reservoir out of service Keyword, NodeID, StartDate, (EndDate) - In case of pump, valve, pipe out of service Keyword, LinkID, StartDate, (EndDate) - In case of TCV valve fixed opening Keyword, LinkID, Valve type, Setting in % opening, StartDate, (EndDate) Valve type is one of these: QUICK-OPENING, GLOBE, BUTTERFLY, PLUG, BALL, EQUAL-PERCENTAGE - In case of FCV valve fixed setting Keyword, LinkID, Setting in flow units matching EPANET flow units, StartDate, (EndDate)

#### Example¶

[ANOMALIES]
OUT-OF-SERVICE TANK DARDENNES  2018-06-23-10-00
OUT-OF-SERVICE RESERVOIR LAVALETTE  2018-09-01-10-00 2018-09-02-10-00
OUT-OF-SERVICE PUMP P_CIRCULAIRE1 2018-09-01-10-00  2018-09-02-00-00
FIXED VALVE TCV VA_POINTEAU PLUG 15  2018-09-01-10-00
FIXED VALVE FCV VANNE_VILLE 180  2018-09-01-10-00