From a9079b023b9f256055195363a8d9bbcb3bfff964 Mon Sep 17 00:00:00 2001 From: Lew Rossman Date: Sat, 13 May 2023 12:29:35 -0400 Subject: [PATCH 1/2] Allow simple controls to set valves OPEN/CLOSED --- include/epanet.cs | 4 +- include/epanet2.bas | 4 +- include/epanet2.pas | 4 +- include/epanet2.vb | 4 +- include/epanet2_enums.h | 6 +- src/epanet.c | 148 ++++++---------------------------------- src/funcs.h | 3 +- src/inpfile.c | 9 ++- src/project.c | 107 ++++++++++++++++++++++++++++- src/types.h | 5 +- 10 files changed, 155 insertions(+), 139 deletions(-) diff --git a/include/epanet.cs b/include/epanet.cs index c9cb2d4..b0b27f9 100644 --- a/include/epanet.cs +++ b/include/epanet.cs @@ -3,7 +3,7 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; //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 //(EPANET2.DLL) for use with C# @@ -264,6 +264,8 @@ namespace EpanetCSharpLibrary public const int EN_R_IS_ACTIVE = 3; 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 diff --git a/include/epanet2.bas b/include/epanet2.bas index 63e5db3..e95cbd0 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 07/28/2022 +'Last updated on 05/13/2023 ' These are codes used by the DLL functions 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_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 diff --git a/include/epanet2.pas b/include/epanet2.pas index af600b8..8edc917 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 07/28/2022} +{Last updated on 05/13/2023} interface @@ -13,6 +13,8 @@ const EN_MAXID = 31; { Max. # characters in ID name } EN_MAXMSG = 255; { Max. # characters in strings } EN_MISSING = -1.E10; + EN_SET_CLOSED = -1.E10; + EN_SET_OPEN = 1.E10; EN_ELEVATION = 0; { Node parameters } EN_BASEDEMAND = 1; diff --git a/include/epanet2.vb b/include/epanet2.vb index daf5703..c7dd94e 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 07/28/2022 +'Last updated on 05/13/2023 Imports System.Runtime.InteropServices Imports System.Text @@ -256,6 +256,8 @@ Public Const EN_R_IS_CLOSED = 2 Public Const EN_R_IS_ACTIVE = 3 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 diff --git a/include/epanet2_enums.h b/include/epanet2_enums.h index 79039ef..0c03bdd 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/05/2023 + Last Updated: 04/28/2023 ****************************************************************************** */ @@ -498,6 +498,8 @@ typedef enum { EN_R_IS_ACTIVE = 3 //!< Control valve is active } 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 diff --git a/src/epanet.c b/src/epanet.c index db47e2f..3a4fe2f 100644 --- a/src/epanet.c +++ b/src/epanet.c @@ -7,7 +7,7 @@ Authors: see AUTHORS Copyright: see AUTHORS License: see LICENSE - Last Updated: 02/05/2023 + Last Updated: 04/29/2023 ****************************************************************************** */ @@ -5069,84 +5069,30 @@ int DLLEXPORT EN_addcontrol(EN_Project p, int type, int linkIndex, double settin */ { 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 if (!p->Openflag) return 102; // Check that controlled link exists if (linkIndex <= 0 || linkIndex > net->Nlinks) return 204; - - // Cannot control check valve - if (net->Link[linkIndex].Type == CVPIPE) return 207; - - // 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; + + // Insert control properties into a temporary struct + err = setcontrol(p, type, linkIndex, setting, nodeIndex, level, &ctrl); + if (err > 0) return err; // Expand project's array of controls n = net->Ncontrols + 1; net->Control = (Scontrol *)realloc(net->Control, (n + 1) * sizeof(Scontrol)); // Set properties of the new control - control = &net->Control[n]; - control->Type = (char)type; - control->Link = linkIndex; - control->Node = nodeIndex; - control->Status = status; - control->Setting = s; - control->Grade = lvl; - control->Time = t; + net->Control[n] = ctrl; // Update number of controls net->Ncontrols = n; - parser->MaxControls = n; + p->parser.MaxControls = n; // Replace the control's index *index = n; @@ -5229,8 +5175,8 @@ int DLLEXPORT EN_getcontrol(EN_Project p, int index, int *type, int *linkIndex, break; } } - else if (control->Status == OPEN) s = 1.0; - else s = 0.0; + else if (control->Status == OPEN) s = SET_OPEN; + else s = SET_CLOSED; // Retrieve level value for a node level control *nodeIndex = control->Node; @@ -5252,8 +5198,8 @@ int DLLEXPORT EN_getcontrol(EN_Project p, int index, int *type, int *linkIndex, { lvl = (double)control->Time; } - *setting = (double)s; - *level = (double)lvl; + *setting = s; + *level = lvl; return 0; } @@ -5275,81 +5221,27 @@ int DLLEXPORT EN_setcontrol(EN_Project p, int index, int type, int linkIndex, { Network *net = &p->network; - char status = ACTIVE; - long t = 0; - double s = setting, lvl = level; - double *Ucf = p->Ucf; - Slink *link; - Scontrol *control; + int err; + Scontrol ctrl; // Check that project exists if (!p->Openflag) return 102; // Check that control exists if (index <= 0 || index > net->Ncontrols) return 241; - control = &net->Control[index]; // Check that controlled link exists (0 index de-activates the control) if (linkIndex == 0) { - control->Link = 0; + net->Control[index].Link = 0; return 0; } if (linkIndex < 0 || linkIndex > net->Nlinks) return 204; - // Cannot control check valve - if (net->Link[linkIndex].Type == CVPIPE) return 207; - - // Check for valid control properties - 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; + // Assign new set of properties to control + err = setcontrol(p, type, linkIndex, setting, nodeIndex, level, &ctrl); + if (err > 0) return err; + net->Control[index] = ctrl; return 0; } diff --git a/src/funcs.h b/src/funcs.h index 3fe38fe..a6d5150 100755 --- a/src/funcs.h +++ b/src/funcs.h @@ -7,7 +7,7 @@ Authors: see AUTHORS Copyright: see AUTHORS License: see LICENSE - Last Updated: 08/13/2022 + Last Updated: 04/29/2023 ****************************************************************************** */ #ifndef FUNCS_H @@ -50,6 +50,7 @@ void adjustpatterns(Network *, int); void adjustcurves(Network *, int); int adjustpumpparams(Project *, int); int resizecurve(Scurve *, int); +int setcontrol(Project *, int, int, double, int, double, Scontrol *); int getcomment(Network *, int, int, char *); int setcomment(Network *, int, int, const char *); diff --git a/src/inpfile.c b/src/inpfile.c index 33365ed..e23284d 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: 02/05/2023 +Last Updated: 04/30/2023 ****************************************************************************** */ @@ -432,7 +432,12 @@ int saveinpfile(Project *pr, const char *fname) link = &net->Link[j]; // 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]); } diff --git a/src/project.c b/src/project.c index d5eee6c..76ca351 100644 --- a/src/project.c +++ b/src/project.c @@ -7,7 +7,7 @@ Authors: see AUTHORS Copyright: see AUTHORS 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; } + +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) //---------------------------------------------------------------- // Input: object = a type of network object diff --git a/src/types.h b/src/types.h index b757041..bdc5c08 100755 --- a/src/types.h +++ b/src/types.h @@ -7,7 +7,7 @@ Authors: see AUTHORS Copyright: see AUTHORS 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 TINY 1.E-6 #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 // @ 20 deg C (sq ft/sec) #define VISCOS 1.1E-5 // Kinematic viscosity of water From 2b346c542761d8c2915f4df69b3a61b2214d93a6 Mon Sep 17 00:00:00 2001 From: Lew Rossman Date: Thu, 8 Jun 2023 10:21:35 -0400 Subject: [PATCH 2/2] Update ReleaseNotes2_3.md --- ReleaseNotes2_3.md | 1 + 1 file changed, 1 insertion(+) diff --git a/ReleaseNotes2_3.md b/ReleaseNotes2_3.md index dfe27d5..2d90393 100644 --- a/ReleaseNotes2_3.md +++ b/ReleaseNotes2_3.md @@ -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. - 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`. + - `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.