From 3ce0361c1d373b9a0ff15662c2b6548bc387090d Mon Sep 17 00:00:00 2001 From: Lew Rossman Date: Thu, 20 Dec 2018 08:36:05 -0500 Subject: [PATCH] Network validity checks added to openhyd() Checks on illegal valve connections made whenever API creates a new link, changes its end nodes, or changes its type. --- src/epanet.c | 35 ++++++++++++++++--------- src/funcs.h | 2 +- src/hydcoeffs.c | 10 ++++++- src/hydraul.c | 14 ++++++++++ src/input3.c | 64 ++++---------------------------------------- src/project.c | 70 ++++++++++++++++++++++++++++++++++++++++++++++--- 6 files changed, 118 insertions(+), 77 deletions(-) diff --git a/src/epanet.c b/src/epanet.c index 265e69f..68be073 100644 --- a/src/epanet.c +++ b/src/epanet.c @@ -203,7 +203,6 @@ int DLLEXPORT EN_open(EN_Project p, const char *f1, const char *f2, const char * errmsg(p, errcode); return errcode; } - writelogo(p); // Allocate memory for project's data arrays writewin(p->viewprog, FMT100); @@ -2592,7 +2591,7 @@ int DLLEXPORT EN_addlink(EN_Project p, char *id, EN_LinkType linkType, Network *net = &p->network; Hydraul *hyd = &p->hydraul; - int i, n, size; + int i, n, size, errcode; int n1, n2; Slink *link; Spump *pump; @@ -2604,6 +2603,9 @@ int DLLEXPORT EN_addlink(EN_Project p, char *id, EN_LinkType linkType, // Check if a link with same id already exists if (EN_getlinkindex(p, id, &i) == 0) return 215; + // Check for valid link type + if (linkType < CVPIPE || linkType > GPV) return 251; + // Lookup the link's from and to nodes n1 = hashtable_find(net->NodeHashTable, fromNode); n2 = hashtable_find(net->NodeHashTable, toNode); @@ -2612,10 +2614,16 @@ int DLLEXPORT EN_addlink(EN_Project p, char *id, EN_LinkType linkType, // Check that id name is not too long if (strlen(id) > MAXID) return 250; - net->Nlinks++; - n = net->Nlinks; + // Check that valve link has legal connections + if (linkType > PUMP) + { + errcode = valvecheck(p, linkType, n1, n2); + if (errcode) return errcode; + } // Grow link-related arrays to accomodate the new link + net->Nlinks++; + n = net->Nlinks; size = (n + 1) * sizeof(Slink); net->Link = (Slink *)realloc(net->Link, size); size = (n + 1) * sizeof(double); @@ -2624,6 +2632,7 @@ int DLLEXPORT EN_addlink(EN_Project p, char *id, EN_LinkType linkType, size = (n + 1) * sizeof(StatusType); hyd->LinkStatus = (StatusType *)realloc(hyd->LinkStatus, size); + // Set properties for the new link link = &net->Link[n]; strncpy(link->ID, id, MAXID); @@ -2636,7 +2645,7 @@ int DLLEXPORT EN_addlink(EN_Project p, char *id, EN_LinkType linkType, net->Pump = (Spump *)realloc(net->Pump, size); pump = &net->Pump[net->Npumps]; pump->Link = n; - pump->Ptype = 0; + pump->Ptype = NOCURVE; pump->Q0 = 0; pump->Qmax = 0; pump->Hmax = 0; @@ -2938,6 +2947,10 @@ int DLLEXPORT EN_setlinktype(EN_Project p, int *index, EN_LinkType type, int act EN_getnodeid(p, n1, id1); EN_getnodeid(p, n2, id2); + // Check for illegal valve connections + errcode = valvecheck(p, type, n1, n2); + if (errcode) return errcode; + // Delete the original link (and any controls containing it) EN_deletelink(p, i, actionCode); @@ -2979,7 +2992,7 @@ int DLLEXPORT EN_setlinknodes(EN_Project p, int index, int node1, int node2) */ { Network *net = &p->network; - int type; + int type, errcode; // Cannot modify network structure while solvers are active if (p->hydraul.OpenHflag || p->quality.OpenQflag) return 262; @@ -2993,14 +3006,10 @@ int DLLEXPORT EN_setlinknodes(EN_Project p, int index, int node1, int node2) // Check for illegal valve connection type = net->Link[index].Type; - if (type == EN_PRV || type == EN_PSV || type == EN_FCV) + if (type > PUMP) { - // Can't be connected to a fixed grade node - if (node1 > net->Njuncs || - node2 > net->Njuncs) return 219; - - // Can't be connected to another pressure/flow control valve - if (!valvecheck(p, type, node1, node2)) return 220; + errcode = valvecheck(p, type, node1, node2); + if (errcode) return errcode; } // Assign new end nodes to link diff --git a/src/funcs.h b/src/funcs.h index abf66ec..28fd298 100755 --- a/src/funcs.h +++ b/src/funcs.h @@ -29,6 +29,7 @@ int buildadjlists(Network *); void freeadjlists(Network *); int incontrols(Project *, int, int); +int valvecheck(Project *, int, int, int); int findnode(Network *, char *); int findlink(Network *, char *); int findtank(Network *, int); @@ -88,7 +89,6 @@ int statusdata(Project *); int reportdata(Project *); int timedata(Project *); int optiondata(Project *); -int valvecheck(Project *, int, int, int); // ------- RULES.C ------------------ diff --git a/src/hydcoeffs.c b/src/hydcoeffs.c index 75c74a1..b6c002c 100644 --- a/src/hydcoeffs.c +++ b/src/hydcoeffs.c @@ -771,11 +771,19 @@ void pumpcoeff(Project *pr, int k) return; } - // Obtain reference to pump object & its speed setting + // Obtain reference to pump object q = ABS(hyd->LinkFlow[k]); p = findpump(&pr->network, k); pump = &pr->network.Pump[p]; + // If no pump curve treat pump as an open valve + if (pump->Ptype == NOCURVE) + { + hyd->P[k] = 1.0 / CSMALL; + hyd->Y[k] = hyd->LinkFlow[k]; + return; + } + // Get pump curve coefficients for custom pump curve // (Other pump types have pre-determined coeffs.) if (pump->Ptype == CUSTOM) diff --git a/src/hydraul.c b/src/hydraul.c index 188eb67..703f7b7 100755 --- a/src/hydraul.c +++ b/src/hydraul.c @@ -58,12 +58,26 @@ int openhyd(Project *pr) int errcode = 0; Slink *link; + // Check for too few nodes & no fixed grade nodes + if (pr->network.Nnodes < 2) errcode = 223; + else if (pr->network.Ntanks == 0) errcode = 224; + // Allocate memory for sparse matrix structures (see SMATRIX.C) ERRCODE(createsparse(pr)); // Allocate memory for hydraulic variables ERRCODE(allocmatrix(pr)); + // Check for unconnected nodes + if (!errcode) for (i = 1; i <= pr->network.Njuncs; i++) + { + if (pr->network.Adjlist[i] == NULL) + { + errcode = 233; + break; + } + } + // Initialize link flows if (!errcode) for (i = 1; i <= pr->network.Nlinks; i++) { diff --git a/src/input3.c b/src/input3.c index 3e43494..fec367f 100644 --- a/src/input3.c +++ b/src/input3.c @@ -514,14 +514,13 @@ 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); - // Check that PRV, PSV, or FCV not connected to a tank & - // check for illegal connections between pairs of valves - if (type == PRV || type == PSV || type == FCV) + // Check for illegal connections + if (valvecheck(pr, type, j1, j2)) { - if (j1 > net->Njuncs) return setError(parser, 1, 219); - if (j2 > net->Njuncs) return setError(parser, 2, 219); + if (j1 > net->Njuncs) return setError(parser, 1, 219); + else if (j2 > net->Njuncs) return setError(parser, 2, 219); + else return setError(parser, -1, 220); } - if (!valvecheck(pr, type, j1, j2)) return setError(parser, -1, 220); // Save valve data link = &net->Link[net->Nlinks]; @@ -2064,59 +2063,6 @@ int powercurve(double h0, double h1, double h2, double q1, double q2, return 1; } -int valvecheck(Project *pr, int type, int j1, int j2) -/* -**-------------------------------------------------------------- -** Input: type = valve type -** j1 = index of upstream node -** j2 = index of downstream node -** Output: returns 1 for legal connection, 0 otherwise -** Purpose: checks for legal connections between PRVs & PSVs -**-------------------------------------------------------------- -*/ -{ - Network *net = &pr->network; - - int k, vj1, vj2; - LinkType vtype; - Slink *link; - Svalve *valve; - - // Examine each existing valve - for (k = 1; k <= net->Nvalves; k++) - { - valve = &net->Valve[k]; - link = &net->Link[valve->Link]; - vj1 = link->N1; - vj2 = link->N2; - vtype = link->Type; - - // Cannot have two PRVs sharing downstream nodes or in series - if (vtype == PRV && type == PRV) - { - if (vj2 == j2 || vj2 == j1 || vj1 == j2) return 0; - } - - // Cannot have two PSVs sharing upstream nodes or in series - if (vtype == PSV && type == PSV) - { - if (vj1 == j1 || vj1 == j2 || vj2 == j1) return 0; - } - - // Cannot have PSV connected to downstream node of PRV - if (vtype == PSV && type == PRV && vj1 == j2) return 0; - if (vtype == PRV && type == PSV && vj2 == j1) return 0; - - // Cannot have PSV connected to downstream node of FCV - // nor have PRV connected to upstream node of FCV - if (vtype == FCV && type == PSV && vj2 == j1) return 0; - if (vtype == FCV && type == PRV && vj1 == j2) return 0; - if (vtype == PSV && type == FCV && vj1 == j2) return 0; - if (vtype == PRV && type == FCV && vj2 == j1) return 0; - } - return 1; -} - void changestatus(Network *net, int j, StatusType status, double y) /* **-------------------------------------------------------------- diff --git a/src/project.c b/src/project.c index afdfd43..462368f 100644 --- a/src/project.c +++ b/src/project.c @@ -60,8 +60,8 @@ int openfiles(Project *pr, const char *f1, const char *f2, const char *f3) else pr->outfile.Outflag = SCRATCH; // Check that file names are not identical - if (strcomp(f1, f2) || strcomp(f1, f3) || - (strcomp(f2, f3) && (strlen(f2) > 0 || strlen(f3) > 0))) return 301; + if (strlen(f1) > 0 && (strcomp(f1, f2) || strcomp(f1, f3))) return 301; + if (strlen(f3) > 0 && strcomp(f2, f3)) return 301; // Attempt to open input and report files if (strlen(f1) > 0) @@ -69,7 +69,12 @@ int openfiles(Project *pr, const char *f1, const char *f2, const char *f3) if ((pr->parser.InFile = fopen(f1, "rt")) == NULL) return 302; } if (strlen(f2) == 0) pr->report.RptFile = stdout; - else if ((pr->report.RptFile = fopen(f2, "wt")) == NULL) return 303; + else + { + pr->report.RptFile = fopen(f2, "wt"); + if (pr->report.RptFile == NULL) return 303; + } + writelogo(pr); return 0; } @@ -625,6 +630,65 @@ int incontrols(Project *pr, int objType, int index) return 0; } +int valvecheck(Project *pr, int type, int j1, int j2) +/* +**-------------------------------------------------------------- +** Input: type = valve type +** j1 = index of upstream node +** j2 = index of downstream node +** Output: returns an error code +** Purpose: checks for illegal connections between valves +**-------------------------------------------------------------- +*/ +{ + Network *net = &pr->network; + + int k, vj1, vj2; + LinkType vtype; + Slink *link; + Svalve *valve; + + if (type == PRV || type == PSV || type == FCV) + { + // Can't be connected to a fixed grade node + if (j1 > net->Njuncs || j2 > net->Njuncs) return 219; + + // Examine each existing valve + for (k = 1; k <= net->Nvalves; k++) + { + valve = &net->Valve[k]; + link = &net->Link[valve->Link]; + vj1 = link->N1; + vj2 = link->N2; + vtype = link->Type; + + // Cannot have two PRVs sharing downstream nodes or in series + if (vtype == PRV && type == PRV) + { + if (vj2 == j2 || vj2 == j1 || vj1 == j2) return 220; + } + + // Cannot have two PSVs sharing upstream nodes or in series + if (vtype == PSV && type == PSV) + { + if (vj1 == j1 || vj1 == j2 || vj2 == j1) return 220; + } + + // Cannot have PSV connected to downstream node of PRV + if (vtype == PSV && type == PRV && vj1 == j2) return 220; + if (vtype == PRV && type == PSV && vj2 == j1) return 220; + + // Cannot have PSV connected to downstream node of FCV + // nor have PRV connected to upstream node of FCV + if (vtype == FCV && type == PSV && vj2 == j1) return 220; + if (vtype == FCV && type == PRV && vj1 == j2) return 220; + if (vtype == PSV && type == FCV && vj1 == j2) return 220; + if (vtype == PRV && type == FCV && vj2 == j1) return 220; + } + } + return 0; +} + int findnode(Network *network, char *id) /*---------------------------------------------------------------- ** Input: id = node ID