From 39fcfa2162cad97381b6902b14dc699dfe1bf8e4 Mon Sep 17 00:00:00 2001 From: Lew Rossman Date: Thu, 25 Oct 2018 09:39:28 -0400 Subject: [PATCH 1/8] Fixed bug setting pump curve params (#314) and in setting demand categories -Split off calc. of a pump's curve coeffs. from getpumpparams to updatepumpparams. - Fixed problem with initializing node demand category when default demand pattern set before node is created. - Increased MAXLINE to 1024 to accomodate increase in MAXMSG. - Replaced EN_geterror calls in input2.c with geterrmsg (it's bad form to call API functions in the body of the library) --- src/epanet.c | 6 +- src/funcs.h | 1 + src/input1.c | 1 + src/input2.c | 215 +++++++++++++++++++++++++++------------------------ src/types.h | 2 +- 5 files changed, 120 insertions(+), 105 deletions(-) diff --git a/src/epanet.c b/src/epanet.c index 00fe533..d4c004f 100644 --- a/src/epanet.c +++ b/src/epanet.c @@ -3835,6 +3835,7 @@ int DLLEXPORT EN_setoption(EN_ProjectHandle ph, int code, EN_API_FLOAT_TYPE v) for (demand = node->D; demand != NULL; demand = demand->next) { if (demand->Pat == tmpPat) { demand->Pat = (int)value; + strcpy(demand->Name, ""); } } } @@ -3973,7 +3974,7 @@ int DLLEXPORT EN_setheadcurveindex(EN_ProjectHandle ph, int index, int curveinde pump->Ptype = NOCURVE; pump->Hcurve = curveindex; // update pump parameters - getpumpparams(p); + updatepumpparams(p, pIdx); // convert units if (pump->Ptype == POWER_FUNC) { pump->H0 /= Ucf[HEAD]; @@ -4871,8 +4872,6 @@ int DLLEXPORT EN_setdemandname(EN_ProjectHandle ph, int nodeIndex, int demandIdx const int Nnodes = net->Nnodes; const int Njuncs = net->Njuncs; - double *Ucf = pr->Ucf; - Pdemand d; int n = 1; /* Check for valid arguments */ @@ -5047,6 +5046,7 @@ int DLLEXPORT EN_addnode(EN_ProjectHandle ph, char *id, EN_NodeType nodeType) { demand = (struct Sdemand *)malloc(sizeof(struct Sdemand)); demand->Base = 0.0; demand->Pat = hyd->DefPat; // Use default pattern + strcpy(demand->Name, ""); demand->next = NULL; node->D = demand; diff --git a/src/funcs.h b/src/funcs.h index 8d267f9..b0b027f 100755 --- a/src/funcs.h +++ b/src/funcs.h @@ -75,6 +75,7 @@ int addcurve(parser_data_t *par, char *); /* Adds curve to data base STmplist *findID(char *, STmplist *); /* Locates ID on linked list */ int unlinked(EN_Project *pr); /* Checks for unlinked nodes */ int getpumpparams(EN_Project *pr); /* Computes pump curve coeffs.*/ +int updatepumpparams(EN_Project *pr, int); // Updates pump curve coeffs. int getpatterns(EN_Project *pr); /* Gets pattern data from list*/ int getcurves(EN_Project *pr); /* Gets curve data from list */ int findmatch(char *, char *[]); /* Finds keyword in line */ diff --git a/src/input1.c b/src/input1.c index 1e59b80..89a4e1d 100644 --- a/src/input1.c +++ b/src/input1.c @@ -356,6 +356,7 @@ void adjustdata(EN_Project *pr) for (demand = node->D; demand != NULL; demand = demand->next) { if (demand->Pat == 0) { demand->Pat = hyd->DefPat; + strcpy(demand->Name, ""); } } } diff --git a/src/input2.c b/src/input2.c index d141d72..374ca87 100644 --- a/src/input2.c +++ b/src/input2.c @@ -40,10 +40,10 @@ The following utility functions are all called from INPUT3.C #define MAXERRS 10 /* Max. input errors reported */ - /* Defined in enumstxt.h in EPANET.C */ extern char *SectTxt[]; /* Input section keywords */ + int netsize(EN_Project *pr) /* **-------------------------------------------------------------- @@ -206,9 +206,10 @@ int readdata(EN_Project *pr) /* Check if max. length exceeded */ if (strlen(line) >= MAXLINE) { - char errMsg[MAXMSG+1]; - EN_geterror(214, errMsg, MAXMSG); - sprintf(pr->Msg, "%s section: %s", errMsg, SectTxt[sect]); +// char errMsg[MAXMSG+1]; +// EN_geterror(214, errMsg, MAXMSG); +// sprintf(pr->Msg, "%s section: %s", errMsg, SectTxt[sect]); + sprintf(pr->Msg, "%s section: %s", geterrmsg(214, pr->Msg), SectTxt[sect]); writeline(pr, pr->Msg); writeline(pr, line); errsum++; @@ -363,101 +364,121 @@ int getpumpparams(EN_Project *pr) **------------------------------------------------------------- ** Input: none ** Output: returns error code -** Purpose: computes & checks pump curve parameters +** Purpose: computes pump curve coefficients for all pumps **-------------------------------------------------------------- */ { - int i, j = 0, k, m, n = 0; - double a, b, c, h0 = 0.0, h1 = 0.0, h2 = 0.0, q1 = 0.0, q2 = 0.0; - char errMsg[MAXMSG+1]; - Spump *pump; - Slink *link; - Scurve *curve; - - EN_Network *net = &pr->network; - - for (i = 1; i <= net->Npumps; i++) { - pump = &net->Pump[i]; - k = pump->Link; - link = &net->Link[k]; - if (pump->Ptype == CONST_HP) { /* Constant Hp pump */ - pump->H0 = 0.0; - pump->R = -8.814 * link->Km; - pump->N = -1.0; - pump->Hmax = BIG; /* No head limit */ - pump->Qmax = BIG; /* No flow limit */ - pump->Q0 = 1.0; /* Init. flow = 1 cfs */ - continue; - } - else if (pump->Ptype == NOCURVE) { /* Pump curve specified */ - j = pump->Hcurve; /* Get index of head curve */ - if (j == 0) { /* Error: No head curve */ - EN_geterror(226, errMsg, MAXMSG); - sprintf(pr->Msg, "%s link: %s", errMsg, link->ID); - writeline(pr, pr->Msg); - return (200); - } - curve = &net->Curve[j]; - curve->Type = P_CURVE; - n = curve->Npts; - if (n == 1) { /* Only a single h-q point supplied so use generic */ - pump->Ptype = POWER_FUNC; /* power function curve. */ - q1 = curve->X[0]; - h1 = curve->Y[0]; - h0 = 1.33334 * h1; - q2 = 2.0 * q1; - h2 = 0.0; - } else if (n == 3 && curve->X[0] == 0.0) /* 3 h-q points supplied with */ - { /* shutoff head so use fitted */ - pump->Ptype = POWER_FUNC; /* power function curve. */ - h0 = curve->Y[0]; - q1 = curve->X[1]; - h1 = curve->Y[1]; - q2 = curve->X[2]; - h2 = curve->Y[2]; - } - else { // use a custom curve, referenced by ID - pump->Ptype = CUSTOM; /* Else use custom pump curve.*/ - // at this point, j is set to that curve's index. - } - - /* Compute shape factors & limits of power function pump curves */ - if (pump->Ptype == POWER_FUNC) { - if (!powercurve(h0, h1, h2, q1, q2, &a, &b, &c)) { /* Error: Invalid curve */ - EN_geterror(227, errMsg, MAXMSG); - sprintf(pr->Msg, "%s link: %s", errMsg, link->ID); - writeline(pr, pr->Msg); - return (200); - } else { - pump->H0 = -a; - pump->R = -b; - pump->N = c; - pump->Q0 = q1; - pump->Qmax = pow((-a / b), (1.0 / c)); - pump->Hmax = h0; + int i, k, errcode = 0; + EN_Network *net = &pr->network; + + for (i = 1; i <= net->Npumps; i++) + { + errcode = updatepumpparams(pr, i); + if (errcode) + { + k = net->Pump[i].Link; + sprintf(pr->Msg, "%s link: %s", geterrmsg(errcode, pr->Msg), + net->Link[k].ID); + writeline(pr, pr->Msg); + return 200; } - } + } + return 0; +} + +int updatepumpparams(EN_Project *pr, int pumpindex) +/* +**------------------------------------------------------------- +** Input: pumpindex = index of a pump +** Output: returns error code +** Purpose: computes & checks a pump's head curve coefficients +**-------------------------------------------------------------- +*/ +{ + int m; + int curveindex; + int npts = 0; + int errcode = 0; + double a, b, c, h0 = 0.0, h1 = 0.0, h2 = 0.0, q1 = 0.0, q2 = 0.0; + + EN_Network *net = &pr->network; + Spump *pump; + Scurve *curve; + + pump = &net->Pump[pumpindex]; + if (pump->Ptype == CONST_HP) // Constant Hp pump + { + pump->H0 = 0.0; + pump->R = -8.814 * net->Link[pump->Link].Km; + pump->N = -1.0; + pump->Hmax = BIG; // No head limit + pump->Qmax = BIG; // No flow limit + pump->Q0 = 1.0; // Init. flow = 1 cfs + return errcode; } - /* Assign limits to custom pump curves */ - if (pump->Ptype == CUSTOM) { - curve = &net->Curve[j]; - for (m = 1; m < n; m++) { - if (curve->Y[m] >= curve->Y[m - 1]) { /* Error: Invalid curve */ - EN_geterror(227, errMsg, MAXMSG); - sprintf(pr->Msg, "%s link: %s", errMsg, link->ID); - writeline(pr, pr->Msg); - return (200); + else if (pump->Ptype == NOCURVE) // Pump curve specified + { + curveindex = pump->Hcurve; + if (curveindex == 0) return 226; + curve = &net->Curve[curveindex]; + curve->Type = P_CURVE; + npts = curve->Npts; + + // Generic power function curve + if (npts == 1) + { + pump->Ptype = POWER_FUNC; + q1 = curve->X[0]; + h1 = curve->Y[0]; + h0 = 1.33334 * h1; + q2 = 2.0 * q1; + h2 = 0.0; + } + + // 3 point curve with shutoff head + else if (npts == 3 && curve->X[0] == 0.0) + { + pump->Ptype = POWER_FUNC; + h0 = curve->Y[0]; + q1 = curve->X[1]; + h1 = curve->Y[1]; + q2 = curve->X[2]; + h2 = curve->Y[2]; + } + + // Custom pump curve + else + { + pump->Ptype = CUSTOM; + for (m = 1; m < npts; m++) + { + if (curve->Y[m] >= curve->Y[m - 1]) return 227; + } + pump->Qmax = curve->X[npts - 1]; + pump->Q0 = (curve->X[0] + pump->Qmax) / 2.0; + pump->Hmax = curve->Y[0]; + } + + // Compute shape factors & limits of power function curves + if (pump->Ptype == POWER_FUNC) + { + if (!powercurve(h0, h1, h2, q1, q2, &a, &b, &c)) return 227; + else + { + pump->H0 = -a; + pump->R = -b; + pump->N = c; + pump->Q0 = q1; + pump->Qmax = pow((-a / b), (1.0 / c)); + pump->Hmax = h0; + } } - } - pump->Qmax = curve->X[n - 1]; - pump->Q0 = (curve->X[0] + pump->Qmax) / 2.0; - pump->Hmax = curve->Y[0]; } - } /* Next pump */ - return (0); + return 0; } + int addnodeID(EN_Network *net, int n, char *id) /* **------------------------------------------------------------- @@ -603,7 +624,6 @@ int unlinked(EN_Project *pr) EN_Network *net = &pr->network; int *marked; int i, err, errcode; - char errMsg[MAXMSG+1]; errcode = 0; err = 0; @@ -621,8 +641,7 @@ int unlinked(EN_Project *pr) if (marked[i] == 0) /* If not marked then error */ { err++; - EN_geterror(233, errMsg, MAXMSG); - sprintf(pr->Msg, "%s node: %s", errMsg, net->Node[i].ID); + sprintf(pr->Msg, "%s link: %s", geterrmsg(233, pr->Msg), net->Node[i].ID); writeline(pr, pr->Msg); } if (err >= MAXERRS) @@ -732,9 +751,7 @@ int getcurves(EN_Project *pr) /* Check that curve has data points */ if (curve->Npts <= 0) { - char errMsg[MAXMSG+1]; - EN_geterror(230, errMsg, MAXMSG); - sprintf(pr->Msg, "%s curve: %s", errMsg, curve->ID); + sprintf(pr->Msg, "%s link: %s", geterrmsg(230, pr->Msg), curve->ID); writeline(pr, pr->Msg); return (200); } @@ -754,9 +771,7 @@ int getcurves(EN_Project *pr) /* Check that x data is in ascending order */ if (fx->value >= x) { - char errMsg[MAXMSG+1]; - EN_geterror(230, errMsg, MAXMSG); - sprintf(pr->Msg, "%s node: %s", errMsg, curve->ID); + sprintf(pr->Msg, "%s link: %s", geterrmsg(230, pr->Msg), curve->ID); writeline(pr, pr->Msg); return (200); } @@ -1012,10 +1027,8 @@ void inperrmsg(EN_Project *pr, int err, int sect, char *line) char errStr[MAXMSG + 1]; char id[MAXMSG + 1]; - EN_geterror(err, errStr, MAXMSG); - /* get text for error message */ - sprintf(pr->Msg, "%s - section: %s", errStr, SectTxt[sect]); + sprintf(pr->Msg, "%s - section: %s", geterrmsg(err, errStr), SectTxt[sect]); // append ID? /* Retrieve ID label of object with input error */ diff --git a/src/types.h b/src/types.h index 1984b28..58f5137 100755 --- a/src/types.h +++ b/src/types.h @@ -51,7 +51,7 @@ typedef int INT4; #define TITLELEN 79 // Max. # characters in a title line #define MAXID 31 /* Max. # characters in ID name */ #define MAXMSG 255 /* Max. # characters in message text */ -#define MAXLINE 255 /* Max. # characters read from input line */ +#define MAXLINE 1024 /* Max. # characters read from input line */ #define MAXFNAME 259 /* Max. # characters in file name */ #define MAXTOKS 40 /* Max. items per line of input */ #define TZERO 1.E-4 /* Zero time tolerance */ From e9161adbe621799a2a717cceae1c6035821dcb85 Mon Sep 17 00:00:00 2001 From: Lew Rossman Date: Sat, 27 Oct 2018 08:48:41 -0400 Subject: [PATCH 2/8] Defined EN_MAXID & EN_MAXMSG limits in API header --- include/epanet2.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/epanet2.h b/include/epanet2.h index 1593783..842875c 100644 --- a/include/epanet2.h +++ b/include/epanet2.h @@ -67,6 +67,9 @@ // --- Define the EPANET toolkit constants +#define EN_MAXID 31 /**< Max. # characters in ID name */ +#define EN_MAXMSG 255 /**< Max. # characters in message text */ + /// Node property codes typedef enum { EN_ELEVATION = 0, /**< Node Elevation */ From 8286918b94e2c073d5ec88b1dbe2bfd27aab04b3 Mon Sep 17 00:00:00 2001 From: Lew Rossman Date: Sat, 27 Oct 2018 09:05:30 -0400 Subject: [PATCH 3/8] Removed unused runconcurrent() function --- src/epanet.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/epanet.c b/src/epanet.c index d4c004f..b26c7b6 100644 --- a/src/epanet.c +++ b/src/epanet.c @@ -143,7 +143,7 @@ void errorLookup(int errcode, char *errmsg, int len); LEGACY (v <= 2.1) API: uses global project variable *****************************************************************/ - +/* int runconcurrent(EN_ProjectHandle ph, const char *inputfile, const char *reportfile, const char *outputfile, void(*pviewprog)(char *)) { @@ -180,7 +180,7 @@ int runconcurrent(EN_ProjectHandle ph, const char *inputfile, const char *report return errcode; } - +*/ /*------------------------------------------------------------------------ ** Input: f1 = pointer to name of input file ** f2 = pointer to name of report file From 9d0b738e771efbd87def518ece8f734c83fe4918 Mon Sep 17 00:00:00 2001 From: Lew Rossman Date: Sat, 27 Oct 2018 09:36:21 -0400 Subject: [PATCH 4/8] Removed writecon function and all calls to it --- src/epanet.c | 58 ++++++++++++++++++++++++++-------------------------- src/funcs.h | 2 +- src/report.c | 12 +++++------ 3 files changed, 36 insertions(+), 36 deletions(-) diff --git a/src/epanet.c b/src/epanet.c index b26c7b6..e8b5eb9 100644 --- a/src/epanet.c +++ b/src/epanet.c @@ -792,8 +792,8 @@ int DLLEXPORT EN_open(EN_ProjectHandle ph, const char *f1, const char *f2, const writelogo(p); /* Find network size & allocate memory for data */ - writecon(FMT02); - writewin(p->viewprog, FMT100); +//// writecon(FMT02); +//// writewin(p->viewprog, FMT100); ERRCODE(netsize(p)); ERRCODE(allocdata(p)); @@ -920,7 +920,7 @@ int DLLEXPORT EN_solveH(EN_ProjectHandle ph) if (!errcode) { /* Initialize hydraulics */ errcode = EN_initH(ph, EN_SAVE); - writecon(FMT14); +//// writecon(FMT14); /* Analyze each hydraulic period */ if (!errcode) @@ -932,7 +932,7 @@ int DLLEXPORT EN_solveH(EN_ProjectHandle ph) sprintf(p->Msg, "%-10s", clocktime(p->report.Atime, p->time_options.Htime)); - writecon(p->Msg); +//// writecon(p->Msg); sprintf(p->Msg, FMT101, p->report.Atime); writewin(p->viewprog, p->Msg); @@ -941,14 +941,14 @@ int DLLEXPORT EN_solveH(EN_ProjectHandle ph) ERRCODE(EN_runH(ph, &t)); ERRCODE(EN_nextH(ph, &tstep)); /*** Updated 6/24/02 ***/ - writecon("\b\b\b\b\b\b\b\b\b\b"); +//// writecon("\b\b\b\b\b\b\b\b\b\b"); } while (tstep > 0); } /* Close hydraulics solver */ /*** Updated 6/24/02 ***/ - writecon("\b\b\b\b\b\b\b\b "); +//// writecon("\b\b\b\b\b\b\b\b "); EN_closeH(ph); errcode = MAX(errcode, p->Warnflag); @@ -1190,12 +1190,12 @@ int DLLEXPORT EN_solveQ(EN_ProjectHandle ph) { if (!errcode) { /* Initialize WQ */ errcode = EN_initQ(ph, EN_SAVE); - if (p->quality.Qualflag) - writecon(FMT15); - else { - writecon(FMT16); - writewin(p->viewprog, FMT103); - } +//// if (p->quality.Qualflag) +//// writecon(FMT15); +//// else { +//// writecon(FMT16); + if (!p->quality.Qualflag) writewin(p->viewprog, FMT103); +//// } /* Analyze each hydraulic period */ if (!errcode) @@ -1207,7 +1207,7 @@ int DLLEXPORT EN_solveQ(EN_ProjectHandle ph) { sprintf(p->Msg, "%-10s", clocktime(p->report.Atime, p->time_options.Htime)); - writecon(p->Msg); +//// writecon(p->Msg); if (p->quality.Qualflag) { sprintf(p->Msg, FMT102, p->report.Atime); writewin(p->viewprog, p->Msg); @@ -1219,7 +1219,7 @@ int DLLEXPORT EN_solveQ(EN_ProjectHandle ph) { ERRCODE(EN_nextQ(ph, &tstep)); /*** Updated 6/24/02 ***/ - writecon("\b\b\b\b\b\b\b\b\b\b"); +//// writecon("\b\b\b\b\b\b\b\b\b\b"); } while (tstep > 0); } @@ -1227,7 +1227,7 @@ int DLLEXPORT EN_solveQ(EN_ProjectHandle ph) { /* Close WQ solver */ /*** Updated 6/24/02 ***/ - writecon("\b\b\b\b\b\b\b\b "); +//// writecon("\b\b\b\b\b\b\b\b "); EN_closeQ(ph); return set_error(p->error_handle, errcode); } @@ -4064,20 +4064,20 @@ int openfiles(EN_Project *p, const char *f1, const char *f2, const char *f3) /* Check that file names are not identical */ if (strcomp(f1, f2) || strcomp(f1, f3) || (strcomp(f2, f3) && (strlen(f2) > 0 || strlen(f3) > 0))) { - writecon(FMT04); +//// writecon(FMT04); return 301; } /* Attempt to open input and report files */ if ((par->InFile = fopen(f1, "rt")) == NULL) { - writecon(FMT05); - writecon(f1); +//// writecon(FMT05); +//// writecon(f1); return 302; } if (strlen(f2) == 0) rep->RptFile = stdout; else if ((rep->RptFile = fopen(f2, "wt")) == NULL) { - writecon(FMT06); +//// writecon(FMT06); return 303; } @@ -4208,7 +4208,7 @@ int openoutfile(EN_Project *p) if (out->Outflag == SAVE) { if ((out->OutFile = fopen(out->OutFname, "w+b")) == NULL) { - writecon(FMT07); +//// writecon(FMT07); errcode = 304; } } @@ -4218,7 +4218,7 @@ int openoutfile(EN_Project *p) getTmpName(p, out->OutFname); if ((out->OutFile = fopen(out->OutFname, "w+b")) == NULL) { - writecon(FMT08); +//// writecon(FMT08); errcode = 304; } } @@ -4733,25 +4733,25 @@ void errmsg(EN_Project *p, int errcode) { if (errcode == 309) /* Report file write error - */ { /* Do not write msg to file. */ - writecon("\n "); - writecon(geterrmsg(errcode,p->Msg)); +//// writecon("\n "); +//// writecon(geterrmsg(errcode,p->Msg)); } else if (p->report.RptFile != NULL && p->report.Messageflag) { writeline(p, geterrmsg(errcode,p->Msg)); } } -void writecon(const char *s) +//void writecon(const char *s) /*---------------------------------------------------------------- ** Input: text string ** Output: none ** Purpose: writes string of characters to console **---------------------------------------------------------------- */ -{ - - fprintf(stdout, "%s", s); - fflush(stdout); -} +//{ +// +// fprintf(stdout, "%s", s); +// fflush(stdout); +//} void writewin(void (*vp)(char *), char *s) /*---------------------------------------------------------------- diff --git a/src/funcs.h b/src/funcs.h index b0b027f..082b576 100755 --- a/src/funcs.h +++ b/src/funcs.h @@ -52,7 +52,7 @@ int findvalve(EN_Network *n, int); /* Find valve index from node int findpump(EN_Network *n, int); /* Find pump index from node index */ // (AH) char *geterrmsg(int errcode, char *msg); /* Gets text of error message */ void errmsg(EN_Project *p, int); /* Reports program error */ -void writecon(const char *); /* Writes text to console */ +////void writecon(const char *); /* Writes text to console */ void writewin(void (*vp)(char *), char *); /* Passes text to calling app */ /* ------- INPUT1.C --------------------*/ diff --git a/src/report.c b/src/report.c index 058ab22..b0ee1b3 100644 --- a/src/report.c +++ b/src/report.c @@ -82,8 +82,8 @@ int writereport(EN_Project *pr) /* write formatted output to primary report file. */ rep->Fprinterr = FALSE; if (rep->Rptflag && strlen(rep->Rpt2Fname) == 0 && rep->RptFile != NULL) { - writecon(FMT17); - writecon(rep->Rpt1Fname); +//// writecon(FMT17); +//// writecon(rep->Rpt1Fname); if (rep->Energyflag) writeenergy(pr); errcode = writeresults(pr); @@ -95,8 +95,8 @@ int writereport(EN_Project *pr) /* If secondary report file has same name as either input */ /* or primary report file then use primary report file. */ if (strcomp(rep->Rpt2Fname, par->InpFname) || strcomp(rep->Rpt2Fname, rep->Rpt1Fname)) { - writecon(FMT17); - writecon(rep->Rpt1Fname); +//// writecon(FMT17); +//// writecon(rep->Rpt1Fname); if (rep->Energyflag) writeenergy(pr); errcode = writeresults(pr); @@ -117,8 +117,8 @@ int writereport(EN_Project *pr) /* Write full formatted report to file */ else { rep->Rptflag = 1; - writecon(FMT17); - writecon(rep->Rpt2Fname); +//// writecon(FMT17); +//// writecon(rep->Rpt2Fname); writelogo(pr); if (rep->Summaryflag) writesummary(pr); From 320dec3ff7a2172a6857b8b7114725a9a4331d0c Mon Sep 17 00:00:00 2001 From: Lew Rossman Date: Sat, 27 Oct 2018 11:00:00 -0400 Subject: [PATCH 5/8] Replaced main() for runnable version of the library --- run/main.c | 143 ++++++++++++++++++++++----------------------------- src/epanet.c | 99 ++++------------------------------- src/funcs.h | 1 - src/report.c | 6 --- src/text.h | 14 ++--- 5 files changed, 76 insertions(+), 187 deletions(-) diff --git a/run/main.c b/run/main.c index 5d811ee..00bc4ee 100644 --- a/run/main.c +++ b/run/main.c @@ -1,33 +1,26 @@ -#include -#include -#include "epanet2.h" - -#define MAXMSG 255 /* Max. # characters in message text */ -#define MAXWARNCODE 99 -/* text copied here, no more need of include "text.h" */ -#define FMT01 "\nEPANET Version %d.%d.%d\n" -#define FMT03 "\nUsage:\n %s []\n" -#define FMT09 "\n\nEPANET completed.\n" -#define FMT10 "\nEPANET completed. There are warnings.\n" -#define FMT11 "\nEPANET completed. There are errors.\n" - - -void writeConsole(char *s); - - /* ---------------------------------------------------------------- - Entry point used to compile a stand-alone executable. +Command line executable for the EPANET water distribution system +analysis program using the EPANET API library. ---------------------------------------------------------------- */ +#include +#include "epanet2.h" -int main(int argc, char *argv[]) +// Function for writing progress messages to the console +void writeConsole(char *s) +{ + fprintf(stdout, "\r%s", s); + fflush(stdout); +} + +int main(int argc, char *argv[]) /*-------------------------------------------------------------- ** Input: argc = number of command line arguments ** *argv[] = array of command line arguments ** Output: none - ** Purpose: main program segment + ** Purpose: main program stub for command line EPANET ** ** Command line for stand-alone operation is: ** progname f1 f2 f3 @@ -38,70 +31,56 @@ int main(int argc, char *argv[]) **-------------------------------------------------------------- */ { - char *f1,*f2,*f3; - char blank[] = ""; - char errmsg[MAXMSG+1]=""; - int errcode; - int version; - int major; - int minor; - int patch; + char *f1,*f2,*f3; + char blank[] = ""; + int errcode, version, major, minor, patch; + EN_ProjectHandle ph; + + // Check for proper number of command line arguments + if (argc < 2) + { + printf( + "\nUsage:\n %s []\n", + argv[0]); + return 0; + } - /* get version from DLL and trasform in Major.Minor.Patch format - instead of hardcoded version */ - ENgetversion(&version); - major= version/10000; - minor= (version%10000)/100; - patch= version%100; - printf(FMT01, major, minor, patch); + // Get version number and display in Major.Minor.Patch format + ENgetversion(&version); + major = version/10000; + minor = (version%10000)/100; + patch = version%100; + printf("\n... Running EPANET Version %d.%d.%d\n", major, minor, patch); - /* Check for proper number of command line arguments */ - if (argc < 2) { - printf(FMT03, argv[0]); - return(1); - } + // Assign pointers to file names + f1 = argv[1]; + if (argc > 2) f2 = argv[2]; // set rptfile name + else f2 = blank; // use stdout for rptfile + if (argc > 3) f3 = argv[3]; // set binary output file name + else f3 = blank; // no binary output file - /* set inputfile name */ - f1 = argv[1]; - if (argc > 2) { - /* set rptfile name */ - f2 = argv[2]; - } - else { - /* use stdout for rptfile */ - f2 = blank; - } - if (argc > 3) { - /* set binary output file name */ - f3 = argv[3]; - } - else { - /* NO binary output*/ - f3 = blank; - } + // Create a project, run it, and delete it + EN_createproject(&ph); + errcode = EN_runproject(ph, f1, f2, f3, &writeConsole); + EN_deleteproject(&ph); - /* Call the main control function */ - errcode = ENepanet(f1,f2,f3,NULL); + // Blank out the last progress message + printf("\r "); - /* Error/Warning check */ - if (errcode == 0) { - /* no errors */ - printf(FMT09); - return(0); - } - else { - if (errcode > MAXWARNCODE) printf("\n Fatal Error: "); - ENgeterror(errcode, errmsg, MAXMSG); - printf("%s\n", errmsg); - if (errcode > MAXWARNCODE) { - // error // - printf(FMT11); - return(errcode); - } - else { - // warning // - printf(FMT10); - return(0); - } - } -} /* End of main */ + // Report run's status + if (errcode == 0) + { + printf("\n... EPANET ran successfully.\n"); + } + else if (errcode < 100) + { + printf( + "\n... EPANET ran with warnings - see the Status Report.\n"); + } + else + { + printf( + "\n... EPANET failed with ERROR %d - see the Status Report\n", errcode); + } + return errcode; +} diff --git a/src/epanet.c b/src/epanet.c index e8b5eb9..74ca6ce 100644 --- a/src/epanet.c +++ b/src/epanet.c @@ -143,44 +143,7 @@ void errorLookup(int errcode, char *errmsg, int len); LEGACY (v <= 2.1) API: uses global project variable *****************************************************************/ -/* -int runconcurrent(EN_ProjectHandle ph, const char *inputfile, const char *reportfile, - const char *outputfile, void(*pviewprog)(char *)) -{ - long t, tstep_h, tstep_q; - int errcode = 0; - EN_Project *p = NULL; - - - ERRCODE(EN_open(ph, inputfile, reportfile, outputfile)); - p = (EN_Project*)(ph); - p->viewprog = pviewprog; - - ERRCODE(EN_openH(ph)); - ERRCODE(EN_initH(ph, EN_SAVE)); - - ERRCODE(EN_openQ(ph)); - ERRCODE(EN_initQ(ph, EN_SAVE)); - - do { - ERRCODE(EN_runH(ph, &t)); - ERRCODE(EN_runQ(ph, &t)); - - ERRCODE(EN_nextH(ph, &tstep_h)); - ERRCODE(EN_nextQ(ph, &tstep_q)); - - } while (tstep_h > 0); - - ERRCODE(EN_closeH(ph)); - ERRCODE(EN_closeQ(ph)); - - ERRCODE(EN_report(ph)); - ERRCODE(EN_close(ph)); - - return errcode; -} -*/ /*------------------------------------------------------------------------ ** Input: f1 = pointer to name of input file ** f2 = pointer to name of report file @@ -201,14 +164,17 @@ int runconcurrent(EN_ProjectHandle ph, const char *inputfile, const char *report int DLLEXPORT ENepanet(const char *f1, const char *f2, const char *f3, void (*pviewprog)(char *)) { int errcode = 0; + int warncode = 0; EN_Project *p = NULL; ERRCODE(EN_createproject(&_defaultModel)); ERRCODE(EN_runproject(_defaultModel, f1, f2, f3, pviewprog)); + if (errcode < 100) warncode = errcode; ERRCODE(EN_deleteproject(&_defaultModel)); + if (warncode) errcode = MAX(errcode, warncode); return (errcode); } @@ -682,7 +648,8 @@ int DLLEXPORT EN_runproject(EN_ProjectHandle ph, const char *f1, const char *f2, ERRCODE(EN_report(ph)); EN_close(ph); - + + if (p->Warnflag) errcode = MAX(errcode, p->Warnflag); return errcode; } @@ -792,8 +759,7 @@ int DLLEXPORT EN_open(EN_ProjectHandle ph, const char *f1, const char *f2, const writelogo(p); /* Find network size & allocate memory for data */ -//// writecon(FMT02); -//// writewin(p->viewprog, FMT100); + writewin(p->viewprog, FMT100); ERRCODE(netsize(p)); ERRCODE(allocdata(p)); @@ -920,19 +886,14 @@ int DLLEXPORT EN_solveH(EN_ProjectHandle ph) if (!errcode) { /* Initialize hydraulics */ errcode = EN_initH(ph, EN_SAVE); -//// writecon(FMT14); /* Analyze each hydraulic period */ if (!errcode) do { /* Display progress message */ - - /*** Updated 6/24/02 ***/ sprintf(p->Msg, "%-10s", clocktime(p->report.Atime, p->time_options.Htime)); - -//// writecon(p->Msg); sprintf(p->Msg, FMT101, p->report.Atime); writewin(p->viewprog, p->Msg); @@ -940,16 +901,10 @@ int DLLEXPORT EN_solveH(EN_ProjectHandle ph) tstep = 0; ERRCODE(EN_runH(ph, &t)); ERRCODE(EN_nextH(ph, &tstep)); - /*** Updated 6/24/02 ***/ -//// writecon("\b\b\b\b\b\b\b\b\b\b"); } while (tstep > 0); } /* Close hydraulics solver */ - - /*** Updated 6/24/02 ***/ -//// writecon("\b\b\b\b\b\b\b\b "); - EN_closeH(ph); errcode = MAX(errcode, p->Warnflag); @@ -1190,24 +1145,15 @@ int DLLEXPORT EN_solveQ(EN_ProjectHandle ph) { if (!errcode) { /* Initialize WQ */ errcode = EN_initQ(ph, EN_SAVE); -//// if (p->quality.Qualflag) -//// writecon(FMT15); -//// else { -//// writecon(FMT16); - if (!p->quality.Qualflag) writewin(p->viewprog, FMT103); -//// } + if (!p->quality.Qualflag) writewin(p->viewprog, FMT106); /* Analyze each hydraulic period */ if (!errcode) do { /* Display progress message */ - - /*** Updated 6/24/02 ***/ sprintf(p->Msg, "%-10s", clocktime(p->report.Atime, p->time_options.Htime)); - -//// writecon(p->Msg); if (p->quality.Qualflag) { sprintf(p->Msg, FMT102, p->report.Atime); writewin(p->viewprog, p->Msg); @@ -1217,17 +1163,10 @@ int DLLEXPORT EN_solveQ(EN_ProjectHandle ph) { tstep = 0; ERRCODE(EN_runQ(ph, &t)); ERRCODE(EN_nextQ(ph, &tstep)); - - /*** Updated 6/24/02 ***/ -//// writecon("\b\b\b\b\b\b\b\b\b\b"); - } while (tstep > 0); } /* Close WQ solver */ - - /*** Updated 6/24/02 ***/ -//// writecon("\b\b\b\b\b\b\b\b "); EN_closeQ(ph); return set_error(p->error_handle, errcode); } @@ -1355,6 +1294,7 @@ int DLLEXPORT EN_report(EN_ProjectHandle ph) { /* Check if results saved to binary output file */ if (!p->save_options.SaveQflag) return set_error(p->error_handle, 106); + writewin(p->viewprog, FMT103); errcode = writereport(p); if (errcode) errmsg(p, errcode); @@ -4064,20 +4004,16 @@ int openfiles(EN_Project *p, const char *f1, const char *f2, const char *f3) /* Check that file names are not identical */ if (strcomp(f1, f2) || strcomp(f1, f3) || (strcomp(f2, f3) && (strlen(f2) > 0 || strlen(f3) > 0))) { -//// writecon(FMT04); return 301; } /* Attempt to open input and report files */ if ((par->InFile = fopen(f1, "rt")) == NULL) { -//// writecon(FMT05); -//// writecon(f1); return 302; } if (strlen(f2) == 0) rep->RptFile = stdout; else if ((rep->RptFile = fopen(f2, "wt")) == NULL) { -//// writecon(FMT06); return 303; } @@ -4208,7 +4144,6 @@ int openoutfile(EN_Project *p) if (out->Outflag == SAVE) { if ((out->OutFile = fopen(out->OutFname, "w+b")) == NULL) { -//// writecon(FMT07); errcode = 304; } } @@ -4218,7 +4153,6 @@ int openoutfile(EN_Project *p) getTmpName(p, out->OutFname); if ((out->OutFile = fopen(out->OutFname, "w+b")) == NULL) { -//// writecon(FMT08); errcode = 304; } } @@ -4715,6 +4649,7 @@ char *geterrmsg(int errcode, char *msg) { switch (errcode) { /* Warnings */ #define DAT(code,enumer,string) case code: strcpy(msg, string); break; +//#define DAT(code,enumer,string) case code: sprintf(msg, "Error %d: %s", code, string); break; #include "errors.dat" #undef DAT default: @@ -4733,26 +4668,12 @@ void errmsg(EN_Project *p, int errcode) { if (errcode == 309) /* Report file write error - */ { /* Do not write msg to file. */ -//// writecon("\n "); -//// writecon(geterrmsg(errcode,p->Msg)); + } else if (p->report.RptFile != NULL && p->report.Messageflag) { writeline(p, geterrmsg(errcode,p->Msg)); } } -//void writecon(const char *s) -/*---------------------------------------------------------------- -** Input: text string -** Output: none -** Purpose: writes string of characters to console -**---------------------------------------------------------------- -*/ -//{ -// -// fprintf(stdout, "%s", s); -// fflush(stdout); -//} - void writewin(void (*vp)(char *), char *s) /*---------------------------------------------------------------- ** Input: text string diff --git a/src/funcs.h b/src/funcs.h index 082b576..bf1d913 100755 --- a/src/funcs.h +++ b/src/funcs.h @@ -52,7 +52,6 @@ int findvalve(EN_Network *n, int); /* Find valve index from node int findpump(EN_Network *n, int); /* Find pump index from node index */ // (AH) char *geterrmsg(int errcode, char *msg); /* Gets text of error message */ void errmsg(EN_Project *p, int); /* Reports program error */ -////void writecon(const char *); /* Writes text to console */ void writewin(void (*vp)(char *), char *); /* Passes text to calling app */ /* ------- INPUT1.C --------------------*/ diff --git a/src/report.c b/src/report.c index b0ee1b3..cbb0e11 100644 --- a/src/report.c +++ b/src/report.c @@ -82,8 +82,6 @@ int writereport(EN_Project *pr) /* write formatted output to primary report file. */ rep->Fprinterr = FALSE; if (rep->Rptflag && strlen(rep->Rpt2Fname) == 0 && rep->RptFile != NULL) { -//// writecon(FMT17); -//// writecon(rep->Rpt1Fname); if (rep->Energyflag) writeenergy(pr); errcode = writeresults(pr); @@ -95,8 +93,6 @@ int writereport(EN_Project *pr) /* If secondary report file has same name as either input */ /* or primary report file then use primary report file. */ if (strcomp(rep->Rpt2Fname, par->InpFname) || strcomp(rep->Rpt2Fname, rep->Rpt1Fname)) { -//// writecon(FMT17); -//// writecon(rep->Rpt1Fname); if (rep->Energyflag) writeenergy(pr); errcode = writeresults(pr); @@ -117,8 +113,6 @@ int writereport(EN_Project *pr) /* Write full formatted report to file */ else { rep->Rptflag = 1; -//// writecon(FMT17); -//// writecon(rep->Rpt2Fname); writelogo(pr); if (rep->Summaryflag) writesummary(pr); diff --git a/src/text.h b/src/text.h index 656b3c2..b428be1 100755 --- a/src/text.h +++ b/src/text.h @@ -449,19 +449,15 @@ AUTHOR: L. Rossman #define FMT82 "\n\f\n Page %-d %60.60s\n" /* ------------------- Progress Messages ---------------------- */ -#define FMT100 "Retrieving network data..." -#define FMT101 "Computing hydraulics at hour %s" -#define FMT102 "Computing water quality at hour %s" -#define FMT103 "Saving results to file..." +#define FMT100 " Retrieving network data ... " +#define FMT101 " Computing hydraulics at hour %-10s " +#define FMT102 " Computing water quality at hour %-10s " +#define FMT103 " Writing output report ... " +#define FMT106 " Transferring results to file ... " #define FMT104 "Analysis begun %s" #define FMT105 "Analysis ended %s" /*------------------- Error Messages --------------------*/ - - - - - #define R_ERR201 "Input Error 201: syntax error in following line of " #define R_ERR202 "Input Error 202: illegal numeric value in following line of " #define R_ERR203 "Input Error 203: undefined node in following line of " From 27cc088c4a9089f604e992db6317adaf44603a6b Mon Sep 17 00:00:00 2001 From: Lew Rossman Date: Sat, 27 Oct 2018 11:31:06 -0400 Subject: [PATCH 6/8] Reverted back to old main() Trying to see why new version of main causes some CI reg tests to fail. --- run/main.c | 143 ++++++++++++++++++++++++++++++----------------------- 1 file changed, 82 insertions(+), 61 deletions(-) diff --git a/run/main.c b/run/main.c index 00bc4ee..5d811ee 100644 --- a/run/main.c +++ b/run/main.c @@ -1,26 +1,33 @@ +#include +#include +#include "epanet2.h" + +#define MAXMSG 255 /* Max. # characters in message text */ +#define MAXWARNCODE 99 +/* text copied here, no more need of include "text.h" */ +#define FMT01 "\nEPANET Version %d.%d.%d\n" +#define FMT03 "\nUsage:\n %s []\n" +#define FMT09 "\n\nEPANET completed.\n" +#define FMT10 "\nEPANET completed. There are warnings.\n" +#define FMT11 "\nEPANET completed. There are errors.\n" + + +void writeConsole(char *s); + + /* ---------------------------------------------------------------- -Command line executable for the EPANET water distribution system -analysis program using the EPANET API library. + Entry point used to compile a stand-alone executable. ---------------------------------------------------------------- */ -#include -#include "epanet2.h" -// Function for writing progress messages to the console -void writeConsole(char *s) -{ - fprintf(stdout, "\r%s", s); - fflush(stdout); -} - -int main(int argc, char *argv[]) +int main(int argc, char *argv[]) /*-------------------------------------------------------------- ** Input: argc = number of command line arguments ** *argv[] = array of command line arguments ** Output: none - ** Purpose: main program stub for command line EPANET + ** Purpose: main program segment ** ** Command line for stand-alone operation is: ** progname f1 f2 f3 @@ -31,56 +38,70 @@ int main(int argc, char *argv[]) **-------------------------------------------------------------- */ { - char *f1,*f2,*f3; - char blank[] = ""; - int errcode, version, major, minor, patch; - EN_ProjectHandle ph; - - // Check for proper number of command line arguments - if (argc < 2) - { - printf( - "\nUsage:\n %s []\n", - argv[0]); - return 0; - } + char *f1,*f2,*f3; + char blank[] = ""; + char errmsg[MAXMSG+1]=""; + int errcode; + int version; + int major; + int minor; + int patch; - // Get version number and display in Major.Minor.Patch format - ENgetversion(&version); - major = version/10000; - minor = (version%10000)/100; - patch = version%100; - printf("\n... Running EPANET Version %d.%d.%d\n", major, minor, patch); + /* get version from DLL and trasform in Major.Minor.Patch format + instead of hardcoded version */ + ENgetversion(&version); + major= version/10000; + minor= (version%10000)/100; + patch= version%100; + printf(FMT01, major, minor, patch); - // Assign pointers to file names - f1 = argv[1]; - if (argc > 2) f2 = argv[2]; // set rptfile name - else f2 = blank; // use stdout for rptfile - if (argc > 3) f3 = argv[3]; // set binary output file name - else f3 = blank; // no binary output file + /* Check for proper number of command line arguments */ + if (argc < 2) { + printf(FMT03, argv[0]); + return(1); + } - // Create a project, run it, and delete it - EN_createproject(&ph); - errcode = EN_runproject(ph, f1, f2, f3, &writeConsole); - EN_deleteproject(&ph); + /* set inputfile name */ + f1 = argv[1]; + if (argc > 2) { + /* set rptfile name */ + f2 = argv[2]; + } + else { + /* use stdout for rptfile */ + f2 = blank; + } + if (argc > 3) { + /* set binary output file name */ + f3 = argv[3]; + } + else { + /* NO binary output*/ + f3 = blank; + } - // Blank out the last progress message - printf("\r "); + /* Call the main control function */ + errcode = ENepanet(f1,f2,f3,NULL); - // Report run's status - if (errcode == 0) - { - printf("\n... EPANET ran successfully.\n"); - } - else if (errcode < 100) - { - printf( - "\n... EPANET ran with warnings - see the Status Report.\n"); - } - else - { - printf( - "\n... EPANET failed with ERROR %d - see the Status Report\n", errcode); - } - return errcode; -} + /* Error/Warning check */ + if (errcode == 0) { + /* no errors */ + printf(FMT09); + return(0); + } + else { + if (errcode > MAXWARNCODE) printf("\n Fatal Error: "); + ENgeterror(errcode, errmsg, MAXMSG); + printf("%s\n", errmsg); + if (errcode > MAXWARNCODE) { + // error // + printf(FMT11); + return(errcode); + } + else { + // warning // + printf(FMT10); + return(0); + } + } +} /* End of main */ From 8514929622454f0ab8659f83aaef859b232db6a4 Mon Sep 17 00:00:00 2001 From: Lew Rossman Date: Sun, 28 Oct 2018 16:58:43 -0400 Subject: [PATCH 7/8] Rewrite of EN_setlinktype function (#305) - Complete rewrite of EN_setlinktype with link index argument passed by reference - New unit test of EN_setlinktype added - New function EN_setlinknodes added --- ReleaseNotes2_2.md | 3 +- include/epanet2.bas | 3 +- include/epanet2.h | 20 ++++++-- include/epanet2.vb | 2 + src/epanet.c | 99 ++++++++++++++++++++++++++++++++---- tests/test_setlinktype.cpp | 100 +++++++++++++++++++++++++++++++++++++ 6 files changed, 211 insertions(+), 16 deletions(-) create mode 100644 tests/test_setlinktype.cpp diff --git a/ReleaseNotes2_2.md b/ReleaseNotes2_2.md index 2b335f3..89adf5d 100644 --- a/ReleaseNotes2_2.md +++ b/ReleaseNotes2_2.md @@ -29,7 +29,7 @@ int runEpanet(char *finp, char *frpt) if (!err) err = EN_solveH(ph); if (!err) err = EN_report(ph); EN_close(ph); - EN_deleteproject(ph); + EN_deleteproject(&ph); return err; } ``` @@ -149,6 +149,7 @@ Both network files are available [here](https://doi.org/10.23719/1375314). |`ENgetruleID`|| |`ENinit`|| |`ENsetheadcurveindex`|| +|`ENsetlinknodes`|| |`ENsetlinktype`|| |`ENaddnode`|| |`ENaddlink`|| diff --git a/include/epanet2.bas b/include/epanet2.bas index c4516f1..8d29b59 100644 --- a/include/epanet2.bas +++ b/include/epanet2.bas @@ -288,7 +288,8 @@ Public Const EN_G_CURVE = 4 ' General\default curve Declare Function ENinit Lib "epanet2.dll" (ByVal rptFile As String, ByVal binOutFile As String, ByVal UnitsType As Long, ByVal HeadlossFormula As Long) As Long Declare Function ENsetheadcurveindex Lib "epanet2.dll" (ByVal pumpIndex As Long, ByVal curveIndex As Long) As Long - Declare Function ENsetlinktype Lib "epanet2.dll" (ByVal index As Long, ByVal code As Long) As Long + Declare Function ENsetlinknodes Lib "epanet2.dll" (ByVal index As Long, ByVal node1 As Long, ByVal node2 As Long) As Long + Declare Function ENsetlinktype Lib "epanet2.dll" (index As Long, ByVal code As Long) As Long Declare Function ENaddnode Lib "epanet2.dll" (ByVal id As String, ByVal nodeType As Long) As Long Declare Function ENaddlink Lib "epanet2.dll" (ByVal id As String, ByVal linkType As Long, ByVal fromNode As String, ByVal toNode As String) As Long Declare Function ENdeletelink Lib "epanet2.dll" (ByVal nodeIndex As Long) As Long diff --git a/include/epanet2.h b/include/epanet2.h index 842875c..2601335 100644 --- a/include/epanet2.h +++ b/include/epanet2.h @@ -743,12 +743,12 @@ extern "C" { /** @brief Set the link type code for a specified link - @param id The id of a link - @param type The type code of the link. + @param[in,out] index The index of a link before [in] and after [out] the type change. + @param code The new type code of the link. @return Error code @see EN_LinkType */ - int DLLEXPORT ENsetlinktype(char *id, EN_LinkType type); + int DLLEXPORT ENsetlinktype(int *index, EN_LinkType code); /** @brief Get the indexes of a link's start- and end-nodes. @@ -872,6 +872,16 @@ extern "C" { */ int DLLEXPORT ENsetlinkid(int index, char *newid); + /** + @brief Set the indexes of a link's start- and end-nodes. + @param index The index of a link (first link is index 1) + @param node1 The index of the link's start node (first node is index 1). + @param node2 The index of the link's end node (first node is index 1). + @return Error code + @see ENsetnodeid, ENsetlinkid + */ + int DLLEXPORT ENsetlinknodes(int index, int node1, int node2); + /** @brief Set a property value for a link. @param index The index of a link. First link is index 1. @@ -1064,7 +1074,6 @@ extern "C" { @see ENgetcurveindex ENsetcurve */ int DLLEXPORT ENaddcurve(char *id); - /** @brief Gets the number of premises, true actions, and false actions and the priority of an existing rule-based control. @@ -1303,7 +1312,6 @@ extern "C" { int DLLEXPORT EN_getlinkindex(EN_ProjectHandle ph, char *id, int *index); int DLLEXPORT EN_getlinkid(EN_ProjectHandle ph, int index, char *id); int DLLEXPORT EN_getlinktype(EN_ProjectHandle ph, int index, EN_LinkType *code); - int DLLEXPORT EN_setlinktype(EN_ProjectHandle ph, char *id, EN_LinkType type); int DLLEXPORT EN_getlinknodes(EN_ProjectHandle ph, int index, int *node1, int *node2); int DLLEXPORT EN_getlinkvalue(EN_ProjectHandle ph, int index, EN_LinkProperty code, EN_API_FLOAT_TYPE *value); int DLLEXPORT EN_getcurve(EN_ProjectHandle ph, int curveIndex, char* id, int *nValues, EN_API_FLOAT_TYPE **xValues, EN_API_FLOAT_TYPE **yValues); @@ -1319,6 +1327,8 @@ extern "C" { int DLLEXPORT EN_setnodeid(EN_ProjectHandle ph, int index, char *newid); int DLLEXPORT EN_setnodevalue(EN_ProjectHandle ph, int index, int code, EN_API_FLOAT_TYPE v); int DLLEXPORT EN_setlinkid(EN_ProjectHandle ph, int index, char *newid); + int DLLEXPORT EN_setlinknodes(EN_ProjectHandle ph, int index, int node1, int node2); + int DLLEXPORT EN_setlinktype(EN_ProjectHandle ph, int *index, EN_LinkType code); int DLLEXPORT EN_setlinkvalue(EN_ProjectHandle ph, int index, int code, EN_API_FLOAT_TYPE v); int DLLEXPORT EN_addpattern(EN_ProjectHandle ph, char *id); int DLLEXPORT EN_setpattern(EN_ProjectHandle ph, int index, EN_API_FLOAT_TYPE *f, int len); diff --git a/include/epanet2.vb b/include/epanet2.vb index 5898dfd..2c6d5f3 100644 --- a/include/epanet2.vb +++ b/include/epanet2.vb @@ -230,6 +230,8 @@ Public Const EN_CUSTOM = 2 ' user-defined custom curve Declare Function ENsetnodeid Lib "epanet2.dll" (ByVal index As Int32, ByVal newid As String) As Int32 Declare Function ENsetnodevalue Lib "epanet2.dll" (ByVal Index As Int32, ByVal Code As Int32, ByVal Value As Single) As Int32 Declare Function ENsetlinkid Lib "epanet2.dll" (ByVal index As Int32, ByVal newid As String) As Int32 + Declare Function ENsetlinknodes Lib "epanet2.dll" (ByVal index As Int32, ByVal node1 As Int32, ByVal node2 As Int32) As Int32 + Declare Function ENsetlinktype Lib "epanet2.dll" (ByRef index As Int32, ByVal code As Int32) As Int32 Declare Function ENsetlinkvalue Lib "epanet2.dll" (ByVal Index As Int32, ByVal Code As Int32, ByVal Value As Single) As Int32 Declare Function ENsetpattern Lib "epanet2.dll" (ByVal Index as Int32, ByRef F as Single, ByVal N as Int32) as Int32 Declare Function ENsetpatternvalue Lib "epanet2.dll" (ByVal Index As Int32, ByVal Period As Int32, ByVal Value As Single) As Int32 diff --git a/src/epanet.c b/src/epanet.c index 74ca6ce..3fc97d9 100644 --- a/src/epanet.c +++ b/src/epanet.c @@ -415,6 +415,14 @@ int DLLEXPORT ENsetlinkid(int index, char *newid) { return EN_setlinkid(_defaultModel, index, newid); } +int DLLEXPORT ENsetlinknodes(int index, int node1, int node2) { + return EN_setlinknodes(_defaultModel, index, node1, node2); +} + +int DLLEXPORT ENsetlinktype(int *index, EN_LinkType type) { + return EN_setlinktype(_defaultModel, index, type); +} + int DLLEXPORT ENsetlinkvalue(int index, int code, EN_API_FLOAT_TYPE v) { return EN_setlinkvalue(_defaultModel, index, code, v); } @@ -567,10 +575,6 @@ int DLLEXPORT ENgetruleID(int indexRule, char* id){ return EN_getruleID(_defaultModel, indexRule, id); } -int DLLEXPORT ENsetlinktype(char *id, EN_LinkType toType) { - return EN_setlinktype(_defaultModel, id, toType); -} - int DLLEXPORT ENaddnode(char *id, EN_NodeType nodeType) { return EN_addnode(_defaultModel, id, nodeType); } @@ -3160,6 +3164,37 @@ int DLLEXPORT EN_setlinkid(EN_ProjectHandle ph, int index, char *newid) return set_error(p->error_handle, 0); } +int DLLEXPORT EN_setlinknodes(EN_ProjectHandle ph, int index, int node1, int node2) +{ + int type; + EN_Project *p = (EN_Project*)ph; + EN_Network *net = &p->network; + + // Check that nodes exist + if (node1 < 0 || node1 > net->Nnodes) return set_error(p->error_handle, 203); + if (node2 < 0 || node2 > net->Nnodes) return set_error(p->error_handle, 203); + + // Check for illegal valve connection + type = net->Link[index].Type; + if (type == EN_PRV || type == EN_PSV || type == EN_FCV) + { + // Can't be connected to a fixed grade node + if (node1 > net->Njuncs || + node2 > net->Njuncs) return set_error(p->error_handle, 219); + + // Can't be connected to another pressure/flow control valve + if (!valvecheck(p, type, node1, node2)) + { + return set_error(p->error_handle, 220); + } + } + + // Assign new end nodes to link + net->Link[index].N1 = node1; + net->Link[index].N2 = node2; + return set_error(p->error_handle, 0); +} + int DLLEXPORT EN_setlinkvalue(EN_ProjectHandle ph, int index, int code, EN_API_FLOAT_TYPE v) @@ -4809,7 +4844,7 @@ int DLLEXPORT EN_setdemandname(EN_ProjectHandle ph, int nodeIndex, int demandIdx } int DLLEXPORT EN_setdemandpattern(EN_ProjectHandle ph, int nodeIndex, int demandIdx, int patIndex) { - + EN_Project *pr = (EN_Project*)ph; EN_Network *net = &pr->network; @@ -4884,7 +4919,52 @@ int DLLEXPORT EN_getaveragepatternvalue(EN_ProjectHandle ph, int index, EN_API_F return set_error(p->error_handle, 0); } -int DLLEXPORT EN_setlinktype(EN_ProjectHandle ph, char *id, EN_LinkType toType) { +int DLLEXPORT EN_setlinktype(EN_ProjectHandle ph, int *index, EN_LinkType type) { + + int i = *index, n1, n2; + char id[MAXID+1]; + char id1[MAXID+1]; + char id2[MAXID+1]; + int errcode; + EN_LinkType oldtype; + EN_Project *p = (EN_Project*)ph; + EN_Network *net = &p->network; + + if (!p->Openflag) return set_error(p->error_handle, 102); + if (type < 0 || type > EN_GPV) return set_error(p->error_handle, 211); + + // Check if a link with the id exists + if (i <= 0 || i > net->Nlinks) return set_error(p->error_handle, 204); + + // Get the current type of the link + EN_getlinktype(p, i, &oldtype); + if (oldtype == type) return set_error(p->error_handle, 0); + + // Pipe changing from or to having a check valve + if (oldtype <= EN_PIPE && type <= EN_PIPE) + { + net->Link[i].Type = type; + if (type == EN_CVPIPE) net->Link[i].Stat = OPEN; + return set_error(p->error_handle, 0); + } + + // Get ID's of link & its end nodes + EN_getlinkid(ph, i, id); + EN_getlinknodes(ph, i, &n1, &n2); + EN_getnodeid(ph, n1, id1); + EN_getnodeid(ph, n2, id2); + + // Delete the original link + EN_deletelink(ph, i); + + // Create a new link of new type and old id + errcode = EN_addlink(ph, id, type, id1, id2); + + // Find the index of this new link + EN_getlinkindex(ph, id, index); + return set_error(p->error_handle, errcode); + + /*********************************************** int i; EN_LinkType fromType; @@ -4895,16 +4975,16 @@ int DLLEXPORT EN_setlinktype(EN_ProjectHandle ph, char *id, EN_LinkType toType) if (!p->Openflag) return set_error(p->error_handle, 102); - /* Check if a link with the id exists */ + // Check if a link with the id exists if (EN_getlinkindex(p, id, &i) != 0) return set_error(p->error_handle, 215); - /* Get the current type of the link */ + // Get the current type of the link EN_getlinktype(p, i, &fromType); if (fromType == toType) return set_error(p->error_handle, 0); - /* Change link from Pipe */ + // Change link from Pipe if (toType <= EN_PIPE) { net->Npipes++; } else if (toType == EN_PUMP) { @@ -4921,6 +5001,7 @@ int DLLEXPORT EN_setlinktype(EN_ProjectHandle ph, char *id, EN_LinkType toType) net->Npumps--; } return set_error(p->error_handle, 0); +**********************************************/ } int DLLEXPORT EN_addnode(EN_ProjectHandle ph, char *id, EN_NodeType nodeType) { diff --git a/tests/test_setlinktype.cpp b/tests/test_setlinktype.cpp new file mode 100644 index 0000000..bf225fe --- /dev/null +++ b/tests/test_setlinktype.cpp @@ -0,0 +1,100 @@ +// Test of ENsetlinktype EPANET API Function +#define _CRT_SECURE_NO_DEPRECATE + +/* +This is a test for the API function that changes a link's type. +Two links in Net1.inp are changed: Pipe 113 is reversed with a CV added +and Pipe 121 is changed to a 100 psi PRV. After running the revised model, +at hour 0 the flow in Pipe 113 should be zero and the pressure at node 31 +of the PRV 121 should be 100. +*/ + +#define BOOST_TEST_MODULE "toolkit" +#include + +#include +#include "epanet2.h" + +#define DATA_PATH_INP "./net1.inp" +#define DATA_PATH_RPT "./test.rpt" +#define DATA_PATH_OUT "./test.out" + +using namespace std; + +BOOST_AUTO_TEST_SUITE (test_toolkit) + +BOOST_AUTO_TEST_CASE(test_setlinktype) +{ + int error = 0; + int p113, n31, p121, n113_1, n113_2; + float q113 = 0.0f, p31 = 0.0f, diam; + + EN_ProjectHandle ph = NULL; + EN_createproject(&ph); + + std::string path_inp = std::string(DATA_PATH_INP); + std::string path_rpt = std::string(DATA_PATH_RPT); + std::string path_out = std::string(DATA_PATH_OUT); + + error = EN_open(ph, path_inp.c_str(), path_rpt.c_str(), ""); + BOOST_REQUIRE(error == 0); + + // Change duration to 0 + error = EN_settimeparam(ph, EN_DURATION, 0); + BOOST_REQUIRE(error == 0); + + // Get indexes of pipe 113 and node 31 + error = EN_getlinkindex(ph, (char *)"113", &p113); + BOOST_REQUIRE(error == 0); + error = EN_getnodeindex(ph, (char *)"31", &n31); + BOOST_REQUIRE(error == 0); + + // Reverse pipe 113 and give it a check valve + error = EN_getlinknodes(ph, p113, &n113_1, &n113_2); + BOOST_REQUIRE(error == 0); + error = EN_setlinknodes(ph, p113, n113_2, n113_1); + BOOST_REQUIRE(error == 0); + error = EN_setlinktype(ph, &p113, EN_CVPIPE); + BOOST_REQUIRE(error == 0); + + // Get index & diameter of pipe 121 connected to node 31 + error = EN_getlinkindex(ph, (char *)"121", &p121); + BOOST_REQUIRE(error == 0); + error = EN_getlinkvalue(ph, p121, EN_DIAMETER, &diam); + BOOST_REQUIRE(error == 0); + + // Replace it with a PRV + error = EN_setlinktype(ph, &p121, EN_PRV); + BOOST_REQUIRE(error == 0); + + // Set diameter & setting of new PRV + error = EN_setlinkvalue(ph, p121, EN_INITSETTING, 100); + BOOST_REQUIRE(error == 0); + error = EN_setlinkvalue(ph, p121, EN_DIAMETER, diam); + BOOST_REQUIRE(error == 0); + + // Solve for hydraulics + error = EN_solveH(ph); + BOOST_REQUIRE(error == 0); + + // Get flow in link 113 and pressure at node 31 + error = EN_getlinkvalue(ph, p113, EN_FLOW, &q113); + BOOST_REQUIRE(error == 0); + error = EN_getnodevalue(ph, n31, EN_PRESSURE, &p31); + BOOST_REQUIRE(error == 0); + + // Require that link 113 flow be 0 + q113 = fabs(q113); + BOOST_REQUIRE(q113 < 0.001); + + // Require that node 31 pressure be 100 + p31 = fabs(p31 - 100.0f); + BOOST_REQUIRE(p31 < 0.001); + + // Close and delete project + error = EN_close(ph); + BOOST_REQUIRE(error == 0); + EN_deleteproject(&ph); +} + +BOOST_AUTO_TEST_SUITE_END() From c2b16a64663608ab3127812f10140c369dce3bd2 Mon Sep 17 00:00:00 2001 From: Lew Rossman Date: Sun, 28 Oct 2018 20:07:54 -0400 Subject: [PATCH 8/8] Deleted previous, commented-out version of EN_setlinktype --- src/epanet.c | 39 --------------------------------------- 1 file changed, 39 deletions(-) diff --git a/src/epanet.c b/src/epanet.c index 3fc97d9..50618cc 100644 --- a/src/epanet.c +++ b/src/epanet.c @@ -4963,45 +4963,6 @@ int DLLEXPORT EN_setlinktype(EN_ProjectHandle ph, int *index, EN_LinkType type) // Find the index of this new link EN_getlinkindex(ph, id, index); return set_error(p->error_handle, errcode); - - /*********************************************** - int i; - EN_LinkType fromType; - - EN_Project *p = (EN_Project*)ph; - - EN_Network *net = &p->network; - - if (!p->Openflag) - return set_error(p->error_handle, 102); - - // Check if a link with the id exists - if (EN_getlinkindex(p, id, &i) != 0) - return set_error(p->error_handle, 215); - - // Get the current type of the link - EN_getlinktype(p, i, &fromType); - if (fromType == toType) - return set_error(p->error_handle, 0); - - // Change link from Pipe - if (toType <= EN_PIPE) { - net->Npipes++; - } else if (toType == EN_PUMP) { - net->Npumps++; - net->Pump[net->Npumps].Link = i; - } else { - net->Nvalves++; - net->Valve[net->Nvalves].Link = i; - } - - if (fromType <= EN_PIPE) { - net->Npipes--; - } else if (fromType == EN_PUMP) { - net->Npumps--; - } - return set_error(p->error_handle, 0); -**********************************************/ } int DLLEXPORT EN_addnode(EN_ProjectHandle ph, char *id, EN_NodeType nodeType) {