diff --git a/ReleaseNotes2_3.md b/ReleaseNotes2_3.md
index 7042988..5a3b2d3 100644
--- a/ReleaseNotes2_3.md
+++ b/ReleaseNotes2_3.md
@@ -24,6 +24,7 @@ This document describes the changes and updates that have been made in version 2
- dropping the check for identical status report content since it prevents accepting code changes that produce more accurate solutions in fewer iterations.
- A possible loss of network connectivity when evaluating a Pressure Sustaining Valve was prevented.
- Having the implied loss coefficient for an active Flow Control Valve be less than its fully opened value was prevented.
+ - A new type of valve, a Positional Control Valve (PCV), was added that uses a valve characteristic curve to relate its loss coefficient to its fraction open setting.
diff --git a/doc/input-file.dox b/doc/input-file.dox
index 8bbe5c1..f4d36ce 100644
--- a/doc/input-file.dox
+++ b/doc/input-file.dox
@@ -92,9 +92,10 @@ __Remarks:__
- Head v. Flow for pumps
- Efficiency v. Flow for pumps
- Volume v. Depth for tanks
+ - Fraction of fully open flow v. fraction open for Positional Control Valves
- Head Loss v. Flow for General Purpose Valves
2. The points of a curve must be entered in order of increasing X-values (lower to higher).
-3. If the input file will be used with the Windows version of EPANET, then adding a comment which contains the curve type and description, separated by a colon, directly above the first entry for a curve will ensure that these items appear correctly in EPANET's Curve Editor. Curve types include PUMP, EFFICIENCY, VOLUME, and HEADLOSS. See the examples below.
+3. If the input file will be used with the Windows version of EPANET, then adding a comment which contains the curve type and description, separated by a colon, directly above the first entry for a curve will ensure that these items appear correctly in EPANET's Curve Editor. Curve types include PUMP, EFFICIENCY, VOLUME, VALVE, and HEADLOSS. See the examples below.
__Example:__
```
@@ -1028,7 +1029,8 @@ One line for each valve containing:
- Diameter, inches (mm)
- Valve type
- Valve setting
-- Minor loss coefficient
+- Minor loss coefficient when fully open
+- ID of valve characteristic curve (PCVs only)
__Remarks:__
1. Valve types and settings include:
@@ -1038,9 +1040,14 @@ __Remarks:__
|PSV (pressure sustaining valve) | Pressure, psi (m) |
|PBV (pressure breaker valve) | Pressure, psi (m) |
|FCV (flow control valve) | Flow (flow units) |
-|TCV (throttle control valve) | Loss Coefficient |
+|TCV (throttle control valve) | Partially open loss coefficient |
+|PCV (positional control valve) | Fraction open |
|GPV (general purpose valve) | ID of head loss curve |
2. Shutoff valves and check valves are considered to be part of a pipe, not a separate control valve component (see @ref PipesPage).
+3. The loss coefficient setting for a TCV should not be less than its fully open loss coefficient.
+4. The characteristic curve for a PCV relates the valve's fraction of fully open flow to the fraction open. If not supplied then a linear curve is assumed.
+5. The partially opened loss coefficient for a PCV is the inverse of the squared value from its characteristic curve times the fully open loss coefficient.
+6. The head loss curve for a GPV relates head loss across the valve to the flow rate through it.
*/
/**
diff --git a/include/epanet.cs b/include/epanet.cs
index 2cec376..68489e6 100644
--- a/include/epanet.cs
+++ b/include/epanet.cs
@@ -73,6 +73,9 @@ namespace EpanetCSharpLibrary
public const int EN_PUMP_ECURVE = 20;
public const int EN_PUMP_ECOST = 21;
public const int EN_PUMP_EPAT = 22;
+ public const int EN_LINK_INCONTROL = 23;
+ public const int EN_GPV_CURVE = 24;
+ public const int EN_PCV_CURVE = 25;
public const int EN_DURATION = 0; //Time parameters
public const int EN_HYDSTEP = 1;
@@ -126,6 +129,7 @@ namespace EpanetCSharpLibrary
public const int EN_FCV = 6;
public const int EN_TCV = 7;
public const int EN_GPV = 8;
+ public const int EN_PCV = 9;
public const int EN_NONE = 0; //Quality analysis types
public const int EN_CHEM = 1;
@@ -209,6 +213,7 @@ namespace EpanetCSharpLibrary
public const int EN_EFFIC_CURVE = 2; //Efficiency curve
public const int EN_HLOSS_CURVE = 3; //Head loss curve
public const int EN_GENERIC_CURVE = 4; //Generic curve
+ public const int EN_VALVE_CURVE = 5; //Valve position curve
public const int EN_UNCONDITIONAL = 0; //Unconditional object deletion
public const int EN_CONDITIONAL = 1; //Conditional object deletion
diff --git a/include/epanet2.bas b/include/epanet2.bas
index c38003e..bc2ce1e 100644
--- a/include/epanet2.bas
+++ b/include/epanet2.bas
@@ -5,7 +5,7 @@ Attribute VB_Name = "Module1"
'Declarations of functions in the EPANET PROGRAMMERs TOOLKIT
'(EPANET2.DLL)
-'Last updated on 02/01/2020
+'Last updated on 07/28/2022
' These are codes used by the DLL functions
Public Const EN_ELEVATION = 0 ' Node parameters
@@ -63,6 +63,7 @@ Public Const EN_PUMP_ECOST = 21
Public Const EN_PUMP_EPAT = 22
Public Const EN_LINK_INCONTROL = 23
Public Const EN_GPV_CURVE = 24
+Public Const EN_PCV_CURVE= 25
Public Const EN_DURATION = 0 ' Time parameters
Public Const EN_HYDSTEP = 1
@@ -117,6 +118,7 @@ Public Const EN_PBV = 5
Public Const EN_FCV = 6
Public Const EN_TCV = 7
Public Const EN_GPV = 8
+Public Const EN_PCV = 9
Public Const EN_CLOSED = 0 ' Link status types
Public Const EN_OPEN = 1
@@ -209,6 +211,7 @@ Public Const EN_PUMP_CURVE = 1 ' Pump curve
Public Const EN_EFFIC_CURVE = 2 ' Efficiency curve
Public Const EN_HLOSS_CURVE = 3 ' Head loss curve
Public Const EN_GENERIC_CURVE = 4 ' Generic curve
+Public Const EN_VALVE_CURVE = 5 ' Valve position curve
Public Const EN_UNCONDITIONAL = 0 ' Unconditional object deletion
Public Const EN_CONDITIONAL = 1 ' Conditional object deletion
diff --git a/include/epanet2.pas b/include/epanet2.pas
index cbba41e..fba8147 100644
--- a/include/epanet2.pas
+++ b/include/epanet2.pas
@@ -3,7 +3,7 @@ unit epanet2;
{ Declarations of imported procedures from the EPANET PROGRAMMERs TOOLKIT }
{ (EPANET2.DLL) }
-{Last updated on 02/01/2020}
+{Last updated on 07/28/2022}
interface
@@ -69,6 +69,7 @@ const
EN_PUMP_EPAT = 22;
EN_LINK_INCONTROL = 23;
EN_GPV_CURVE = 24;
+ EN_PCV_CURVE = 25;
EN_DURATION = 0; { Time parameters }
EN_HYDSTEP = 1;
@@ -123,6 +124,7 @@ const
EN_FCV = 6;
EN_TCV = 7;
EN_GPV = 8;
+ EN_PCV = 9;
EN_CLOSED = 0; { Link status types }
EN_OPEN = 1;
@@ -214,7 +216,8 @@ const
EN_PUMP_CURVE = 1;
EN_EFFIC_CURVE = 2;
EN_HLOSS_CURVE = 3;
- EN_GENERIC_CURVE = 4;
+ EN_GENERIC_CURVE = 4;
+ EN_VALVE_CURVE = 5;
EN_UNCONDITIONAL = 0; { Deletion action codes }
EN_CONDITIONAL = 1;
diff --git a/include/epanet2.vb b/include/epanet2.vb
index bbd365e..7c283f0 100644
--- a/include/epanet2.vb
+++ b/include/epanet2.vb
@@ -4,7 +4,7 @@
'Declarations of functions in the EPANET PROGRAMMERs TOOLKIT
'(EPANET2.DLL) for use with VB.Net.
-'Last updated on 02/01/2020
+'Last updated on 07/28/2022
Imports System.Runtime.InteropServices
Imports System.Text
@@ -67,6 +67,7 @@ Public Const EN_PUMP_ECOST = 21
Public Const EN_PUMP_EPAT = 22
Public Const EN_LINK_INCONTROL = 23
Public Const EN_GPV_CURVE = 24
+Public Const EN_PCV_CURVE = 25
Public Const EN_DURATION = 0 ' Time parameters
Public Const EN_HYDSTEP = 1
@@ -120,6 +121,7 @@ Public Const EN_PBV = 5
Public Const EN_FCV = 6
Public Const EN_TCV = 7
Public Const EN_GPV = 8
+Public Const EN_PCV = 9
Public Const EN_NONE = 0 ' Quality analysis types
Public Const EN_CHEM = 1
@@ -203,6 +205,7 @@ Public Const EN_PUMP_CURVE = 1 ' Pump curve
Public Const EN_EFFIC_CURVE = 2 ' Efficiency curve
Public Const EN_HLOSS_CURVE = 3 ' Head loss curve
Public Const EN_GENERIC_CURVE = 4 ' Generic curve
+Public Const EN_VALVE_CURVE = 5 ' Valve position curve
Public Const EN_UNCONDITIONAL = 0 ' Unconditional object deletion
Public Const EN_CONDITIONAL = 1 ' Conditional object deletion
diff --git a/include/epanet2_enums.h b/include/epanet2_enums.h
index a000b0c..b3347bc 100644
--- a/include/epanet2_enums.h
+++ b/include/epanet2_enums.h
@@ -9,7 +9,7 @@
Authors: see AUTHORS
Copyright: see AUTHORS
License: see LICENSE
- Last Updated: 02/01/2020
+ Last Updated: 08/13/2022
******************************************************************************
*/
@@ -97,7 +97,8 @@ typedef enum {
EN_PUMP_ECOST = 21, //!< Pump average energy price
EN_PUMP_EPAT = 22, //!< Pump energy price time pattern index
EN_LINK_INCONTROL = 23, //!< Is present in any simple or rule-based control (= 1) or not (= 0)
- EN_GPV_CURVE = 24 //!< GPV head loss v. flow curve index
+ EN_GPV_CURVE = 24, //!< GPV head loss v. flow curve index
+ EN_PCV_CURVE = 25 //!< PCV loss coeff. curve index
} EN_LinkProperty;
/// Time parameters
@@ -191,7 +192,8 @@ typedef enum {
EN_PBV = 5, //!< Pressure breaker valve
EN_FCV = 6, //!< Flow control valve
EN_TCV = 7, //!< Throttle control valve
- EN_GPV = 8 //!< General purpose valve
+ EN_GPV = 8, //!< General purpose valve
+ EN_PCV = 9 //!< Positional control valve
} EN_LinkType;
/// Link status
@@ -397,7 +399,8 @@ typedef enum {
EN_PUMP_CURVE = 1, //!< Pump head v. flow curve
EN_EFFIC_CURVE = 2, //!< Pump efficiency v. flow curve
EN_HLOSS_CURVE = 3, //!< Valve head loss v. flow curve
- EN_GENERIC_CURVE = 4 //!< Generic curve
+ EN_GENERIC_CURVE = 4, //!< Generic curve
+ EN_VALVE_CURVE = 5 //!< Valve loss coeff. v. frac. open
} EN_CurveType;
/// Deletion action codes
diff --git a/src/enumstxt.h b/src/enumstxt.h
index fdb9311..ccc12e8 100755
--- a/src/enumstxt.h
+++ b/src/enumstxt.h
@@ -7,7 +7,7 @@
Authors: see AUTHORS
Copyright: see AUTHORS
License: see LICENSE
- Last Updated: 06/20/2019
+ Last Updated: 08/13/2022
******************************************************************************
*/
@@ -27,7 +27,8 @@ char *LinkTxt[] = {w_CV,
w_PBV,
w_FCV,
w_TCV,
- w_GPV};
+ w_GPV,
+ w_PCV};
char *StatTxt[] = {t_XHEAD,
t_TEMPCLOSED,
diff --git a/src/epanet.c b/src/epanet.c
index 9a3c194..1635b36 100644
--- a/src/epanet.c
+++ b/src/epanet.c
@@ -7,7 +7,7 @@
Authors: see AUTHORS
Copyright: see AUTHORS
License: see LICENSE
- Last Updated: 11/08/2020
+ Last Updated: 08/13/2022
******************************************************************************
*/
@@ -3149,7 +3149,7 @@ int DLLEXPORT EN_addlink(EN_Project p, char *id, int linkType,
if (EN_getlinkindex(p, id, &i) == 0) return 215;
// Check for valid link type
- if (linkType < CVPIPE || linkType > GPV) return 251;
+ if (linkType < CVPIPE || linkType > PCV) return 251;
// Lookup the link's from and to nodes
n1 = hashtable_find(net->NodeHashTable, fromNode);
@@ -3209,6 +3209,7 @@ int DLLEXPORT EN_addlink(EN_Project p, char *id, int linkType,
size = (net->Nvalves + 1) * sizeof(Svalve);
net->Valve = (Svalve *)realloc(net->Valve, size);
net->Valve[net->Nvalves].Link = n;
+ net->Valve[net->Nvalves].Curve = 0;
}
link->Type = linkType;
@@ -3468,7 +3469,7 @@ int DLLEXPORT EN_setlinktype(EN_Project p, int *index, int linkType, int actionC
if (p->hydraul.OpenHflag || p->quality.OpenQflag) return 262;
// Check for valid input parameters
- if (linkType < 0 || linkType > GPV || actionCode < EN_UNCONDITIONAL ||
+ if (linkType < 0 || linkType > PCV || actionCode < EN_UNCONDITIONAL ||
actionCode > EN_CONDITIONAL)
{
return 251;
@@ -3794,6 +3795,13 @@ int DLLEXPORT EN_getlinkvalue(EN_Project p, int index, int property, double *val
}
break;
+ case EN_PCV_CURVE:
+ if (Link[index].Type == PCV)
+ {
+ v = net->Valve[findvalve(&p->network, index)].Curve;
+ }
+ break;
+
case EN_GPV_CURVE:
if (Link[index].Type == GPV)
{
@@ -3916,6 +3924,7 @@ int DLLEXPORT EN_setlinkvalue(EN_Project p, int index, int property, double valu
value /= Ucf[FLOW];
break;
case TCV:
+ case PCV:
break;
case GPV:
return 207; // Cannot modify setting for GPV
@@ -4012,6 +4021,15 @@ int DLLEXPORT EN_setlinkvalue(EN_Project p, int index, int property, double valu
}
break;
+ case EN_PCV_CURVE:
+ if (Link[index].Type == PCV)
+ {
+ curveIndex = ROUND(value);
+ if (curveIndex < 0 || curveIndex > net->Ncurves) return 206;
+ net->Valve[findvalve(&p->network, index)].Curve = curveIndex;
+ }
+ break;
+
case EN_GPV_CURVE:
if (Link[index].Type == GPV)
{
diff --git a/src/funcs.h b/src/funcs.h
index 366e5c7..f3e5ea4 100755
--- a/src/funcs.h
+++ b/src/funcs.h
@@ -7,7 +7,7 @@
Authors: see AUTHORS
Copyright: see AUTHORS
License: see LICENSE
- Last Updated: 02/03/2020
+ Last Updated: 08/13/2022
******************************************************************************
*/
#ifndef FUNCS_H
@@ -166,6 +166,7 @@ void headlosscoeffs(Project *);
void matrixcoeffs(Project *);
void emitterheadloss(Project *, int, double *, double *);
void demandheadloss(Project *, int, double, double, double *, double *);
+double pcvlosscoeff(Project *, int, double);
// ------- QUALITY.C --------------------
diff --git a/src/hydcoeffs.c b/src/hydcoeffs.c
index 83b2dc3..b45f032 100644
--- a/src/hydcoeffs.c
+++ b/src/hydcoeffs.c
@@ -7,7 +7,7 @@
Authors: see AUTHORS
Copyright: see AUTHORS
License: see LICENSE
- Last Updated: 03/30/2022
+ Last Updated: 08/13/2022
******************************************************************************
*/
@@ -36,6 +36,7 @@ const double CBIG = 1.e8;
// Exported functions
//void resistcoeff(Project *, int );
+//double pcvlosscoeff(Project *, int, double);
//void headlosscoeffs(Project *);
//void matrixcoeffs(Project *);
//void emitterheadloss(Project *, int, double *, double *);
@@ -59,6 +60,7 @@ static void valvecoeff(Project *pr, int k);
static void gpvcoeff(Project *pr, int k);
static void pbvcoeff(Project *pr, int k);
static void tcvcoeff(Project *pr, int k);
+static void pcvcoeff(Project *pr, int k);
static void prvcoeff(Project *pr, int k, int n1, int n2);
static void psvcoeff(Project *pr, int k, int n1, int n2);
static void fcvcoeff(Project *pr, int k, int n1, int n2);
@@ -107,6 +109,10 @@ void resistcoeff(Project *pr, int k)
case PUMP:
link->R = CBIG;
break;
+
+ case PCV:
+ link->R = pcvlosscoeff(pr, k, link->Kc);
+ break;
// ... For all other links (e.g. valves) use a small resistance
default:
@@ -116,6 +122,86 @@ void resistcoeff(Project *pr, int k)
}
+double pcvlosscoeff(Project* pr, int k, double s)
+/*
+**--------------------------------------------------------------
+** Input: k = link index
+** s = valve fraction open setting
+** Output: returns a valve loss coefficient
+** Purpose: finds a Positional Control Valve's loss
+** coefficient from its fraction open setting.
+**--------------------------------------------------------------
+*/
+{
+ Network* net = &pr->network;
+
+ int v = findvalve(net, k); // valve index
+ int c = net->Valve[v].Curve; // Kv curve index
+ double d; // valve diameter
+ double kmo; // fully open loss coeff.
+ double km; // partly open loss coeff.
+ double kvr; // Kv / Kvo (Kvo = Kv at fully open)
+ double *x, *y; // points on kvr v. frac. open curve
+ int k1, k2, npts;
+ Scurve *curve;
+
+ // Valve has no setting so return 0
+ if (s == MISSING) return 0.0;
+
+ // Valve is completely open so return its Km value
+ d = net->Link[k].Diam;
+ kmo = net->Link[k].Km;
+ if (s >= 1.0) return kmo;
+
+ // Valve is completely closed so return a large coeff.
+ if (s <= 0.0) return CBIG;
+
+ // Valve has no assigned curve so assume a linear one
+ if (c == 0) kvr = s;
+
+ else
+ {
+ // Valve curve data
+ curve = &net->Curve[c];
+ npts = curve->Npts;
+ x = curve->X; // x = frac. open
+ y = curve->Y; // y = Kv / Kvo
+
+ // s lies below first point of curve
+ if (s < x[0])
+ kvr = s / x[0] * y[0];
+
+ // s lies above last point of curve
+ else if (s > x[npts-1])
+ {
+ k2 = npts - 1;
+ kvr = (s - x[k2]) / (1. - x[k2]) * (1. - y[k2]) + y[k2];
+ }
+
+ // Otherwise interpolate over curve segment that brackets s
+ else
+ {
+ k2 = 0;
+ while (k2 < npts && x[k2] < s) k2++;
+ if (k2 == 0) k2++;
+ else if (k2 == npts) k2--;
+ k1 = k2 - 1;
+ kvr = (y[k2] - y[k1]) / (x[k2] - x[k1]);
+ kvr = y[k1] + kvr * (s - x[k1]);
+ }
+ }
+
+ // kvr can't be > 1 or <= 0
+ kvr = MIN(kvr, 1.0);
+ kvr = MAX(kvr, CSMALL);
+
+ // Convert from Kv ratio to minor loss coeff.
+ km = kmo / (kvr * kvr);
+ km = MIN(km, CBIG);
+ return km;
+}
+
+
void headlosscoeffs(Project *pr)
/*
**--------------------------------------------------------------
@@ -148,6 +234,9 @@ void headlosscoeffs(Project *pr)
case TCV:
tcvcoeff(pr, k);
break;
+ case PCV:
+ pcvcoeff(pr, k);
+ break;
case GPV:
gpvcoeff(pr, k);
break;
@@ -945,6 +1034,36 @@ void tcvcoeff(Project *pr, int k)
}
+void pcvcoeff(Project *pr, int k)
+/*
+**--------------------------------------------------------------
+** Input: k = link index
+** Output: none
+** Purpose: computes P & Y coeffs. for positional control valve
+**--------------------------------------------------------------
+*/
+{
+ double km;
+ Hydraul *hyd = &pr->hydraul;
+ Slink *link = &pr->network.Link[k];
+
+ // Save original loss coeff. for open valve
+ km = link->Km;
+
+ // If valve not fixed OPEN or CLOSED, compute its loss coeff.
+ if (hyd->LinkSetting[k] != MISSING)
+ {
+ link->Km = link->R;
+ }
+
+ // Then apply usual valve formula
+ valvecoeff(pr, k);
+
+ // Restore original loss coeff.
+ link->Km = km;
+}
+
+
void prvcoeff(Project *pr, int k, int n1, int n2)
/*
**--------------------------------------------------------------
diff --git a/src/hydraul.c b/src/hydraul.c
index 36cd67b..fe9999a 100755
--- a/src/hydraul.c
+++ b/src/hydraul.c
@@ -7,7 +7,7 @@
Authors: see AUTHORS
Copyright: see AUTHORS
License: see LICENSE
- Last Updated: 03/19/2022
+ Last Updated: 08/13/2022
******************************************************************************
*/
@@ -450,6 +450,7 @@ void setlinksetting(Project *pr, int index, double value, StatusType *s,
else
{
if (*k == MISSING && *s <= CLOSED) *s = OPEN;
+ if (t == PCV) link->R = pcvlosscoeff(pr, index, link->Kc);
*k = value;
}
}
@@ -605,6 +606,7 @@ int controls(Project *pr)
{
hyd->LinkStatus[k] = s2;
hyd->LinkSetting[k] = k2;
+ if (link->Type == PCV) link->R = pcvlosscoeff(pr, k, k2);
if (pr->report.Statflag) writecontrolaction(pr,k,i);
setsum++;
}
diff --git a/src/inpfile.c b/src/inpfile.c
index bc5d4e6..c1fcc48 100644
--- a/src/inpfile.c
+++ b/src/inpfile.c
@@ -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: 10/29/2019
+Last Updated: 08/13/2022
******************************************************************************
*/
@@ -312,6 +312,11 @@ int saveinpfile(Project *pr, const char *fname)
{
sprintf(s1, "%-31s %12.4f", net->Curve[j].ID, km);
}
+ // For PCV add loss curve if present
+ else if (link->Type == PCV && (j = net->Valve[i].Curve) > 0)
+ {
+ sprintf(s1, "%12.4f %12.4f %-31s", kc, km, net->Curve[j].ID);
+ }
else sprintf(s1, "%12.4f %12.4f", kc, km);
fprintf(f, "\n%s %s", s, s1);
if (link->Comment) fprintf(f, " ;%s", link->Comment);
diff --git a/src/input3.c b/src/input3.c
index c32eaf7..9534b35 100644
--- a/src/input3.c
+++ b/src/input3.c
@@ -7,7 +7,7 @@ Description: parses network data from a line of an EPANET input file
Authors: see AUTHORS
Copyright: see AUTHORS
License: see LICENSE
-Last Updated: 03/20/2022
+Last Updated: 08/13/2022
******************************************************************************
*/
@@ -471,7 +471,7 @@ int valvedata(Project *pr)
** Purpose: processes valve data
** Format:
** [VALVE]
-** id node1 node2 diam type setting (lcoeff)
+** id node1 node2 diam type setting (lcoeff lcurve)
**--------------------------------------------------------------
*/
{
@@ -488,7 +488,8 @@ int valvedata(Project *pr)
setting, // Valve setting
lcoeff = 0.0; // Minor loss coeff.
Slink *link;
- int err = 0;
+ int err = 0,
+ losscurve = 0; // Loss coeff. curve
// Add new valve to data base
n = parser->Ntokens;
@@ -521,6 +522,8 @@ int valvedata(Project *pr)
type = TCV;
else if (match(parser->Tok[4], w_GPV))
type = GPV;
+ else if (match(parser->Tok[4], w_PCV))
+ type = PCV;
else
return setError(parser, 4, 213);
@@ -538,6 +541,16 @@ int valvedata(Project *pr)
}
else if (!getfloat(parser->Tok[5], &setting)) return setError(parser, 5, 202);
if (n >= 7 && !getfloat(parser->Tok[6], &lcoeff)) return setError(parser, 6, 202);
+
+ // Find loss coeff. curve for PCV
+ if (type == PCV && n >= 8)
+ {
+ c = findcurve(net, parser->Tok[7]);
+ if (c == 0) return setError(parser, 7, 206);
+ losscurve = c;
+ net->Curve[c].Type = VALVE_CURVE;
+ if (setting > 1.0) setting = 1.0;
+ }
// Check for illegal connections
if (valvecheck(pr, net->Nlinks, type, j1, j2))
@@ -563,6 +576,7 @@ int valvedata(Project *pr)
link->ResultIndex = 0;
link->Comment = xstrcpy(&link->Comment, parser->Comment, MAXMSG);
net->Valve[net->Nvalves].Link = net->Nlinks;
+ net->Valve[net->Nvalves].Curve = losscurve;
return 0;
}
diff --git a/src/output.c b/src/output.c
index 1e9a6ab..51685e2 100644
--- a/src/output.c
+++ b/src/output.c
@@ -7,7 +7,7 @@ Description: binary file read/write routines
Authors: see AUTHORS
Copyright: see AUTHORS
License: see LICENSE
-Last Updated: 05/13/2019
+Last Updated: 08/13/2022
******************************************************************************
*/
@@ -567,6 +567,7 @@ int linkoutput(Project *pr, int j, REAL4 *x, double ucf)
case FCV:
x[i] = (REAL4)(setting * pr->Ucf[FLOW]); break;
case TCV:
+ case PCV:
x[i] = (REAL4)setting; break;
default: x[i] = 0.0f;
}
diff --git a/src/project.c b/src/project.c
index b470ec3..4b8a971 100644
--- a/src/project.c
+++ b/src/project.c
@@ -7,7 +7,7 @@
Authors: see AUTHORS
Copyright: see AUTHORS
License: see LICENSE
- Last Updated: 02/03/2020
+ Last Updated: 08/13/2022
******************************************************************************
*/
@@ -860,7 +860,7 @@ int findtank(Network *network, int index)
/*----------------------------------------------------------------
** Input: index = node index
** Output: none
-** Returns: index of tank with given node id, or NOTFOUND if tank not found
+** Returns: index of tank with given node index, or NOTFOUND if tank not found
** Purpose: for use in the deletenode function
**----------------------------------------------------------------
*/
@@ -877,7 +877,7 @@ int findpump(Network *network, int index)
/*----------------------------------------------------------------
** Input: index = link ID
** Output: none
-** Returns: index of pump with given link id, or NOTFOUND if pump not found
+** Returns: index of pump with given link index, or NOTFOUND if pump not found
** Purpose: for use in the deletelink function
**----------------------------------------------------------------
*/
@@ -894,7 +894,7 @@ int findvalve(Network *network, int index)
/*----------------------------------------------------------------
** Input: index = link ID
** Output: none
-** Returns: index of valve with given link id, or NOTFOUND if valve not found
+** Returns: index of valve with given link index, or NOTFOUND if valve not found
** Purpose: for use in the deletelink function
**----------------------------------------------------------------
*/
@@ -1010,7 +1010,7 @@ void adjustcurves(Network *network, int index)
**----------------------------------------------------------------
*/
{
- int j, k, setting;
+ int j, k, curve;
// Adjust tank volume curves
for (j = 1; j <= network->Ntanks; j++)
@@ -1025,15 +1025,25 @@ void adjustcurves(Network *network, int index)
adjustcurve(&network->Pump[j].Ecurve, index);
}
- // Adjust GPV curves
+ // Adjust PCV & GPV curves
for (j = 1; j <= network->Nvalves; j++)
{
k = network->Valve[j].Link;
+ if (network->Link[k].Type == PCV)
+ {
+ if ((curve = network->Valve[j].Curve) > 0)
+ {
+ adjustcurve(&curve, index);
+ network->Valve[j].Curve = curve;
+ if (curve == 0)
+ network->Link[k].Kc = 0.0;
+ }
+ }
if (network->Link[k].Type == GPV)
{
- setting = INT(network->Link[k].Kc);
- adjustcurve(&setting, index);
- network->Link[k].Kc = setting;
+ curve = INT(network->Link[k].Kc);
+ adjustcurve(&curve, index);
+ network->Link[k].Kc = curve;
}
}
}
diff --git a/src/text.h b/src/text.h
index a4ac84b..72fef68 100755
--- a/src/text.h
+++ b/src/text.h
@@ -7,7 +7,7 @@
Authors: see AUTHORS
Copyright: see AUTHORS
License: see LICENSE
- Last Updated: 07/15/2019
+ Last Updated: 08/13/2022
******************************************************************************
*/
@@ -40,6 +40,7 @@
#define w_FCV "FCV"
#define w_TCV "TCV"
#define w_GPV "GPV"
+#define w_PCV "PCV"
#define w_OPEN "OPEN"
#define w_CLOSED "CLOSED"
diff --git a/src/types.h b/src/types.h
index b0e91b5..281e012 100755
--- a/src/types.h
+++ b/src/types.h
@@ -7,7 +7,7 @@
Authors: see AUTHORS
Copyright: see AUTHORS
License: see LICENSE
- Last Updated: 07/11/2020
+ Last Updated: 08/13/2022
******************************************************************************
*/
@@ -145,7 +145,8 @@ typedef enum {
PBV, // pressure breaker valve
FCV, // flow control valve
TCV, // throttle control valve
- GPV // general purpose valve
+ GPV, // general purpose valve
+ PCV // positional control valve
} LinkType;
typedef enum {
@@ -166,7 +167,8 @@ typedef enum {
PUMP_CURVE, // pump curve
EFFIC_CURVE, // efficiency curve
HLOSS_CURVE, // head loss curve
- GENERIC_CURVE // generic curve
+ GENERIC_CURVE, // generic curve
+ VALVE_CURVE // positional valve loss curve
} CurveType;
typedef enum {
@@ -455,6 +457,7 @@ typedef struct // Pump Object
typedef struct // Valve Object
{
int Link; // link index of valve
+ int Curve; // positional loss coeff. curve
} Svalve;
typedef struct // Control Statement
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 995d01f..946e9f6 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -39,6 +39,7 @@ set(toolkit_test_srcs
test_control.cpp
test_overflow.cpp
test_pda.cpp
+ test_valve.cpp
)
add_executable(test_toolkit ${toolkit_test_srcs})
diff --git a/tests/test_valve.cpp b/tests/test_valve.cpp
new file mode 100644
index 0000000..a4c3c89
--- /dev/null
+++ b/tests/test_valve.cpp
@@ -0,0 +1,73 @@
+/*
+ ******************************************************************************
+ Project: OWA EPANET
+ Version: 2.2
+ Module: test_valve.cpp
+ Description: Tests EPANET toolkit api functions
+ Authors: see AUTHORS
+ Copyright: see AUTHORS
+ License: see LICENSE
+ Last Updated: 07/28/2022
+ ******************************************************************************
+*/
+
+/*
+ Tests PCV valve with position curve
+*/
+
+#include
+
+#include "test_toolkit.hpp"
+
+BOOST_AUTO_TEST_SUITE (test_valve)
+
+BOOST_FIXTURE_TEST_CASE(test_PCV_valve, FixtureOpenClose)
+
+{
+ int npts = 5;
+ double x[] = { 0.0, 0.25, 0.5, 0.75, 1. };
+ double y[] = {0.0, 0.089, 0.184, 0.406, 1.0};
+ double v;
+ int linkIndex, curveIndex;
+
+ // Make steady state run
+ error = EN_settimeparam(ph, EN_DURATION, 0);
+ BOOST_REQUIRE(error == 0);
+
+ // Convert pipe 22 to a PCV
+ error = EN_getlinkindex(ph, (char*)"22", &linkIndex);
+ BOOST_REQUIRE(error == 0);
+ error = EN_setlinktype(ph, &linkIndex, EN_PCV, EN_UNCONDITIONAL);
+ BOOST_REQUIRE(error == 0);
+ error = EN_setlinkvalue(ph, linkIndex, EN_DIAMETER, 12);
+ BOOST_REQUIRE(error == 0);
+ error = EN_setlinkvalue(ph, linkIndex, EN_MINORLOSS, 0.19);
+
+ // Create the PCV's position-loss curve
+ error = EN_addcurve(ph, (char*)"ValveCurve");
+ BOOST_REQUIRE(error == 0);
+ error = EN_getcurveindex(ph, (char*)"ValveCurve", &curveIndex);
+ BOOST_REQUIRE(error == 0);
+ error = EN_setcurve(ph, curveIndex, x, y, npts);
+ BOOST_REQUIRE(error == 0);
+
+ // Assign curve & initial setting to PCV
+ error = EN_setlinkvalue(ph, linkIndex, EN_PCV_CURVE, curveIndex);
+ BOOST_REQUIRE(error == 0);
+ error = EN_setlinkvalue(ph, linkIndex, EN_INITSETTING, 0.35);
+ BOOST_REQUIRE(error == 0);
+
+ // Solve for hydraulics
+ error = EN_solveH(ph);
+ BOOST_REQUIRE(error == 0);
+
+ // The PCV interpolated relative flow coeff. at 0.35 open is 0.127.
+ // This translates to a minor loss coeff. of 0.19 / 0.127^2 = 11.78.
+ // If the PCV were replaced with a TCV at that setting the resulting
+ // head loss would be 0.0255 ft which should equal the PCV result.
+ error = EN_getlinkvalue(ph, linkIndex, EN_HEADLOSS, &v);
+ BOOST_REQUIRE(error == 0);
+ BOOST_REQUIRE(abs(v - 0.0255) < 0.001);
+}
+
+BOOST_AUTO_TEST_SUITE_END()