Merge pull request #732 from OpenWaterAnalytics/dev-controls

Allow simple controls to set valves OPEN/CLOSED
This commit is contained in:
Lew Rossman
2023-06-08 10:37:45 -04:00
committed by GitHub
11 changed files with 156 additions and 139 deletions

View File

@@ -42,3 +42,4 @@ This document describes the changes and updates that have been made in version 2
- A simple control with more than 9 input tokens would set the incorrect hour, this has been fixed. - A simple control with more than 9 input tokens would set the incorrect hour, this has been fixed.
- When reading an EPANET inp file, errors in node and link vertex coordinates are ignored. - When reading an EPANET inp file, errors in node and link vertex coordinates are ignored.
- Non-zero demands are now not included in `[DEMANDS]` when running `EN_saveinpfile`. - Non-zero demands are now not included in `[DEMANDS]` when running `EN_saveinpfile`.
- `EN_SET_CLOSED` and `EN_SET_OPEN` constants were added that can be used with `EN_setcontrol` to fix the status of pipes and valves to completely closed or completely open.

View File

@@ -3,7 +3,7 @@ using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
//epanet2.cs[By Oscar Vegas] //epanet2.cs[By Oscar Vegas]
//Last updated on 17/09/2020 //Last updated on 05/13/2023
//Declarations of functions in the EPANET PROGRAMMERs TOOLKIT //Declarations of functions in the EPANET PROGRAMMERs TOOLKIT
//(EPANET2.DLL) for use with C# //(EPANET2.DLL) for use with C#
@@ -264,6 +264,8 @@ namespace EpanetCSharpLibrary
public const int EN_R_IS_ACTIVE = 3; public const int EN_R_IS_ACTIVE = 3;
public const double EN_MISSING = -1.0E10; public const double EN_MISSING = -1.0E10;
public const double EN_SET_CLOSED = -1.0E10
public const double EN_SET_OPEN = 1.0E10
#region Epanet Imports #region Epanet Imports

View File

@@ -5,7 +5,7 @@ Attribute VB_Name = "Module1"
'Declarations of functions in the EPANET PROGRAMMERs TOOLKIT 'Declarations of functions in the EPANET PROGRAMMERs TOOLKIT
'(EPANET2.DLL) '(EPANET2.DLL)
'Last updated on 07/28/2022 'Last updated on 05/13/2023
' These are codes used by the DLL functions ' These are codes used by the DLL functions
Public Const EN_ELEVATION = 0 ' Node parameters Public Const EN_ELEVATION = 0 ' Node parameters
@@ -268,6 +268,8 @@ Public Const EN_STEP_TANKEVENT = 3
Public Const EN_STEP_CONTROLEVENT = 4 Public Const EN_STEP_CONTROLEVENT = 4
Public Const EN_MISSING As Double = -1.0E10 Public Const EN_MISSING As Double = -1.0E10
Public Const EN_SET_CLOSED As Double = -1.0E10
Public Const EN_SET_OPEN As Double = 1.0E10
'These are the external functions that comprise the DLL 'These are the external functions that comprise the DLL

View File

@@ -3,7 +3,7 @@ unit epanet2;
{ Declarations of imported procedures from the EPANET PROGRAMMERs TOOLKIT } { Declarations of imported procedures from the EPANET PROGRAMMERs TOOLKIT }
{ (EPANET2.DLL) } { (EPANET2.DLL) }
{Last updated on 07/28/2022} {Last updated on 05/13/2023}
interface interface
@@ -13,6 +13,8 @@ const
EN_MAXID = 31; { Max. # characters in ID name } EN_MAXID = 31; { Max. # characters in ID name }
EN_MAXMSG = 255; { Max. # characters in strings } EN_MAXMSG = 255; { Max. # characters in strings }
EN_MISSING = -1.E10; EN_MISSING = -1.E10;
EN_SET_CLOSED = -1.E10;
EN_SET_OPEN = 1.E10;
EN_ELEVATION = 0; { Node parameters } EN_ELEVATION = 0; { Node parameters }
EN_BASEDEMAND = 1; EN_BASEDEMAND = 1;

View File

@@ -4,7 +4,7 @@
'Declarations of functions in the EPANET PROGRAMMERs TOOLKIT 'Declarations of functions in the EPANET PROGRAMMERs TOOLKIT
'(EPANET2.DLL) for use with VB.Net. '(EPANET2.DLL) for use with VB.Net.
'Last updated on 07/28/2022 'Last updated on 05/13/2023
Imports System.Runtime.InteropServices Imports System.Runtime.InteropServices
Imports System.Text Imports System.Text
@@ -256,6 +256,8 @@ Public Const EN_R_IS_CLOSED = 2
Public Const EN_R_IS_ACTIVE = 3 Public Const EN_R_IS_ACTIVE = 3
Public Const EN_MISSING As Double = -1.0E10 Public Const EN_MISSING As Double = -1.0E10
Public Const EN_SET_CLOSED As Double = -1.0E10
Public Const EN_SET_OPEN As Double = 1.0E10
'These are the external functions that comprise the DLL 'These are the external functions that comprise the DLL

View File

@@ -9,7 +9,7 @@
Authors: see AUTHORS Authors: see AUTHORS
Copyright: see AUTHORS Copyright: see AUTHORS
License: see LICENSE License: see LICENSE
Last Updated: 02/05/2023 Last Updated: 04/28/2023
****************************************************************************** ******************************************************************************
*/ */
@@ -499,5 +499,7 @@ typedef enum {
} EN_RuleStatus; } EN_RuleStatus;
#define EN_MISSING -1.E10 //!< Missing value indicator #define EN_MISSING -1.E10 //!< Missing value indicator
#define EN_SET_CLOSED -1.E10 //!< Link set closed indicator
#define EN_SET_OPEN 1.E10 //!< Link set open indicator
#endif //EPANET2_ENUMS_H #endif //EPANET2_ENUMS_H

View File

@@ -7,7 +7,7 @@
Authors: see AUTHORS Authors: see AUTHORS
Copyright: see AUTHORS Copyright: see AUTHORS
License: see LICENSE License: see LICENSE
Last Updated: 02/05/2023 Last Updated: 04/29/2023
****************************************************************************** ******************************************************************************
*/ */
@@ -5070,15 +5070,9 @@ int DLLEXPORT EN_addcontrol(EN_Project p, int type, int linkIndex, double settin
*/ */
{ {
Network *net = &p->network; Network *net = &p->network;
Parser *parser = &p->parser;
char status = ACTIVE;
int n;
long t = 0;
double s = setting, lvl = level;
double *Ucf = p->Ucf;
Scontrol *control;
int err, n;
Scontrol ctrl;
// Check that project exists // Check that project exists
if (!p->Openflag) return 102; if (!p->Openflag) return 102;
@@ -5086,68 +5080,20 @@ int DLLEXPORT EN_addcontrol(EN_Project p, int type, int linkIndex, double settin
// Check that controlled link exists // Check that controlled link exists
if (linkIndex <= 0 || linkIndex > net->Nlinks) return 204; if (linkIndex <= 0 || linkIndex > net->Nlinks) return 204;
// Cannot control check valve // Insert control properties into a temporary struct
if (net->Link[linkIndex].Type == CVPIPE) return 207; err = setcontrol(p, type, linkIndex, setting, nodeIndex, level, &ctrl);
if (err > 0) return err;
// Check for valid parameters
if (type < 0 || type > EN_TIMEOFDAY) return 251;
if (type == EN_LOWLEVEL || type == EN_HILEVEL)
{
if (nodeIndex < 1 || nodeIndex > net->Nnodes) return 203;
}
else nodeIndex = 0;
if (s < 0.0 || lvl < 0.0) return 202;
// Adjust units of control parameters
switch (net->Link[linkIndex].Type)
{
case PRV:
case PSV:
case PBV:
s /= Ucf[PRESSURE];
break;
case FCV:
s /= Ucf[FLOW];
break;
case GPV:
if (s == 0.0) status = CLOSED;
else if (s == 1.0) status = OPEN;
else return 202;
s = net->Link[linkIndex].Kc;
break;
case PIPE:
case PUMP:
status = OPEN;
if (s == 0.0) status = CLOSED;
default:
break;
}
if (type == LOWLEVEL || type == HILEVEL)
{
if (nodeIndex > net->Njuncs) lvl = net->Node[nodeIndex].El + level / Ucf[ELEV];
else lvl = net->Node[nodeIndex].El + level / Ucf[PRESSURE];
}
if (type == TIMER) t = (long)ROUND(lvl);
if (type == TIMEOFDAY) t = (long)ROUND(lvl) % SECperDAY;
// Expand project's array of controls // Expand project's array of controls
n = net->Ncontrols + 1; n = net->Ncontrols + 1;
net->Control = (Scontrol *)realloc(net->Control, (n + 1) * sizeof(Scontrol)); net->Control = (Scontrol *)realloc(net->Control, (n + 1) * sizeof(Scontrol));
// Set properties of the new control // Set properties of the new control
control = &net->Control[n]; net->Control[n] = ctrl;
control->Type = (char)type;
control->Link = linkIndex;
control->Node = nodeIndex;
control->Status = status;
control->Setting = s;
control->Grade = lvl;
control->Time = t;
// Update number of controls // Update number of controls
net->Ncontrols = n; net->Ncontrols = n;
parser->MaxControls = n; p->parser.MaxControls = n;
// Replace the control's index // Replace the control's index
*index = n; *index = n;
@@ -5230,8 +5176,8 @@ int DLLEXPORT EN_getcontrol(EN_Project p, int index, int *type, int *linkIndex,
break; break;
} }
} }
else if (control->Status == OPEN) s = 1.0; else if (control->Status == OPEN) s = SET_OPEN;
else s = 0.0; else s = SET_CLOSED;
// Retrieve level value for a node level control // Retrieve level value for a node level control
*nodeIndex = control->Node; *nodeIndex = control->Node;
@@ -5253,8 +5199,8 @@ int DLLEXPORT EN_getcontrol(EN_Project p, int index, int *type, int *linkIndex,
{ {
lvl = (double)control->Time; lvl = (double)control->Time;
} }
*setting = (double)s; *setting = s;
*level = (double)lvl; *level = lvl;
return 0; return 0;
} }
@@ -5276,81 +5222,27 @@ int DLLEXPORT EN_setcontrol(EN_Project p, int index, int type, int linkIndex,
{ {
Network *net = &p->network; Network *net = &p->network;
char status = ACTIVE; int err;
long t = 0; Scontrol ctrl;
double s = setting, lvl = level;
double *Ucf = p->Ucf;
Slink *link;
Scontrol *control;
// Check that project exists // Check that project exists
if (!p->Openflag) return 102; if (!p->Openflag) return 102;
// Check that control exists // Check that control exists
if (index <= 0 || index > net->Ncontrols) return 241; if (index <= 0 || index > net->Ncontrols) return 241;
control = &net->Control[index];
// Check that controlled link exists (0 index de-activates the control) // Check that controlled link exists (0 index de-activates the control)
if (linkIndex == 0) if (linkIndex == 0)
{ {
control->Link = 0; net->Control[index].Link = 0;
return 0; return 0;
} }
if (linkIndex < 0 || linkIndex > net->Nlinks) return 204; if (linkIndex < 0 || linkIndex > net->Nlinks) return 204;
// Cannot control check valve // Assign new set of properties to control
if (net->Link[linkIndex].Type == CVPIPE) return 207; err = setcontrol(p, type, linkIndex, setting, nodeIndex, level, &ctrl);
if (err > 0) return err;
// Check for valid control properties net->Control[index] = ctrl;
if (type < 0 || type > EN_TIMEOFDAY) return 251;
if (type == EN_LOWLEVEL || type == EN_HILEVEL)
{
if (nodeIndex < 1 || nodeIndex > net->Nnodes) return 203;
}
else nodeIndex = 0;
if (s < 0.0 || lvl < 0.0) return 202;
// Adjust units of control's properties
link = &net->Link[linkIndex];
switch (link->Type)
{
case PRV:
case PSV:
case PBV:
s /= Ucf[PRESSURE];
break;
case FCV:
s /= Ucf[FLOW];
break;
case GPV:
if (s == 0.0) status = CLOSED;
else if (s == 1.0) status = OPEN;
else return 202;
s = link->Kc;
break;
case PIPE:
case PUMP:
status = OPEN;
if (s == 0.0) status = CLOSED;
default:
break;
}
if (type == LOWLEVEL || type == HILEVEL)
{
if (nodeIndex > net->Njuncs) lvl = net->Node[nodeIndex].El + level / Ucf[ELEV];
else lvl = net->Node[nodeIndex].El + level / Ucf[PRESSURE];
}
if (type == TIMER) t = (long)ROUND(lvl);
if (type == TIMEOFDAY) t = (long)ROUND(lvl) % SECperDAY;
/* Reset control's parameters */
control->Type = (char)type;
control->Link = linkIndex;
control->Node = nodeIndex;
control->Status = status;
control->Setting = s;
control->Grade = lvl;
control->Time = t;
return 0; return 0;
} }

View File

@@ -7,7 +7,7 @@
Authors: see AUTHORS Authors: see AUTHORS
Copyright: see AUTHORS Copyright: see AUTHORS
License: see LICENSE License: see LICENSE
Last Updated: 08/13/2022 Last Updated: 04/29/2023
****************************************************************************** ******************************************************************************
*/ */
#ifndef FUNCS_H #ifndef FUNCS_H
@@ -50,6 +50,7 @@ void adjustpatterns(Network *, int);
void adjustcurves(Network *, int); void adjustcurves(Network *, int);
int adjustpumpparams(Project *, int); int adjustpumpparams(Project *, int);
int resizecurve(Scurve *, int); int resizecurve(Scurve *, int);
int setcontrol(Project *, int, int, double, int, double, Scontrol *);
int getcomment(Network *, int, int, char *); int getcomment(Network *, int, int, char *);
int setcomment(Network *, int, int, const char *); int setcomment(Network *, int, int, const char *);

View File

@@ -7,7 +7,7 @@ Description: saves network data to an EPANET formatted text file
Authors: see AUTHORS Authors: see AUTHORS
Copyright: see AUTHORS Copyright: see AUTHORS
License: see LICENSE License: see LICENSE
Last Updated: 02/05/2023 Last Updated: 04/30/2023
****************************************************************************** ******************************************************************************
*/ */
@@ -432,7 +432,12 @@ int saveinpfile(Project *pr, const char *fname)
link = &net->Link[j]; link = &net->Link[j];
// Get text of control's link status/setting // Get text of control's link status/setting
if (control->Setting == MISSING || link->Type == GPV) if (control->Setting == MISSING || link->Type == GPV || link->Type == PIPE)
{
sprintf(s, " LINK %s %s ", link->ID, StatTxt[control->Status]);
}
else if (link->Type == PUMP &&
(control->Setting == 0.0 || control->Setting == 1.0))
{ {
sprintf(s, " LINK %s %s ", link->ID, StatTxt[control->Status]); sprintf(s, " LINK %s %s ", link->ID, StatTxt[control->Status]);
} }

View File

@@ -7,7 +7,7 @@
Authors: see AUTHORS Authors: see AUTHORS
Copyright: see AUTHORS Copyright: see AUTHORS
License: see LICENSE License: see LICENSE
Last Updated: 08/13/2022 Last Updated: 04/29/2023
****************************************************************************** ******************************************************************************
*/ */
@@ -1121,6 +1121,111 @@ int resizecurve(Scurve *curve, int size)
return 0; return 0;
} }
int setcontrol(EN_Project p, int type, int linkIndex, double setting,
int nodeIndex, double level, Scontrol *control)
/*----------------------------------------------------------------
** Input: type = type of control (see EN_ControlType)
** linkIndex = index of link being controlled
** setting = link control setting (e.g., pump speed)
** nodeIndex = index of node controlling a link (for level controls)
** level = control activation level (pressure for junction nodes,
** water level for tank nodes or time value for time-based
** control)
** Output: control = control struct whose properties are being set
** Returns: error code
** Purpose: assigns properties to a control struct.
**----------------------------------------------------------------
*/
{
Network *net = &p->network;
Parser *parser = &p->parser;
long t = 0;
double lvl = 0.0, s = MISSING;
double *Ucf = p->Ucf;
LinkType linktype;
StatusType status = ACTIVE;
// Cannot control check valve
linktype = net->Link[linkIndex].Type;
if (linktype == CVPIPE) return 207;
// Check for valid control type and node index
if (type < 0 || type > TIMEOFDAY) return 251;
if (type == LOWLEVEL || type == HILEVEL)
{
if (nodeIndex < 1 || nodeIndex > net->Nnodes) return 203;
}
else nodeIndex = 0;
// Check if control setting is a status level
if (setting == SET_OPEN)
{
status = OPEN;
if (linktype == PUMP) s = 1.0;
if (linktype == GPV) s = net->Link[linkIndex].Kc;
}
else if (setting == SET_CLOSED)
{
status = CLOSED;
if (linktype == PUMP) s = 0.0;
if (linktype == GPV) s = net->Link[linkIndex].Kc;
}
// Convert units of control setting
else
{
s = setting;
switch (linktype)
{
case PIPE:
case PUMP:
if (s < 0.0) return 202;
else if (s == 0.0) status = CLOSED;
else status = OPEN;
break;
case PRV:
case PSV:
case PBV:
s /= Ucf[PRESSURE];
break;
case FCV:
s /= Ucf[FLOW];
break;
case GPV:
if (s == 0.0) status = CLOSED;
else if (s == 1.0) status = OPEN;
else return 202;
s = net->Link[linkIndex].Kc;
break;
}
}
// Determine if control level is a pressure, tank level or time value
if (type == LOWLEVEL || type == HILEVEL)
{
if (nodeIndex > net->Njuncs) lvl = net->Node[nodeIndex].El + level / Ucf[ELEV];
else lvl = net->Node[nodeIndex].El + level / Ucf[PRESSURE];
}
else if (type == TIMER || type == TIMEOFDAY)
{
t = (long)level;
if (t < 0) return 202;
}
// Assign values to control struct
control->Link = linkIndex;
control->Node = nodeIndex;
control->Type = type;
control->Status = status;
control->Setting = s;
control->Time = t;
control->Grade = lvl;
return 0;
}
int getcomment(Network *network, int object, int index, char *comment) int getcomment(Network *network, int object, int index, char *comment)
//---------------------------------------------------------------- //----------------------------------------------------------------
// Input: object = a type of network object // Input: object = a type of network object

View File

@@ -7,7 +7,7 @@
Authors: see AUTHORS Authors: see AUTHORS
Copyright: see AUTHORS Copyright: see AUTHORS
License: see LICENSE License: see LICENSE
Last Updated: 02/05/2023 Last Updated: 04/29/2023
****************************************************************************** ******************************************************************************
*/ */
@@ -48,6 +48,9 @@ typedef int INT4;
#define BIG 1.E10 #define BIG 1.E10
#define TINY 1.E-6 #define TINY 1.E-6
#define MISSING -1.E10 // Missing value indicator #define MISSING -1.E10 // Missing value indicator
#define SET_CLOSED -1.E10 // Link set closed indicator
#define SET_OPEN 1.E10 // Link set open indicator
#define DIFFUS 1.3E-8 // Diffusivity of chlorine #define DIFFUS 1.3E-8 // Diffusivity of chlorine
// @ 20 deg C (sq ft/sec) // @ 20 deg C (sq ft/sec)
#define VISCOS 1.1E-5 // Kinematic viscosity of water #define VISCOS 1.1E-5 // Kinematic viscosity of water