Adding Pipe Leakage Modeling
This commit is contained in:
@@ -7,7 +7,7 @@
|
||||
Authors: see AUTHORS
|
||||
Copyright: see AUTHORS
|
||||
License: see LICENSE
|
||||
Last Updated: 02/05/2023
|
||||
Last Updated: 06/24/2024
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
@@ -127,7 +127,7 @@ char *SectTxt[] = {s_TITLE, s_JUNCTIONS, s_RESERVOIRS,
|
||||
s_REACTIONS, s_MIXING, s_REPORT,
|
||||
s_TIMES, s_OPTIONS, s_COORDS,
|
||||
s_VERTICES, s_LABELS, s_BACKDROP,
|
||||
s_TAGS, s_END,
|
||||
s_TAGS, s_LEAKAGE, s_END,
|
||||
NULL};
|
||||
|
||||
char *Fldname[] = {t_ELEV, t_DEMAND, t_HEAD,
|
||||
|
||||
66
src/epanet.c
66
src/epanet.c
@@ -7,7 +7,7 @@
|
||||
Authors: see AUTHORS
|
||||
Copyright: see AUTHORS
|
||||
License: see LICENSE
|
||||
Last Updated: 09/28/2023
|
||||
Last Updated: 06/26/2024
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
@@ -510,6 +510,10 @@ int DLLEXPORT EN_initH(EN_Project p, int initFlag)
|
||||
return errcode;
|
||||
}
|
||||
}
|
||||
|
||||
// Open pipe leakage modeling system
|
||||
errcode = openleakage(p);
|
||||
if (errcode) return errcode;
|
||||
|
||||
// Initialize hydraulics solver
|
||||
inithyd(p, fflag);
|
||||
@@ -564,7 +568,11 @@ int DLLEXPORT EN_closeH(EN_Project p)
|
||||
*/
|
||||
{
|
||||
if (!p->Openflag) return 102;
|
||||
if (p->hydraul.OpenHflag) closehyd(p);
|
||||
if (p->hydraul.OpenHflag)
|
||||
{
|
||||
closeleakage(p);
|
||||
closehyd(p);
|
||||
}
|
||||
p->hydraul.OpenHflag = FALSE;
|
||||
return 0;
|
||||
}
|
||||
@@ -1044,6 +1052,9 @@ int DLLEXPORT EN_getstatistic(EN_Project p, int type, double *value)
|
||||
case EN_DEMANDREDUCTION:
|
||||
*value = p->hydraul.DemandReduction;
|
||||
break;
|
||||
case EN_LEAKAGELOSS:
|
||||
*value = p->hydraul.LeakageLoss;
|
||||
break;
|
||||
case EN_MASSBALANCE:
|
||||
*value = p->quality.MassBalance.ratio;
|
||||
break;
|
||||
@@ -1864,8 +1875,10 @@ int DLLEXPORT EN_addnode(EN_Project p, const char *id, int nodeType, int *index)
|
||||
hyd->NodeDemand = (double *)realloc(hyd->NodeDemand, size);
|
||||
qual->NodeQual = (double *)realloc(qual->NodeQual, size);
|
||||
hyd->NodeHead = (double *)realloc(hyd->NodeHead, size);
|
||||
hyd->DemandFlow = (double *)realloc(hyd->DemandFlow, size);
|
||||
hyd->FullDemand = (double *)realloc(hyd->FullDemand, size);
|
||||
hyd->EmitterFlow = (double *)realloc(hyd->EmitterFlow, size);
|
||||
hyd->LeakageFlow = (double *)realloc(hyd->LeakageFlow, size);
|
||||
hyd->DemandFlow = (double *)realloc(hyd->DemandFlow, size);
|
||||
|
||||
// Actions taken when a new Junction is added
|
||||
if (nodeType == EN_JUNCTION)
|
||||
@@ -2256,7 +2269,7 @@ int DLLEXPORT EN_getnodevalue(EN_Project p, int index, int property, double *val
|
||||
Ucf[VOLUME];
|
||||
break;
|
||||
|
||||
case EN_DEMAND:
|
||||
case EN_DEMAND: // Consumer Demand + Emitter Flow + Leakage Flow
|
||||
v = hyd->NodeDemand[index] * Ucf[FLOW];
|
||||
break;
|
||||
|
||||
@@ -2336,11 +2349,10 @@ int DLLEXPORT EN_getnodevalue(EN_Project p, int index, int property, double *val
|
||||
|
||||
case EN_DEMANDDEFICIT:
|
||||
if (index > nJuncs) return 0;
|
||||
// After an analysis, DemandFlow contains node's required demand
|
||||
// while NodeDemand contains delivered demand + emitter flow
|
||||
if (hyd->DemandFlow[index] < 0.0) return 0;
|
||||
v = (hyd->DemandFlow[index] -
|
||||
(hyd->NodeDemand[index] - hyd->EmitterFlow[index])) * Ucf[FLOW];
|
||||
// FullDemand contains node's required consumer demand
|
||||
// while DemandFlow contains delivered consumer demand
|
||||
if (hyd->FullDemand[index] <= 0.0) return 0;
|
||||
v = (hyd->FullDemand[index] - hyd->DemandFlow[index]) * Ucf[FLOW];
|
||||
break;
|
||||
|
||||
case EN_NODE_INCONTROL:
|
||||
@@ -2350,6 +2362,18 @@ int DLLEXPORT EN_getnodevalue(EN_Project p, int index, int property, double *val
|
||||
case EN_EMITTERFLOW:
|
||||
v = hyd->EmitterFlow[index] * Ucf[FLOW];
|
||||
break;
|
||||
|
||||
case EN_LEAKAGEFLOW:
|
||||
v = hyd->LeakageFlow[index] * Ucf[FLOW];
|
||||
break;
|
||||
|
||||
case EN_DEMANDFLOW: // Consumer demand delivered
|
||||
v = hyd->DemandFlow[index] * Ucf[FLOW];
|
||||
break;
|
||||
|
||||
case EN_FULLDEMAND: // Consumer demand requested
|
||||
v = hyd->FullDemand[index] * Ucf[FLOW];
|
||||
break;
|
||||
|
||||
default:
|
||||
return 251;
|
||||
@@ -3352,6 +3376,8 @@ int DLLEXPORT EN_addlink(EN_Project p, const char *id, int linkType,
|
||||
}
|
||||
link->Kb = 0;
|
||||
link->Kw = 0;
|
||||
link->LeakArea = 0;
|
||||
link->LeakExpan = 0;
|
||||
link->R = 0;
|
||||
link->Rc = 0;
|
||||
link->Rpt = 0;
|
||||
@@ -3923,6 +3949,18 @@ int DLLEXPORT EN_getlinkvalue(EN_Project p, int index, int property, double *val
|
||||
v = (double)incontrols(p, LINK, index);
|
||||
break;
|
||||
|
||||
case EN_LEAK_AREA:
|
||||
v = Link[index].LeakArea * Ucf[LENGTH];
|
||||
break;
|
||||
|
||||
case EN_LEAK_EXPAN:
|
||||
v = Link[index].LeakExpan * Ucf[LENGTH];
|
||||
break;
|
||||
|
||||
case EN_LINK_LEAKAGE:
|
||||
v = findlinkleakage(p, index) * Ucf[FLOW];
|
||||
break;
|
||||
|
||||
default:
|
||||
return 251;
|
||||
}
|
||||
@@ -4163,6 +4201,16 @@ int DLLEXPORT EN_setlinkvalue(EN_Project p, int index, int property, double valu
|
||||
}
|
||||
break;
|
||||
|
||||
case EN_LEAK_AREA: // leak area in sq mm per 100 pipe length units
|
||||
if (value < 0.0) return 211;
|
||||
Link[index].LeakArea = value / Ucf[LENGTH];
|
||||
break;
|
||||
|
||||
case EN_LEAK_EXPAN: // leak area expansion slope (sq mm per unit of head)
|
||||
if (value < 0.0) return 211;
|
||||
Link[index].LeakExpan = value / Ucf[LENGTH];
|
||||
break;
|
||||
|
||||
default:
|
||||
return 251;
|
||||
}
|
||||
|
||||
191
src/flowbalance.c
Normal file
191
src/flowbalance.c
Normal file
@@ -0,0 +1,191 @@
|
||||
/*
|
||||
******************************************************************************
|
||||
Project: OWA EPANET
|
||||
Version: 2.3
|
||||
Module: flowbalance.c
|
||||
Description: computes components of network's flow balance
|
||||
Authors: see AUTHORS
|
||||
Copyright: see AUTHORS
|
||||
License: see LICENSE
|
||||
Last Updated: 06/26/2024
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include "types.h"
|
||||
|
||||
// Exported functions (declared in funcs.h)
|
||||
//void startflowbalance(Project *);
|
||||
//void updateflowbalance(Project *, long);
|
||||
//void endflowbalance(Project *);
|
||||
|
||||
void startflowbalance(Project *pr)
|
||||
/*
|
||||
**-------------------------------------------------------------------
|
||||
** Input: none
|
||||
** Output: none
|
||||
** Purpose: initializes components of the network's flow balance.
|
||||
**-------------------------------------------------------------------
|
||||
*/
|
||||
{
|
||||
Hydraul *hyd = &pr->hydraul;
|
||||
hyd->FlowBalance.totalInflow = 0.0;
|
||||
hyd->FlowBalance.totalOutflow = 0.0;
|
||||
hyd->FlowBalance.consumerDemand = 0.0;
|
||||
hyd->FlowBalance.emitterDemand = 0.0;
|
||||
hyd->FlowBalance.leakageDemand = 0.0;
|
||||
hyd->FlowBalance.deficitDemand = 0.0;
|
||||
hyd->FlowBalance.storageDemand = 0.0;
|
||||
hyd->FlowBalance.ratio = 0.0;
|
||||
}
|
||||
|
||||
void updateflowbalance(Project *pr, long hstep)
|
||||
/*
|
||||
**-------------------------------------------------------------------
|
||||
** Input: hstep = time step (sec)
|
||||
** Output: none
|
||||
** Purpose: updates components of the system flow balance.
|
||||
**-------------------------------------------------------------------
|
||||
*/
|
||||
{
|
||||
Network *net = &pr->network;
|
||||
Hydraul *hyd = &pr->hydraul;
|
||||
Times *time = &pr->times;
|
||||
|
||||
int i, j;
|
||||
double v, dt, deficit, fullDemand;
|
||||
SflowBalance flowBalance;
|
||||
|
||||
// Determine current time interval in seconds
|
||||
if (time->Dur == 0) dt = 1.0;
|
||||
else if (time->Htime < time->Dur)
|
||||
{
|
||||
dt = (double) hstep;
|
||||
}
|
||||
else return;
|
||||
|
||||
// Initialize local flow balance
|
||||
flowBalance.totalInflow = 0.0;
|
||||
flowBalance.totalOutflow = 0.0;
|
||||
flowBalance.consumerDemand = 0.0;
|
||||
flowBalance.emitterDemand = 0.0;
|
||||
flowBalance.leakageDemand = 0.0;
|
||||
flowBalance.deficitDemand = 0.0;
|
||||
flowBalance.storageDemand = 0.0;
|
||||
fullDemand = 0.0;
|
||||
|
||||
// Initialize demand deficiency & leakage loss
|
||||
hyd->DeficientNodes = 0;
|
||||
hyd->DemandReduction = 0.0;
|
||||
hyd->LeakageLoss = 0.0;
|
||||
|
||||
// Examine each junction node
|
||||
for (i = 1; i <= net->Njuncs; i++)
|
||||
{
|
||||
// Accumulate consumer demand flow
|
||||
v = hyd->DemandFlow[i];
|
||||
if (v < 0.0)
|
||||
flowBalance.totalInflow += (-v);
|
||||
else
|
||||
{
|
||||
fullDemand += hyd->FullDemand[i];
|
||||
flowBalance.consumerDemand += v;
|
||||
flowBalance.totalOutflow += v;
|
||||
}
|
||||
|
||||
// Accumulate emitter and leakage flow
|
||||
v = hyd->EmitterFlow[i];
|
||||
flowBalance.emitterDemand += v;
|
||||
flowBalance.totalOutflow += v;
|
||||
v = hyd->LeakageFlow[i];
|
||||
flowBalance.leakageDemand += v;
|
||||
flowBalance.totalOutflow += v;
|
||||
|
||||
// Accumulate demand deficit flow
|
||||
if (hyd->DemandModel == PDA && hyd->FullDemand[i] > 0.0)
|
||||
{
|
||||
deficit = hyd->FullDemand[i] - hyd->DemandFlow[i];
|
||||
if (deficit > TINY)
|
||||
{
|
||||
hyd->DeficientNodes++;
|
||||
flowBalance.deficitDemand += deficit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Examine each tank/reservoir node
|
||||
for (j = 1; j <= net->Ntanks; j++)
|
||||
{
|
||||
i = net->Tank[j].Node;
|
||||
v = hyd->NodeDemand[i];
|
||||
|
||||
// For a snapshot analysis or a reservoir node
|
||||
if (time->Dur == 0 || net->Tank[j].A == 0.0)
|
||||
{
|
||||
if (v >= 0.0)
|
||||
flowBalance.totalOutflow += v;
|
||||
else
|
||||
flowBalance.totalInflow += (-v);
|
||||
}
|
||||
|
||||
// For tank under extended period analysis
|
||||
else
|
||||
flowBalance.storageDemand += v;
|
||||
}
|
||||
|
||||
// Find % demand reduction & % leakage for current period
|
||||
if (fullDemand > 0.0)
|
||||
hyd->DemandReduction = flowBalance.deficitDemand / fullDemand * 100.0;
|
||||
if (flowBalance.totalInflow > 0.0)
|
||||
hyd->LeakageLoss = flowBalance.leakageDemand / flowBalance.totalInflow * 100.0;
|
||||
|
||||
// Update flow balance for entire run
|
||||
hyd->FlowBalance.totalInflow += flowBalance.totalInflow * dt;
|
||||
hyd->FlowBalance.totalOutflow += flowBalance.totalOutflow * dt;
|
||||
hyd->FlowBalance.consumerDemand += flowBalance.consumerDemand * dt;
|
||||
hyd->FlowBalance.emitterDemand += flowBalance.emitterDemand * dt;
|
||||
hyd->FlowBalance.leakageDemand += flowBalance.leakageDemand * dt;
|
||||
hyd->FlowBalance.deficitDemand += flowBalance.deficitDemand * dt;
|
||||
hyd->FlowBalance.storageDemand += flowBalance.storageDemand * dt;
|
||||
}
|
||||
|
||||
void endflowbalance(Project *pr)
|
||||
/*
|
||||
**-------------------------------------------------------------------
|
||||
** Input: none
|
||||
** Output: none
|
||||
** Purpose: finalizes components of the system flow balance.
|
||||
**-------------------------------------------------------------------
|
||||
*/
|
||||
{
|
||||
Hydraul *hyd = &pr->hydraul;
|
||||
Times *time = &pr->times;
|
||||
|
||||
double seconds, qin, qout, qstor, r;
|
||||
|
||||
if (time->Htime > 0)
|
||||
seconds = time->Htime;
|
||||
else
|
||||
seconds = 1.0;
|
||||
hyd->FlowBalance.totalInflow /= seconds;
|
||||
hyd->FlowBalance.totalOutflow /= seconds;
|
||||
hyd->FlowBalance.consumerDemand /= seconds;
|
||||
hyd->FlowBalance.emitterDemand /= seconds;
|
||||
hyd->FlowBalance.leakageDemand /= seconds;
|
||||
hyd->FlowBalance.deficitDemand /= seconds;
|
||||
hyd->FlowBalance.storageDemand /= seconds;
|
||||
|
||||
qin = hyd->FlowBalance.totalInflow;
|
||||
qout = hyd->FlowBalance.totalOutflow;
|
||||
qstor = hyd->FlowBalance.storageDemand;
|
||||
if (qstor > 0.0)
|
||||
qout += qstor;
|
||||
else
|
||||
qin -= qstor;
|
||||
if (qin == qout)
|
||||
r = 1.0;
|
||||
else if (qin > 0.0)
|
||||
r = qout / qin;
|
||||
else
|
||||
r = 0.0;
|
||||
hyd->FlowBalance.ratio = r;
|
||||
}
|
||||
19
src/funcs.h
19
src/funcs.h
@@ -7,7 +7,7 @@
|
||||
Authors: see AUTHORS
|
||||
Copyright: see AUTHORS
|
||||
License: see LICENSE
|
||||
Last Updated: 09/28/2023
|
||||
Last Updated: 06/26/2024
|
||||
******************************************************************************
|
||||
*/
|
||||
#ifndef FUNCS_H
|
||||
@@ -100,6 +100,7 @@ int controldata(Project *);
|
||||
int energydata(Project *);
|
||||
int sourcedata(Project *);
|
||||
int emitterdata(Project *);
|
||||
int leakagedata(Project *);
|
||||
int qualdata(Project *);
|
||||
int reactdata(Project *);
|
||||
int mixingdata(Project *);
|
||||
@@ -142,6 +143,7 @@ void writecontrolaction(Project *, int, int);
|
||||
void writeruleaction(Project *, int, char *);
|
||||
int writehydwarn(Project *, int,double);
|
||||
void writehyderr(Project *, int);
|
||||
void writeflowbalance(Project *);
|
||||
void writemassbalance(Project *);
|
||||
void writetime(Project *, char *);
|
||||
char *clocktime(char *, long);
|
||||
@@ -195,4 +197,19 @@ int savefinaloutput(Project *);
|
||||
|
||||
int saveinpfile(Project *, const char *);
|
||||
|
||||
// ------- LEAKAGE.C --------------------
|
||||
|
||||
int openleakage(Project *);
|
||||
void closeleakage(Project *);
|
||||
double findlinkleakage(Project *, int);
|
||||
void leakagecoeffs(Project *);
|
||||
double leakageflowchange(Project *, int);
|
||||
int leakagehasconverged(Project *);
|
||||
|
||||
// ------- FLOWBALANCE.C-----------------
|
||||
|
||||
void startflowbalance(Project *);
|
||||
void updateflowbalance(Project *, long);
|
||||
void endflowbalance(Project *);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
Authors: see AUTHORS
|
||||
Copyright: see AUTHORS
|
||||
License: see LICENSE
|
||||
Last Updated: 03/29/2023
|
||||
Last Updated: 06/15/2024
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
@@ -310,6 +310,7 @@ void matrixcoeffs(Project *pr)
|
||||
linkcoeffs(pr);
|
||||
emittercoeffs(pr);
|
||||
demandcoeffs(pr);
|
||||
if (hyd->HasLeakage) leakagecoeffs(pr);
|
||||
|
||||
// Update nodal flow balances with demands and add onto r.h.s. coeffs.
|
||||
nodecoeffs(pr);
|
||||
@@ -606,7 +607,7 @@ void demandheadloss(Project *pr, int i, double dp, double n,
|
||||
Hydraul *hyd = &pr->hydraul;
|
||||
|
||||
double d = hyd->DemandFlow[i];
|
||||
double dfull = hyd->NodeDemand[i];
|
||||
double dfull = hyd->FullDemand[i];
|
||||
double r = d / dfull;
|
||||
|
||||
// Evaluate inverted demand function
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
Authors: see AUTHORS
|
||||
Copyright: see AUTHORS
|
||||
License: see LICENSE
|
||||
Last Updated: 09/28/2023
|
||||
Last Updated: 06/26/2024
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
@@ -63,7 +63,7 @@ int openhyd(Project *pr)
|
||||
|
||||
// Allocate memory for hydraulic variables
|
||||
ERRCODE(allocmatrix(pr));
|
||||
|
||||
|
||||
// Check for unconnected nodes
|
||||
ERRCODE(unlinked(pr));
|
||||
|
||||
@@ -107,8 +107,10 @@ void inithyd(Project *pr, int initflag)
|
||||
hyd->OldStatus[net->Nlinks+i] = TEMPCLOSED;
|
||||
}
|
||||
|
||||
// Initialize emitter flows
|
||||
// Initialize node outflows
|
||||
memset(hyd->DemandFlow,0,(net->Nnodes+1)*sizeof(double));
|
||||
memset(hyd->EmitterFlow,0,(net->Nnodes+1)*sizeof(double));
|
||||
memset(hyd->LeakageFlow,0,(net->Nnodes+1)*sizeof(double));
|
||||
for (i = 1; i <= net->Nnodes; i++)
|
||||
{
|
||||
net->Node[i].ResultIndex = i;
|
||||
@@ -161,6 +163,9 @@ void inithyd(Project *pr, int initflag)
|
||||
pump->Energy.CurrentPower = 0.0;
|
||||
pump->Energy.CurrentEffic = 0.0;
|
||||
}
|
||||
|
||||
// Initialize flow balance
|
||||
startflowbalance(pr);
|
||||
|
||||
// Re-position hydraulics file
|
||||
if (pr->outfile.Saveflag)
|
||||
@@ -253,6 +258,9 @@ int nexthyd(Project *pr, long *tstep)
|
||||
// Accumulate pumping energy
|
||||
if (time->Dur == 0) addenergy(pr,0);
|
||||
else if (time->Htime < time->Dur) addenergy(pr,hydstep);
|
||||
|
||||
// Update flow balance
|
||||
updateflowbalance(pr, hydstep);
|
||||
|
||||
// More time remains - update current time
|
||||
if (time->Htime < time->Dur)
|
||||
@@ -267,6 +275,8 @@ int nexthyd(Project *pr, long *tstep)
|
||||
// No more time remains - force completion of analysis
|
||||
else
|
||||
{
|
||||
endflowbalance(pr);
|
||||
if (pr->report.Statflag) writeflowbalance(pr);
|
||||
time->Htime++;
|
||||
if (pr->quality.OpenQflag) time->Qtime++;
|
||||
}
|
||||
@@ -495,7 +505,7 @@ void demands(Project *pr)
|
||||
if (djunc > 0.0) hyd->Dsystem += djunc;
|
||||
sum += djunc;
|
||||
}
|
||||
hyd->NodeDemand[i] = sum;
|
||||
hyd->FullDemand[i] = sum;
|
||||
|
||||
// Initialize pressure dependent demand
|
||||
hyd->DemandFlow[i] = sum;
|
||||
@@ -1147,4 +1157,3 @@ void resetpumpflow(Project *pr, int i)
|
||||
if (pump->Ptype == CONST_HP)
|
||||
pr->hydraul.LinkFlow[i] = pump->Q0;
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
Authors: see AUTHORS
|
||||
Copyright: see AUTHORS
|
||||
License: see LICENSE
|
||||
Last Updated: 02/14/2022
|
||||
Last Updated: 06/15/2024
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
@@ -47,6 +47,7 @@ static double newflows(Project *, Hydbalance *);
|
||||
static void newlinkflows(Project *, Hydbalance *, double *, double *);
|
||||
static void newemitterflows(Project *, Hydbalance *, double *, double *);
|
||||
static void newdemandflows(Project *, Hydbalance *, double *, double *);
|
||||
static void newleakageflows(Project *, Hydbalance *, double *, double *);
|
||||
|
||||
static void checkhydbalance(Project *, Hydbalance *);
|
||||
static int hasconverged(Project *, double *, Hydbalance *);
|
||||
@@ -93,7 +94,6 @@ int hydsolve(Project *pr, int *iter, double *relerr)
|
||||
int valveChange; // Valve status change flag
|
||||
int statChange; // Non-valve status change flag
|
||||
Hydbalance hydbal; // Hydraulic balance errors
|
||||
double fullDemand; // Full demand for a node (cfs)
|
||||
|
||||
// Initialize status checking & relaxation factor
|
||||
nextcheck = hyd->CheckFreq;
|
||||
@@ -195,12 +195,12 @@ int hydsolve(Project *pr, int *iter, double *relerr)
|
||||
errcode = 110;
|
||||
}
|
||||
|
||||
// Store actual junction outflow in NodeDemand & full demand in DemandFlow
|
||||
// Save total outflow (NodeDemand) at each junction
|
||||
for (i = 1; i <= net->Njuncs; i++)
|
||||
{
|
||||
fullDemand = hyd->NodeDemand[i];
|
||||
hyd->NodeDemand[i] = hyd->DemandFlow[i] + hyd->EmitterFlow[i];
|
||||
hyd->DemandFlow[i] = fullDemand;
|
||||
hyd->NodeDemand[i] = hyd->DemandFlow[i] +
|
||||
hyd->EmitterFlow[i] +
|
||||
hyd->LeakageFlow[i];
|
||||
}
|
||||
|
||||
// Save convergence info
|
||||
@@ -381,6 +381,7 @@ double newflows(Project *pr, Hydbalance *hbal)
|
||||
newlinkflows(pr, hbal, &qsum, &dqsum);
|
||||
newemitterflows(pr, hbal, &qsum, &dqsum);
|
||||
newdemandflows(pr, hbal, &qsum, &dqsum);
|
||||
if (hyd->HasLeakage) newleakageflows(pr, hbal, &qsum, &dqsum);
|
||||
|
||||
// Return ratio of total flow corrections to total flow
|
||||
if (qsum > hyd->Hacc) return (dqsum / qsum);
|
||||
@@ -514,6 +515,45 @@ void newemitterflows(Project *pr, Hydbalance *hbal, double *qsum,
|
||||
}
|
||||
|
||||
|
||||
void newleakageflows(Project *pr, Hydbalance *hbal, double *qsum,
|
||||
double *dqsum)
|
||||
/*
|
||||
**----------------------------------------------------------------
|
||||
** Input: hbal = ptr. to hydraulic balance information
|
||||
** qsum = sum of current system flows
|
||||
** dqsum = sum of system flow changes
|
||||
** Output: updates hbal, qsum and dqsum
|
||||
** Purpose: updates nodal leakage flows after new nodal heads computed
|
||||
**----------------------------------------------------------------
|
||||
*/
|
||||
{
|
||||
Network *net = &pr->network;
|
||||
Hydraul *hyd = &pr->hydraul;
|
||||
|
||||
int i;
|
||||
double dq;
|
||||
|
||||
for (i = 1; i <= net->Njuncs; i++)
|
||||
{
|
||||
// Update leakage flow at node i
|
||||
dq = leakageflowchange(pr, i);
|
||||
if (dq == 0.0) continue;
|
||||
|
||||
// Update system flow summation
|
||||
*qsum += ABS(hyd->LeakageFlow[i]);
|
||||
*dqsum += ABS(dq);
|
||||
|
||||
// Update identity of element with max. flow change
|
||||
if (ABS(dq) > hbal->maxflowchange)
|
||||
{
|
||||
hbal->maxflowchange = ABS(dq);
|
||||
hbal->maxflownode = i;
|
||||
hbal->maxflowlink = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void newdemandflows(Project *pr, Hydbalance *hbal, double *qsum, double *dqsum)
|
||||
/*
|
||||
**----------------------------------------------------------------
|
||||
@@ -546,7 +586,7 @@ void newdemandflows(Project *pr, Hydbalance *hbal, double *qsum, double *dqsum)
|
||||
for (i = 1; i <= net->Njuncs; i++)
|
||||
{
|
||||
// Skip junctions with no positive demand
|
||||
if (hyd->NodeDemand[i] <= 0.0) continue;
|
||||
if (hyd->FullDemand[i] <= 0.0) continue;
|
||||
|
||||
// Find change in demand flow (see hydcoeffs.c)
|
||||
demandheadloss(pr, i, dp, n, &hloss, &hgrad);
|
||||
@@ -555,8 +595,8 @@ void newdemandflows(Project *pr, Hydbalance *hbal, double *qsum, double *dqsum)
|
||||
dq *= hyd->RelaxFactor;
|
||||
|
||||
// Prevent a flow change greater than full demand
|
||||
if (fabs(dq) > 0.4 * hyd->NodeDemand[i])
|
||||
dq = 0.4 * SGN(dq) * hyd->NodeDemand[i];
|
||||
if (fabs(dq) > 0.4 * hyd->FullDemand[i])
|
||||
dq = 0.4 * SGN(dq) * hyd->FullDemand[i];
|
||||
hyd->DemandFlow[i] -= dq;
|
||||
|
||||
// Update system flow summation
|
||||
@@ -641,11 +681,15 @@ int hasconverged(Project *pr, double *relerr, Hydbalance *hbal)
|
||||
if (hyd->FlowChangeLimit > 0.0 &&
|
||||
hbal->maxflowchange > hyd->FlowChangeLimit) return 0;
|
||||
|
||||
// Check for node leakage convergence
|
||||
if (hyd->HasLeakage && !leakagehasconverged(pr)) return 0;
|
||||
|
||||
// Check for pressure driven analysis convergence
|
||||
if (hyd->DemandModel == PDA) return pdaconverged(pr);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
int pdaconverged(Project *pr)
|
||||
/*
|
||||
**--------------------------------------------------------------
|
||||
@@ -659,47 +703,32 @@ int pdaconverged(Project *pr)
|
||||
Hydraul *hyd = &pr->hydraul;
|
||||
|
||||
const double QTOL = 0.0001; // 0.0001 cfs ~= 0.005 gpm ~= 0.2 lpm)
|
||||
int i, converged = 1;
|
||||
double totalDemand = 0.0, totalReduction = 0.0;
|
||||
int i;
|
||||
double dp = hyd->Preq - hyd->Pmin;
|
||||
double p, q, r;
|
||||
|
||||
hyd->DeficientNodes = 0;
|
||||
hyd->DemandReduction = 0.0;
|
||||
|
||||
// Add up number of junctions with demand deficits
|
||||
// Examine each network junction
|
||||
for (i = 1; i <= pr->network.Njuncs; i++)
|
||||
{
|
||||
// Skip nodes whose required demand is non-positive
|
||||
if (hyd->NodeDemand[i] <= 0.0) continue;
|
||||
if (hyd->FullDemand[i] <= 0.0) continue;
|
||||
|
||||
// Evaluate demand equation at current pressure solution
|
||||
p = hyd->NodeHead[i] - pr->network.Node[i].El;
|
||||
if (p <= hyd->Pmin)
|
||||
q = 0.0;
|
||||
else if (p >= hyd->Preq)
|
||||
q = hyd->NodeDemand[i];
|
||||
q = hyd->FullDemand[i];
|
||||
else
|
||||
{
|
||||
r = (p - hyd->Pmin) / dp;
|
||||
q = hyd->NodeDemand[i] * pow(r, hyd->Pexp);
|
||||
q = hyd->FullDemand[i] * pow(r, hyd->Pexp);
|
||||
}
|
||||
|
||||
// Check if demand has not converged
|
||||
if (fabs(q - hyd->DemandFlow[i]) > QTOL)
|
||||
converged = 0;
|
||||
|
||||
// Accumulate total required demand and demand deficit
|
||||
if (hyd->DemandFlow[i] + QTOL < hyd->NodeDemand[i])
|
||||
{
|
||||
hyd->DeficientNodes++;
|
||||
totalDemand += hyd->NodeDemand[i];
|
||||
totalReduction += hyd->NodeDemand[i] - hyd->DemandFlow[i];
|
||||
}
|
||||
}
|
||||
if (totalDemand > 0.0)
|
||||
hyd->DemandReduction = totalReduction / totalDemand * 100.0;
|
||||
return converged;
|
||||
if (fabs(q - hyd->DemandFlow[i]) > QTOL) return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ Description: saves network data to an EPANET formatted text file
|
||||
Authors: see AUTHORS
|
||||
Copyright: see AUTHORS
|
||||
License: see LICENSE
|
||||
Last Updated: 05/11/2024
|
||||
Last Updated: 06/18/2024
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
@@ -369,6 +369,19 @@ int saveinpfile(Project *pr, const char *fname)
|
||||
fprintf(f, "\n %-31s\t%-14.6f", node->ID, ke);
|
||||
}
|
||||
|
||||
// Write [LEAKAGE] section
|
||||
fprintf(f, "\n\n");
|
||||
fprintf(f, s_LEAKAGE);
|
||||
fprintf(f, "\n;;%-31s\t%-14s\t%-14s",
|
||||
"Pipe", "Leak Area", "Leak Expansion");
|
||||
for (i = 1; i <= net->Nlinks; i++)
|
||||
{
|
||||
link = &net->Link[i];
|
||||
if (link->LeakArea == 0.0 && link->LeakExpan == 0.0) continue;
|
||||
fprintf(f, "\n %-31s %14.6f %14.6f", link->ID,
|
||||
link->LeakArea * pr->Ucf[LENGTH], link->LeakExpan * pr->Ucf[LENGTH]);
|
||||
}
|
||||
|
||||
// Write [STATUS] section
|
||||
fprintf(f, "\n\n");
|
||||
fprintf(f, s_STATUS);
|
||||
@@ -584,7 +597,7 @@ int saveinpfile(Project *pr, const char *fname)
|
||||
|
||||
fprintf(f, "\n\n");
|
||||
fprintf(f, s_REACTIONS);
|
||||
fprintf(f, "\n;;%-9s\t%-31s\t%-12s", "Type", "Pipe/Tank", "Coefficient");
|
||||
fprintf(f, "\n;%-9s\t%-31s\t%-12s", "Type", "Pipe/Tank", "Coefficient");
|
||||
|
||||
// Pipe-specific parameters
|
||||
for (i = 1; i <= net->Nlinks; i++)
|
||||
|
||||
@@ -7,7 +7,7 @@ Description: retrieves network data from an EPANET input file
|
||||
Authors: see AUTHORS
|
||||
Copyright: see AUTHORS
|
||||
License: see LICENSE
|
||||
Last Updated: 09/28/2023
|
||||
Last Updated: 06/15/2024
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
@@ -593,6 +593,10 @@ void convertunits(Project *pr)
|
||||
// Convert units on reaction coeffs.
|
||||
link->Kb /= SECperDAY;
|
||||
link->Kw /= SECperDAY;
|
||||
|
||||
// Convert leakage parameters
|
||||
link->LeakArea /= pr->Ucf[LENGTH];
|
||||
link->LeakExpan /= pr->Ucf[LENGTH];
|
||||
}
|
||||
|
||||
else if (link->Type == PUMP)
|
||||
|
||||
@@ -310,6 +310,7 @@ int newline(Project *pr, int sect, char *line)
|
||||
else return 0;
|
||||
case _SOURCES: return (sourcedata(pr));
|
||||
case _EMITTERS: return (emitterdata(pr));
|
||||
case _LEAKAGE: return (leakagedata(pr));
|
||||
case _QUALITY: return (qualdata(pr));
|
||||
case _STATUS: return (statusdata(pr));
|
||||
case _ROUGHNESS: return (0);
|
||||
|
||||
41
src/input3.c
41
src/input3.c
@@ -387,6 +387,8 @@ int pipedata(Project *pr)
|
||||
link->Km = 0.0;
|
||||
link->Kb = MISSING;
|
||||
link->Kw = MISSING;
|
||||
link->LeakArea = 0.0;
|
||||
link->LeakExpan = 0.0;
|
||||
link->Type = PIPE;
|
||||
link->Status = OPEN;
|
||||
link->Rpt = 0;
|
||||
@@ -494,6 +496,8 @@ int pumpdata(Project *pr)
|
||||
link->Km = 0.0;
|
||||
link->Kb = 0.0;
|
||||
link->Kw = 0.0;
|
||||
link->LeakArea = 0.0;
|
||||
link->LeakExpan = 0.0;
|
||||
link->Type = PUMP;
|
||||
link->Status = OPEN;
|
||||
link->Rpt = 0;
|
||||
@@ -613,6 +617,8 @@ int valvedata(Project *pr)
|
||||
link->Km = 0.0;
|
||||
link->Kb = 0.0;
|
||||
link->Kw = 0.0;
|
||||
link->LeakArea = 0.0;
|
||||
link->LeakExpan = 0.0;
|
||||
link->Type = type;
|
||||
link->Status = ACTIVE;
|
||||
link->Rpt = 0;
|
||||
@@ -1118,6 +1124,41 @@ int emitterdata(Project *pr)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int leakagedata(Project *pr)
|
||||
/*
|
||||
**--------------------------------------------------------------
|
||||
** Input: none
|
||||
** Output: returns error code
|
||||
** Purpose: processes link leakage data
|
||||
** Format:
|
||||
** [LEAKAGE]
|
||||
** link C1 C2
|
||||
**--------------------------------------------------------------
|
||||
*/
|
||||
{
|
||||
Network *net = &pr->network;
|
||||
Parser *parser = &pr->parser;
|
||||
|
||||
int j, // Link index
|
||||
n; // # data items
|
||||
double c1, c2; // Flow coeff.
|
||||
|
||||
// Check that link exists & is a pipe
|
||||
n = parser->Ntokens;
|
||||
if (n < 3) return 201;
|
||||
if ((j = findlink(net, parser->Tok[0])) == 0) return setError(parser, 0, 203);
|
||||
if (net->Link[j].Type > PIPE) return 0;
|
||||
|
||||
// Parse leakage coeffs.
|
||||
if (!getfloat(parser->Tok[1], &c1)) return setError(parser, 1, 202);
|
||||
if (c1 < 0.0) return setError(parser, 1, 209);
|
||||
if (!getfloat(parser->Tok[2], &c2)) return setError(parser, 2, 202);
|
||||
if (c2 < 0.0) return setError(parser, 1, 209);
|
||||
net->Link[j].LeakArea = c1;
|
||||
net->Link[j].LeakExpan = c2;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int qualdata(Project *pr)
|
||||
/*
|
||||
**--------------------------------------------------------------
|
||||
|
||||
531
src/leakage.c
Normal file
531
src/leakage.c
Normal file
@@ -0,0 +1,531 @@
|
||||
/*
|
||||
******************************************************************************
|
||||
Project: OWA EPANET
|
||||
Version: 2.3
|
||||
Module: leakage.c
|
||||
Description: models additional nodal demands due to pipe leaks.
|
||||
Authors: see AUTHORS
|
||||
Copyright: see AUTHORS
|
||||
License: see LICENSE
|
||||
Last Updated: 06/14/2024
|
||||
******************************************************************************
|
||||
*/
|
||||
/*
|
||||
This module uses the FAVAD (Fixed and Variable Discharge) equation to model
|
||||
leaky pipes:
|
||||
|
||||
Q = Co * L * (Ao + m * H) * sqrt(H)
|
||||
|
||||
where Q = leak flow rate, Co = an orifice coefficient (= 0.6*sqrt(2g)),
|
||||
L = pipe length, Ao = initial area of leak per unit of pipe length,
|
||||
m = change in leak area per unit of pressure head, and H = pressure head.
|
||||
|
||||
The inverted form of this equation is used to model the leakage demand from
|
||||
a pipe's end node using a pair of equivalent emitters as follows:
|
||||
|
||||
H = Cfa * Qfa^2
|
||||
H = Cva * Qva^(2/3)
|
||||
|
||||
where Qfa = fixed area leakage rate, Qva = variable area leakage rate,
|
||||
Cfa = 1 / SUM(Co*(L/2)*Ao)^2, Cva = 1 / SUM(Co*(L/2)*m)^2/3, and
|
||||
SUM(x) is the summation of x over all pipes connected to the node.
|
||||
|
||||
In implementing this model, the pipe property "LeakArea" represents Ao in
|
||||
sq. mm per 100 units of pipe length and "LeakExpan" represents m in sq. mm
|
||||
per unit of pressure head.
|
||||
|
||||
*/
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
|
||||
#include "types.h"
|
||||
#include "funcs.h"
|
||||
|
||||
// Exported functions (declared in funcs.h)
|
||||
//int openleakage(Project *);
|
||||
//void closeleakage(Project *);
|
||||
//double findlinkleakage(Project *, int);
|
||||
//void leakagecoeffs(Project *);
|
||||
//double leakageflowchange(Project *, int);
|
||||
//int leakagehasconverged(Project *);
|
||||
|
||||
// Local functions
|
||||
static int check_for_leakage(Project *pr);
|
||||
static int create_leakage_objects(Project *pr);
|
||||
static void convert_pipe_to_node_leakage(Project *pr);
|
||||
static void init_node_leakage(Project *pr);
|
||||
static int leakage_headloss(Project* pr, int i, double *hfa,
|
||||
double *gfa, double *hva, double *gva);
|
||||
static void eval_node_leakage(double RQtol, double q, double c,
|
||||
double n, double *h, double *g);
|
||||
static void add_lower_barrier(double q, double* h, double* g);
|
||||
|
||||
|
||||
int openleakage(Project *pr)
|
||||
/*-------------------------------------------------------------
|
||||
** Input: none
|
||||
** Output: returns an error code
|
||||
** Purpose: opens the pipe leakage modeling system
|
||||
**-------------------------------------------------------------
|
||||
*/
|
||||
{
|
||||
Hydraul *hyd = &pr->hydraul;
|
||||
|
||||
int err;
|
||||
|
||||
// Check if project includes leakage
|
||||
closeleakage(pr);
|
||||
hyd->HasLeakage = check_for_leakage(pr);
|
||||
if (!hyd->HasLeakage) return 0;
|
||||
|
||||
// Allocate memory for leakage data objects
|
||||
err = create_leakage_objects(pr);
|
||||
if (err > 0) return err;
|
||||
|
||||
// Convert pipe leakage coeffs. to node coeffs.
|
||||
convert_pipe_to_node_leakage(pr);
|
||||
init_node_leakage(pr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int check_for_leakage(Project *pr)
|
||||
/*-------------------------------------------------------------
|
||||
** Input: none
|
||||
** Output: returns TRUE if any pipes can leak, FALSE otherwise
|
||||
** Purpose: checks if any pipes can leak.
|
||||
**-------------------------------------------------------------
|
||||
*/
|
||||
{
|
||||
Network *net = &pr->network;
|
||||
int i;
|
||||
Slink *link;
|
||||
|
||||
for (i = 1; i <= net->Nlinks; i++)
|
||||
{
|
||||
// Only pipes have leakage
|
||||
link = &net->Link[i];
|
||||
if (link->Type > PIPE) continue;
|
||||
if (link->LeakArea > 0.0 || link->LeakExpan > 0.0) return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
int create_leakage_objects(Project *pr)
|
||||
/*-------------------------------------------------------------
|
||||
** Input: none
|
||||
** Output: returns an error code
|
||||
** Purpose: allocates an array of Leakage objects.
|
||||
**-------------------------------------------------------------
|
||||
*/
|
||||
{
|
||||
Network *net = &pr->network;
|
||||
Hydraul *hyd = &pr->hydraul;
|
||||
int i;
|
||||
|
||||
hyd->Leakage = (Sleakage *)calloc(net->Njuncs + 1, sizeof(Sleakage));
|
||||
if (hyd->Leakage == NULL) return 101;
|
||||
for (i = 1; i <= net->Njuncs; i++)
|
||||
{
|
||||
hyd->Leakage[i].cfa = 0.0;
|
||||
hyd->Leakage[i].cva = 0.0;
|
||||
hyd->Leakage[i].qfa = 0.0;
|
||||
hyd->Leakage[i].qva = 0.0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void convert_pipe_to_node_leakage(Project *pr)
|
||||
/*-------------------------------------------------------------
|
||||
** Input: none
|
||||
** Output: none
|
||||
** Purpose: converts pipe leakage parameters into node leakage
|
||||
** coefficents.
|
||||
**-------------------------------------------------------------
|
||||
*/
|
||||
{
|
||||
Network *net = &pr->network;
|
||||
Hydraul *hyd = &pr->hydraul;
|
||||
|
||||
int i;
|
||||
double c_area, c_expan, c_orif, len;
|
||||
Slink *link;
|
||||
Snode *node1;
|
||||
Snode *node2;
|
||||
|
||||
// Examine each link
|
||||
c_orif = 4.8149866 * 1.e-6;
|
||||
for (i = 1; i <= net->Nlinks; i++)
|
||||
{
|
||||
// Only pipes have leakage
|
||||
link = &net->Link[i];
|
||||
if (link->Type > PIPE) continue;
|
||||
|
||||
// Ignore leakage in a pipe connecting two tanks or
|
||||
// reservoirs (since those nodes don't have demands)
|
||||
node1 = &net->Node[link->N1];
|
||||
node2 = &net->Node[link->N2];
|
||||
if (node1->Type != JUNCTION && node2->Type != JUNCTION) continue;
|
||||
|
||||
// Get pipe's fixed and variable area leak coeffs.
|
||||
if (link->LeakArea == 0.0 && link->LeakExpan == 0.0) continue;
|
||||
c_area = c_orif * link->LeakArea / SQR(MperFT);
|
||||
c_expan = c_orif * link->LeakExpan;
|
||||
|
||||
// Adjust for number of 100-ft pipe sections
|
||||
len = link->Len * pr->Ucf[LENGTH] / 100.;
|
||||
if (node1->Type == JUNCTION && node2->Type == JUNCTION)
|
||||
{
|
||||
len *= 0.5;
|
||||
}
|
||||
c_area *= len;
|
||||
c_expan *= len;
|
||||
|
||||
// Add these coeffs. to pipe's end nodes
|
||||
if (node1->Type == JUNCTION)
|
||||
{
|
||||
hyd->Leakage[link->N1].cfa += c_area;
|
||||
hyd->Leakage[link->N1].cva += c_expan;
|
||||
}
|
||||
if (node2->Type == JUNCTION)
|
||||
{
|
||||
hyd->Leakage[link->N2].cfa += c_area;
|
||||
hyd->Leakage[link->N2].cva += c_expan;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void init_node_leakage(Project *pr)
|
||||
/*-------------------------------------------------------------
|
||||
** Input: none
|
||||
** Output: none
|
||||
** Purpose: initializes node leakage coeffs. and flows.
|
||||
**-------------------------------------------------------------
|
||||
*/
|
||||
{
|
||||
Network *net = &pr->network;
|
||||
Hydraul *hyd = &pr->hydraul;
|
||||
|
||||
int i;
|
||||
double c_area, c_expan;
|
||||
|
||||
for (i = 1; i <= net->Njuncs; i++)
|
||||
{
|
||||
// Coeff. for fixed area leakage
|
||||
c_area = hyd->Leakage[i].cfa;
|
||||
if (c_area > 0.0)
|
||||
hyd->Leakage[i].cfa = 1.0 / (c_area * c_area);
|
||||
else
|
||||
hyd->Leakage[i].cfa = 0.0;
|
||||
|
||||
// Coeff. for variable area leakage
|
||||
c_expan = hyd->Leakage[i].cva;
|
||||
if (c_expan > 0.0)
|
||||
hyd->Leakage[i].cva = 1.0 / pow(c_expan, 2./3.);
|
||||
else
|
||||
hyd->Leakage[i].cva = 0.0;
|
||||
|
||||
// Initialize leakage flow to a non-zero value (as required by
|
||||
// the hydraulic solver)
|
||||
if (hyd->Leakage[i].cfa > 0.0)
|
||||
hyd->Leakage[i].qfa = 0.001;
|
||||
if (hyd->Leakage[i].cva > 0.0)
|
||||
hyd->Leakage[i].qva = 0.001;
|
||||
}
|
||||
}
|
||||
|
||||
void closeleakage(Project *pr)
|
||||
/*-------------------------------------------------------------
|
||||
** Input: none
|
||||
** Output: none
|
||||
** Purpose: frees memory for nodal leakage objects.
|
||||
**-------------------------------------------------------------
|
||||
*/
|
||||
{
|
||||
Hydraul *hyd = &pr->hydraul;
|
||||
if (hyd->Leakage) free(hyd->Leakage);
|
||||
hyd->Leakage = NULL;
|
||||
hyd->HasLeakage = FALSE;
|
||||
}
|
||||
|
||||
double findlinkleakage(Project *pr, int i)
|
||||
/*-------------------------------------------------------------
|
||||
** Input: i = link index
|
||||
** Output: returns link leakage flow (cfs)
|
||||
** Purpose: computes leakage flow from link i at current
|
||||
** hydraulic solution.
|
||||
**-------------------------------------------------------------
|
||||
*/
|
||||
{
|
||||
Network *net = &pr->network;
|
||||
Hydraul *hyd = &pr->hydraul;
|
||||
Smatrix *sm = &hyd->smatrix;
|
||||
Slink *link = &net->Link[i];
|
||||
|
||||
int n1, n2;
|
||||
double h1, h2, hsqrt, a, m, c, len, q1, q2;
|
||||
|
||||
// Only pipes can leak
|
||||
link = &net->Link[i];
|
||||
if (link->Type > PIPE) return 0.0;
|
||||
|
||||
// No leakage if area & expansion are 0
|
||||
if (link->LeakArea == 0.0 && link->LeakExpan == 0.0) return 0.0;
|
||||
|
||||
// No leakage if link's end nodes are both fixed grade
|
||||
n1 = link->N1;
|
||||
n2 = link->N2;
|
||||
if (n1 > net->Njuncs && n2 > net->Njuncs) return 0.0;
|
||||
|
||||
// Pressure head of end nodes
|
||||
h1 = hyd->NodeHead[n1] - net->Node[n1].El;
|
||||
h1 = MAX(h1, 0.0);
|
||||
h2 = hyd->NodeHead[n2] - net->Node[n2].El;
|
||||
h2 = MAX(h2, 0.0);
|
||||
|
||||
// Pipe leak parameters converted to feet
|
||||
a = link->LeakArea / SQR(MperFT);
|
||||
m = link->LeakExpan;
|
||||
len = link->Len * pr->Ucf[LENGTH] / 100.; // # 100 ft pipe lengths
|
||||
c = 4.8149866 * len / 2.0 * 1.e-6;
|
||||
|
||||
// Leakage from 1st half of pipe connected to node n1
|
||||
q1 = 0.0;
|
||||
if (n1 <= net->Njuncs)
|
||||
{
|
||||
hsqrt = sqrt(h1);
|
||||
q1 = c * (a + m * h1) * hsqrt;
|
||||
}
|
||||
|
||||
// Leakage from 2nd half of pipe connected to node n2
|
||||
q2 = 0.0;
|
||||
if (n2 <= net->Njuncs)
|
||||
{
|
||||
hsqrt = sqrt(h2);
|
||||
q2 = c * (a + m * h2) * hsqrt;
|
||||
}
|
||||
|
||||
// Adjust leakage flows to account for one node being fixed grade
|
||||
if (q2 == 0.0) q1 *= 2.0;
|
||||
if (q1 == 0.0) q2 *= 2.0;
|
||||
return q1 + q2;
|
||||
}
|
||||
|
||||
void leakagecoeffs(Project *pr)
|
||||
/*
|
||||
**--------------------------------------------------------------
|
||||
** Input: none
|
||||
** Output: none
|
||||
** Purpose: computes coeffs. of the linearized hydraulic eqns.
|
||||
** contributed by node leakages.
|
||||
**--------------------------------------------------------------
|
||||
*/
|
||||
{
|
||||
Network *net = &pr->network;
|
||||
Hydraul *hyd = &pr->hydraul;
|
||||
Smatrix *sm = &hyd->smatrix;
|
||||
|
||||
int i, row;
|
||||
double hfa, // head loss producing current fixed area leakage
|
||||
gfa, // gradient of fixed area head loss
|
||||
hva, // head loss producing current variable area leakage
|
||||
gva; // gradient of variable area head loss
|
||||
|
||||
Snode* node;
|
||||
|
||||
for (i = 1; i <= net->Njuncs; i++)
|
||||
{
|
||||
// Skip junctions that don't leak
|
||||
node = &net->Node[i];
|
||||
if (!leakage_headloss(pr, i, &hfa, &gfa, &hva, &gva)) continue;
|
||||
|
||||
// Addition to matrix diagonal & r.h.s
|
||||
row = sm->Row[i];
|
||||
if (gfa > 0.0)
|
||||
{
|
||||
sm->Aii[row] += 1.0 / gfa;
|
||||
sm->F[row] += (hfa + node->El) / gfa;
|
||||
}
|
||||
if (gva > 0.0)
|
||||
{
|
||||
sm->Aii[row] += 1.0 / gva;
|
||||
sm->F[row] += (hva + node->El) / gva;
|
||||
}
|
||||
|
||||
// Update node's flow excess (inflow - outflow)
|
||||
hyd->Xflow[i] -= (hyd->Leakage[i].qfa + hyd->Leakage[i].qva);
|
||||
}
|
||||
}
|
||||
|
||||
double leakageflowchange(Project *pr, int i)
|
||||
/*
|
||||
**--------------------------------------------------------------
|
||||
** Input: i = node index
|
||||
** Output: returns change in leakage flow rate
|
||||
** Purpose: finds new leakage flow rate at a node after new
|
||||
** heads are computed by the hydraulic solver.
|
||||
**--------------------------------------------------------------
|
||||
*/
|
||||
{
|
||||
Network *net = &pr->network;
|
||||
Hydraul *hyd = &pr->hydraul;
|
||||
|
||||
double hfa, gfa, hva, gva, // same as defined in leakage_solvercoeffs()
|
||||
dh, dqfa, dqva;
|
||||
|
||||
// Find the head loss and gradient of the inverted leakage
|
||||
// equation for both fixed and variable area leakage at the
|
||||
// current leakage flow rates
|
||||
if (!leakage_headloss(pr, i, &hfa, &gfa, &hva, &gva)) return 0.0;
|
||||
|
||||
// Pressure head using latest head solution
|
||||
dh = hyd->NodeHead[i] - net->Node[i].El;
|
||||
|
||||
// GGA flow update formula for fixed area leakage
|
||||
dqfa = 0.0;
|
||||
if (gfa > 0.0)
|
||||
{
|
||||
dqfa = (hfa - dh) / gfa * hyd->RelaxFactor;
|
||||
hyd->Leakage[i].qfa -= dqfa;
|
||||
}
|
||||
|
||||
// GGA flow update formula for variable area leakage
|
||||
dqva = 0.0;
|
||||
if (gva > 0.0)
|
||||
{
|
||||
dqva = (hva - dh) / gva * hyd->RelaxFactor;
|
||||
hyd->Leakage[i].qva -= dqva;
|
||||
}
|
||||
|
||||
// New leakage flow at the node
|
||||
hyd->LeakageFlow[i] = hyd->Leakage[i].qfa + hyd->Leakage[i].qva;
|
||||
return dqfa + dqva;
|
||||
}
|
||||
|
||||
int leakagehasconverged(Project *pr)
|
||||
/*
|
||||
**--------------------------------------------------------------
|
||||
** Input: none
|
||||
** Output: returns TRUE if leakage calculations converged,
|
||||
** FALSE if not
|
||||
** Purpose: checks if leakage calculations have converged.
|
||||
**--------------------------------------------------------------
|
||||
*/
|
||||
{
|
||||
Network *net = &pr->network;
|
||||
Hydraul *hyd = &pr->hydraul;
|
||||
int i;
|
||||
double h, qref, qtest;
|
||||
const double ABSTOL = 0.0001; // 0.0001 cfs ~= 0.005 gpm ~= 0.2 lpm)
|
||||
const double RELTOL = 0.001;
|
||||
|
||||
for (i = 1; i <= net->Njuncs; i++)
|
||||
{
|
||||
// Skip junctions that don't leak
|
||||
if (hyd->Leakage[i].cfa == 0 && hyd->Leakage[i].cva == 0) continue;
|
||||
|
||||
// Evaluate node's pressure head
|
||||
h = hyd->NodeHead[i] - net->Node[i].El;
|
||||
|
||||
// Directly compute a reference leakage at this pressure head
|
||||
qref = 0.0;
|
||||
// Contribution from pipes with fixed area leaks
|
||||
if (hyd->Leakage[i].cfa > 0.0)
|
||||
qref = sqrt(h / hyd->Leakage[i].cfa);
|
||||
// Contribution from pipes with variable area leaks
|
||||
if (hyd->Leakage[i].cva > 0.0)
|
||||
qref += pow((h / hyd->Leakage[i].cva), 1.5);
|
||||
|
||||
// Compare reference leakage to solution leakage
|
||||
qtest = hyd->Leakage[i].qfa + hyd->Leakage[i].qva;
|
||||
if (fabs(qref - qtest) > ABSTOL + RELTOL * qref) return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
int leakage_headloss(Project* pr, int i, double *hfa, double *gfa,
|
||||
double *hva, double *gva)
|
||||
/*
|
||||
**--------------------------------------------------------------
|
||||
** Input: i = node index
|
||||
** Output: hfa = fixed area leak head loss (ft)
|
||||
** gfa = gradient of fixed area head loss (ft/cfs)
|
||||
** hva = variable area leak head loss (ft)
|
||||
** gva = gradient of variable area head loss (ft/cfs)
|
||||
** returns TRUE if node has leakage, FALSE otherwise
|
||||
** Purpose: finds head loss and its gradient for a node's
|
||||
** leakage as a function of leakage flow.
|
||||
**--------------------------------------------------------------
|
||||
*/
|
||||
{
|
||||
Hydraul *hyd = &pr->hydraul;
|
||||
if (hyd->Leakage[i].cfa == 0.0 && hyd->Leakage[i].cva == 0.0) return FALSE;
|
||||
if (hyd->Leakage[i].cfa == 0.0)
|
||||
{
|
||||
*hfa = 0.0;
|
||||
*gfa = 0.0;
|
||||
}
|
||||
else
|
||||
eval_node_leakage(hyd->RQtol, hyd->Leakage[i].qfa, hyd->Leakage[i].cfa,
|
||||
0.5, hfa, gfa);
|
||||
if (hyd->Leakage[i].cva == 0.0)
|
||||
{
|
||||
*hva = 0.0;
|
||||
*gva = 0.0;
|
||||
}
|
||||
else
|
||||
eval_node_leakage(hyd->RQtol, hyd->Leakage[i].qva, hyd->Leakage[i].cva,
|
||||
1.5, hva, gva);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void eval_node_leakage(double RQtol, double q, double c, double n,
|
||||
double *h, double *g)
|
||||
/*
|
||||
**--------------------------------------------------------------
|
||||
** Input: RQtol = low gradient tolerance (ft/cfs)
|
||||
** q = leakage flow rate (cfs)
|
||||
** c = leakage head loss coefficient
|
||||
** n = leakage head loss exponent
|
||||
** Output: h = leakage head loss (ft)
|
||||
** g = gradient of leakage head loss (ft/cfs)
|
||||
** Purpose: evaluates inverted form of leakage equation to
|
||||
** compute head loss and its gradient as a function
|
||||
** flow.
|
||||
**--------------------------------------------------------------
|
||||
*/
|
||||
{
|
||||
n = 1.0 / n;
|
||||
*g = n * c * pow(fabs(q), n - 1.0);
|
||||
|
||||
// Use linear head loss function for small gradient
|
||||
if (*g < RQtol)
|
||||
{
|
||||
*g = RQtol / n;
|
||||
*h = (*g) * q;
|
||||
}
|
||||
|
||||
// Otherwise use normal leakage head loss function
|
||||
else *h = (*g) * q / n;
|
||||
|
||||
// Prevent leakage from going negative
|
||||
add_lower_barrier(q, h, g);
|
||||
}
|
||||
|
||||
void add_lower_barrier(double q, double* h, double* g)
|
||||
/*
|
||||
**--------------------------------------------------------------------
|
||||
** Input: q = current flow rate
|
||||
** Output: h = head loss value
|
||||
** g = head loss gradient value
|
||||
** Purpose: adds a head loss barrier to prevent flow from falling
|
||||
** below 0.
|
||||
**--------------------------------------------------------------------
|
||||
*/
|
||||
{
|
||||
double a = 1.e9 * q;
|
||||
double b = sqrt(a*a + 1.e-6);
|
||||
*h += (a - b) / 2.;
|
||||
*g += (1.e9 / 2.) * ( 1.0 - a / b);
|
||||
}
|
||||
@@ -7,7 +7,7 @@
|
||||
Authors: see AUTHORS
|
||||
Copyright: see AUTHORS
|
||||
License: see LICENSE
|
||||
Last Updated: 09/28/2023
|
||||
Last Updated: 06/24/2024
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
@@ -328,8 +328,11 @@ void initpointers(Project *pr)
|
||||
pr->hydraul.P = NULL;
|
||||
pr->hydraul.Y = NULL;
|
||||
pr->hydraul.Xflow = NULL;
|
||||
pr->hydraul.FullDemand = NULL;
|
||||
pr->hydraul.DemandFlow = NULL;
|
||||
pr->hydraul.EmitterFlow = NULL;
|
||||
pr->hydraul.LeakageFlow = NULL;
|
||||
pr->hydraul.Leakage = NULL;
|
||||
|
||||
pr->quality.NodeQual = NULL;
|
||||
pr->quality.PipeRateCoeff = NULL;
|
||||
@@ -392,14 +395,18 @@ int allocdata(Project *pr)
|
||||
pr->hydraul.NodeDemand = (double *)calloc(n, sizeof(double));
|
||||
pr->hydraul.NodeHead = (double *)calloc(n, sizeof(double));
|
||||
pr->quality.NodeQual = (double *)calloc(n, sizeof(double));
|
||||
pr->hydraul.FullDemand = (double *)calloc(n, sizeof(double));
|
||||
pr->hydraul.DemandFlow = (double *)calloc(n, sizeof(double));
|
||||
pr->hydraul.EmitterFlow = (double *)calloc(n, sizeof(double));
|
||||
pr->hydraul.LeakageFlow = (double *)calloc(n, sizeof(double));
|
||||
ERRCODE(MEMCHECK(pr->network.Node));
|
||||
ERRCODE(MEMCHECK(pr->hydraul.NodeDemand));
|
||||
ERRCODE(MEMCHECK(pr->hydraul.NodeHead));
|
||||
ERRCODE(MEMCHECK(pr->quality.NodeQual));
|
||||
ERRCODE(MEMCHECK(pr->hydraul.FullDemand));
|
||||
ERRCODE(MEMCHECK(pr->hydraul.DemandFlow));
|
||||
ERRCODE(MEMCHECK(pr->hydraul.EmitterFlow));
|
||||
ERRCODE(MEMCHECK(pr->hydraul.LeakageFlow));
|
||||
}
|
||||
|
||||
// Allocate memory for network links
|
||||
@@ -471,8 +478,10 @@ void freedata(Project *pr)
|
||||
free(pr->hydraul.LinkFlow);
|
||||
free(pr->hydraul.LinkSetting);
|
||||
free(pr->hydraul.LinkStatus);
|
||||
free(pr->hydraul.FullDemand);
|
||||
free(pr->hydraul.DemandFlow);
|
||||
free(pr->hydraul.EmitterFlow);
|
||||
free(pr->hydraul.LeakageFlow);
|
||||
free(pr->quality.NodeQual);
|
||||
|
||||
// Free memory used for nodal adjacency lists
|
||||
|
||||
38
src/report.c
38
src/report.c
@@ -422,6 +422,44 @@ void writehydstat(Project *pr, int iter, double relerr)
|
||||
writeline(pr, " ");
|
||||
}
|
||||
|
||||
void writeflowbalance(Project *pr)
|
||||
/*
|
||||
**-------------------------------------------------------------
|
||||
** Input: none
|
||||
** Output: none
|
||||
** Purpose: writes hydraulic flow balance ratio to report file.
|
||||
**-------------------------------------------------------------
|
||||
*/
|
||||
{
|
||||
Hydraul *hyd = &pr->hydraul;
|
||||
Report *rpt = &pr->report;
|
||||
char s1[MAXMSG+1];
|
||||
double ucf = pr->Ucf[FLOW];
|
||||
|
||||
snprintf(s1, MAXMSG, "Hydraulic Flow Balance (%s)", rpt->Field[DEMAND].Units);
|
||||
writeline(pr, s1);
|
||||
snprintf(s1, MAXMSG, "================================");
|
||||
writeline(pr, s1);
|
||||
snprintf(s1, MAXMSG, "Total Inflow: %12.3f", hyd->FlowBalance.totalInflow*ucf);
|
||||
writeline(pr, s1);
|
||||
snprintf(s1, MAXMSG, "Consumer Demand: %12.3f", hyd->FlowBalance.consumerDemand*ucf);
|
||||
writeline(pr, s1);
|
||||
snprintf(s1, MAXMSG, "Demand Deficit: %12.3f", hyd->FlowBalance.deficitDemand*ucf);
|
||||
writeline(pr, s1);
|
||||
snprintf(s1, MAXMSG, "Emitter Flow: %12.3f", hyd->FlowBalance.emitterDemand*ucf);
|
||||
writeline(pr, s1);
|
||||
snprintf(s1, MAXMSG, "Leakage Flow: %12.3f", hyd->FlowBalance.leakageDemand*ucf);
|
||||
writeline(pr, s1);
|
||||
snprintf(s1, MAXMSG, "Total Outflow: %12.3f", hyd->FlowBalance.totalOutflow*ucf);
|
||||
writeline(pr, s1);
|
||||
snprintf(s1, MAXMSG, "Storage Flow: %12.3f", hyd->FlowBalance.storageDemand*ucf);
|
||||
writeline(pr, s1);
|
||||
snprintf(s1, MAXMSG, "Flow Ratio: %12.3f", hyd->FlowBalance.ratio);
|
||||
writeline(pr, s1);
|
||||
snprintf(s1, MAXMSG, "================================\n");
|
||||
writeline(pr, s1);
|
||||
}
|
||||
|
||||
void writemassbalance(Project *pr)
|
||||
/*
|
||||
**-------------------------------------------------------------
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
Authors: see AUTHORS
|
||||
Copyright: see AUTHORS
|
||||
License: see LICENSE
|
||||
Last Updated: 05/11/2024
|
||||
Last Updated: 06/15/2024
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
@@ -151,6 +151,9 @@
|
||||
#define w_REQUIRED "REQ"
|
||||
#define w_EXPONENT "EXP"
|
||||
|
||||
#define w_AREA "AREA"
|
||||
#define w_EXPAN "EXPAN"
|
||||
|
||||
#define w_SECONDS "SEC"
|
||||
#define w_MINUTES "MIN"
|
||||
#define w_HOURS "HOU"
|
||||
@@ -208,6 +211,7 @@
|
||||
#define s_DEMANDS "[DEMANDS]"
|
||||
#define s_SOURCES "[SOURCES]"
|
||||
#define s_EMITTERS "[EMITTERS]"
|
||||
#define s_LEAKAGE "[LEAKAGE]"
|
||||
#define s_PATTERNS "[PATTERNS]"
|
||||
#define s_CURVES "[CURVES]"
|
||||
#define s_QUALITY "[QUALITY]"
|
||||
|
||||
43
src/types.h
43
src/types.h
@@ -7,7 +7,7 @@
|
||||
Authors: see AUTHORS
|
||||
Copyright: see AUTHORS
|
||||
License: see LICENSE
|
||||
Last Updated: 07/17/2023
|
||||
Last Updated: 06/15/2024
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
@@ -294,7 +294,7 @@ typedef enum {
|
||||
_VALVES, _CONTROLS, _RULES, _DEMANDS, _SOURCES, _EMITTERS,
|
||||
_PATTERNS, _CURVES, _QUALITY, _STATUS, _ROUGHNESS, _ENERGY,
|
||||
_REACTIONS, _MIXING, _REPORT, _TIMES, _OPTIONS,
|
||||
_COORDS, _VERTICES, _LABELS, _BACKDROP, _TAGS, _END
|
||||
_COORDS, _VERTICES, _LABELS, _BACKDROP, _TAGS, _LEAKAGE, _END
|
||||
} SectionType;
|
||||
|
||||
typedef enum {
|
||||
@@ -413,6 +413,8 @@ typedef struct // Link Object
|
||||
double Kw; // wall react. coef.
|
||||
double R; // flow resistance
|
||||
double Rc; // reaction coeff.
|
||||
double LeakArea; // leak area (sq mm per 100 pipe length units
|
||||
double LeakExpan; // leak expansion (sq mm per unit of head)
|
||||
LinkType Type; // link type
|
||||
StatusType Status; // initial status
|
||||
Pvertices Vertices; // internal vertex coordinates
|
||||
@@ -549,6 +551,26 @@ typedef struct // Mass Balance Components
|
||||
double ratio; // ratio of mass added to mass lost
|
||||
} SmassBalance;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
double totalInflow;
|
||||
double totalOutflow;
|
||||
double consumerDemand;
|
||||
double emitterDemand;
|
||||
double leakageDemand;
|
||||
double deficitDemand;
|
||||
double storageDemand;
|
||||
double ratio;
|
||||
} SflowBalance;
|
||||
|
||||
typedef struct // Node Leakage Object
|
||||
{
|
||||
double qfa; // fixed area leakage flow
|
||||
double qva; // variable area leakage flow
|
||||
double cfa; // fixed area leakage coeff.
|
||||
double cva; // variable area leakage coeff.
|
||||
} Sleakage;
|
||||
|
||||
/*
|
||||
------------------------------------------------------
|
||||
Wrapper Data Structures
|
||||
@@ -712,9 +734,11 @@ typedef struct {
|
||||
|
||||
double
|
||||
*NodeHead, // Node hydraulic heads
|
||||
*NodeDemand, // Node demand + emitter flows
|
||||
*DemandFlow, // Work array of demand flows
|
||||
*EmitterFlow, // Emitter outflows
|
||||
*NodeDemand, // Node total demand (consumer + emitter + leakage)
|
||||
*FullDemand, // Required consumer demand
|
||||
*DemandFlow, // Demand flow from nodes
|
||||
*EmitterFlow, // Emitter flow from nodes
|
||||
*LeakageFlow, // Leakage flow from nodes
|
||||
*LinkFlow, // Link flows
|
||||
*LinkSetting, // Link settings
|
||||
Htol, // Hydraulic head tolerance
|
||||
@@ -741,6 +765,7 @@ typedef struct {
|
||||
MaxHeadError, // Max. error for link head loss
|
||||
MaxFlowChange, // Max. change in link flow
|
||||
DemandReduction, // % demand reduction at pressure deficient nodes
|
||||
LeakageLoss, // % system leakage loss
|
||||
RelaxFactor, // Relaxation factor for flow updating
|
||||
*P, // Inverse of head loss derivatives
|
||||
*Y, // Flow correction factors
|
||||
@@ -759,12 +784,18 @@ typedef struct {
|
||||
MaxCheck, // Hydraulic trials limit on status checks
|
||||
OpenHflag, // Hydraulic system opened flag
|
||||
Haltflag, // Flag to halt simulation
|
||||
DeficientNodes; // Number of pressure deficient nodes
|
||||
DeficientNodes, // Number of pressure deficient nodes
|
||||
HasLeakage; // TRUE if project has non-zero leakage parameters
|
||||
|
||||
Sleakage *Leakage; // Array of node leakage parameters
|
||||
|
||||
StatusType
|
||||
*LinkStatus, // Link status
|
||||
*OldStatus; // Previous link/tank status
|
||||
|
||||
SflowBalance
|
||||
FlowBalance; // Flow balance components
|
||||
|
||||
Smatrix smatrix; // Sparse matrix storage
|
||||
|
||||
} Hydraul;
|
||||
|
||||
Reference in New Issue
Block a user