From 92cef4692c0199959e5a7985a2f05835b1d12628 Mon Sep 17 00:00:00 2001 From: Sam Hatchett Date: Wed, 27 Jul 2022 14:01:45 -0400 Subject: [PATCH 01/13] Create conanfile.py --- conanfile.py | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 conanfile.py diff --git a/conanfile.py b/conanfile.py new file mode 100644 index 0000000..fb9db09 --- /dev/null +++ b/conanfile.py @@ -0,0 +1,45 @@ +from conans import ConanFile +from conan.tools.cmake import CMakeToolchain, CMake, cmake_layout + +class EpanetConan(ConanFile): + name = "epanet" + version = "2.2" + description = "EPANET is an industry-standard program for modeling the hydraulic and water quality behavior of water distribution system pipe networks." + homepage = "https://github.com/OpenWaterAnalytics/EPANET" + url = "https://github.com/OpenWaterAnalytics/EPANET" + license = "MIT" + + # Binary configuration + settings = "os", "compiler", "build_type", "arch" + options = {"shared": [True, False], "fPIC": [True, False]} + default_options = {"shared": False, "fPIC": True} + + exports_sources = "CMakeLists.txt", "src/*", "include/*", "run/*" + + def config_options(self): + if self.settings.os == "Windows": + del self.options.fPIC + + def layout(self): + cmake_layout(self) + + def generate(self): + tc = CMakeToolchain(self) + tc.generate() + + def build(self): + cmake = CMake(self) + cmake.configure() + cmake.build() + + def package(self): + self.copy("lib/libepanet2.dylib", "lib", keep_path=False) + self.copy("lib/libepanet-output.dylib", "lib", keep_path=False) + self.copy("*.h", "include", "include", keep_path=False) + self.copy("types.h", "include", "src", keep_path=False) + self.copy("hash.h", "include", "src", keep_path=False) + + def package_info(self): + self.cpp_info.libdirs = ["lib"] + self.cpp_info.libs = ["epanet2", "epanet-output"] + self.cpp_info.includedirs = ["include"] From 41887e90168fec96c109225aa011359a0218fea8 Mon Sep 17 00:00:00 2001 From: Sam Hatchett Date: Wed, 27 Jul 2022 16:37:49 -0400 Subject: [PATCH 02/13] adds client callback API function --- include/epanet2_2.h | 12 ++++++++++++ src/epanet.c | 13 +++++++++++++ src/project.c | 2 ++ src/report.c | 6 ++++++ src/types.h | 5 ++++- 5 files changed, 37 insertions(+), 1 deletion(-) diff --git a/include/epanet2_2.h b/include/epanet2_2.h index 18ef127..ddd1db7 100644 --- a/include/epanet2_2.h +++ b/include/epanet2_2.h @@ -517,6 +517,18 @@ typedef struct Project *EN_Project; ********************************************************************/ + /** + @brief Set a user-supplied callback function for reporting + @param ph an EPANET project handle. + @param callback a function pointer with declared signature, which gets called by EPANET for reporting. + @return an error code. + @details The report callback function must have the signature specified - void(void* userData, EN_Project, char*) - + use the userData parameter to pass any client context necessary (a context pointer or wrapper object perhaps). + Leave un-set or set the report callback to NULL to revert to EPANET's default behavior. + **/ + int DLLEXPORT EN_setReportCallback(EN_Project ph, void (*callback)(void *userData, void *EN_projectHandle, char*)); + int DLLEXPORT EN_setReportCallbackUserData(EN_Project ph, void *userData); + /** @brief Writes a line of text to a project's report file. @param ph an EPANET project handle. diff --git a/src/epanet.c b/src/epanet.c index 9a3c194..6c2ba3d 100644 --- a/src/epanet.c +++ b/src/epanet.c @@ -841,6 +841,19 @@ int DLLEXPORT EN_closeQ(EN_Project p) ********************************************************************/ + + int DLLEXPORT EN_setReportCallback(EN_Project p, void (*callback)(void*,void*,char*)) + { + p->report.reportCallback = callback; + return 0; + } + + int DLLEXPORT EN_setReportCallbackUserData(EN_Project p, void *userData) + { + p->report.reportCallbackUserData = userData; + return 0; + } + int DLLEXPORT EN_writeline(EN_Project p, char *line) /*---------------------------------------------------------------- ** Input: line = line of text diff --git a/src/project.c b/src/project.c index b470ec3..40f5639 100644 --- a/src/project.c +++ b/src/project.c @@ -281,6 +281,8 @@ void initpointers(Project *pr) pr->hydraul.smatrix.NZSUB = NULL; pr->hydraul.smatrix.LNZ = NULL; + pr->report.reportCallback = NULL; + initrules(pr); } diff --git a/src/report.c b/src/report.c index 8fc6d16..2922332 100644 --- a/src/report.c +++ b/src/report.c @@ -885,6 +885,12 @@ void writeline(Project *pr, char *s) **-------------------------------------------------------------- */ { + if (pr->report.reportCallback != NULL) + { + pr->report.reportCallback(pr->report.reportCallbackUserData, pr, s); + return; + } + Report *rpt = &pr->report; if (rpt->RptFile == NULL) return; diff --git a/src/types.h b/src/types.h index b0e91b5..27d2fd7 100755 --- a/src/types.h +++ b/src/types.h @@ -629,7 +629,10 @@ typedef struct { Rpt2Fname[MAXFNAME+1], // Secondary report file name DateStamp[26]; // Current date & time - SField Field[MAXVAR]; // Output reporting fields + SField Field[MAXVAR]; // Output reporting fields + + void (*reportCallback)(void*,void*,char*); // user-supplied reporting callback + void *reportCallbackUserData; // user-supplied reporting context } Report; From 0bf09b525fc88e6b151d3180aeb289adb430df57 Mon Sep 17 00:00:00 2001 From: "Marios S. Kyriakou" Date: Thu, 28 Jul 2022 23:37:10 +0300 Subject: [PATCH 03/13] build libs / win & linux --- .github/workflows/ccpp.yml | 6 +++++- .github/workflows/win32.yml | 22 ++++++++++++++++++++++ .github/workflows/win64.yml | 22 ++++++++++++++++++++++ 3 files changed, 49 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/win32.yml create mode 100644 .github/workflows/win64.yml diff --git a/.github/workflows/ccpp.yml b/.github/workflows/ccpp.yml index 4f163f3..9dc80b2 100644 --- a/.github/workflows/ccpp.yml +++ b/.github/workflows/ccpp.yml @@ -1,4 +1,4 @@ -name: C/C++ CI +name: linux on: [push] @@ -17,3 +17,7 @@ jobs: - name: make working-directory: ./buildproducts run: make + - uses: actions/upload-artifact@v2 + with: + name: libepanet-output + path: /home/runner/work/EPANET/EPANET/buildproducts/ diff --git a/.github/workflows/win32.yml b/.github/workflows/win32.yml new file mode 100644 index 0000000..94b09ae --- /dev/null +++ b/.github/workflows/win32.yml @@ -0,0 +1,22 @@ +name: epanet2-win32 + +on: [push] + +jobs: + build: + runs-on: windows-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v2 + + - name: setup_build_dir + run: mkdir buildproducts + - name: cmake + working-directory: ./buildproducts + run: cmake .. -A Win32 && cmake --build . --config Release + + - uses: actions/upload-artifact@v2 + with: + name: epanet2-win32 + path: D:\a\EPANET\EPANET\buildproducts\bin\Release\ diff --git a/.github/workflows/win64.yml b/.github/workflows/win64.yml new file mode 100644 index 0000000..c6802ae --- /dev/null +++ b/.github/workflows/win64.yml @@ -0,0 +1,22 @@ +name: epanet2-win64 + +on: [push] + +jobs: + build: + runs-on: windows-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v2 + + - name: setup_build_dir + run: mkdir buildproducts + - name: cmake + working-directory: ./buildproducts + run: cmake .. -A x64 && cmake --build . --config Release + + - uses: actions/upload-artifact@v2 + with: + name: epanet2-win64 + path: D:\a\EPANET\EPANET\buildproducts\bin\Release\ From 93389ad1e4b22885e45cb69fe2b334ea3096c142 Mon Sep 17 00:00:00 2001 From: Lew Rossman Date: Wed, 3 Aug 2022 19:52:01 -0400 Subject: [PATCH 04/13] Fix for PRV directly downstream of a PSV --- src/hydcoeffs.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/hydcoeffs.c b/src/hydcoeffs.c index 73c38a1..b4ba6cd 100644 --- a/src/hydcoeffs.c +++ b/src/hydcoeffs.c @@ -62,6 +62,7 @@ static void tcvcoeff(Project *pr, int k); static void prvcoeff(Project *pr, int k, int n1, int n2); static void psvcoeff(Project *pr, int k, int n1, int n2); static void fcvcoeff(Project *pr, int k, int n1, int n2); +static void valveconnectcoeffs(Project *pr, int k, int n1, int n2); void resistcoeff(Project *pr, int k) @@ -317,9 +318,11 @@ void valvecoeffs(Project *pr) { case PRV: prvcoeff(pr, k, n1, n2); + valveconnectcoeffs(pr, k, n1, n2); break; case PSV: psvcoeff(pr, k, n1, n2); + valveconnectcoeffs(pr, k, n1, n2); break; case FCV: fcvcoeff(pr, k, n1, n2); @@ -329,6 +332,25 @@ void valvecoeffs(Project *pr) } } +void valveconnectcoeffs(Project *pr, int k, int n1, int n2) +/* +**-------------------------------------------------------------- +** Input: k = valve's link index +** n1 = upstream node of valve +** n2 = downstream node of valve +** Output: none +** Purpose: insures that the off-diagonals in the rows for a +** PRV/PSV are non-zero to preserve connectivity. +**-------------------------------------------------------------- +*/ +{ + Smatrix *sm = &pr->hydraul.smatrix; + double p = 1.0 / CBIG; + sm->Aij[sm->Ndx[k]] -= p; + sm->Aii[sm->Row[n1]] += p; + sm->Aii[sm->Row[n2]] += p; +} + void emittercoeffs(Project *pr) /* From 79b4fa45a15a511d035e1fbb14b4e0736f7a7870 Mon Sep 17 00:00:00 2001 From: Lew Rossman Date: Fri, 5 Aug 2022 16:59:32 -0400 Subject: [PATCH 05/13] Update hydcoeffs.c Only an Active PSV needs to preserve connectivity. --- src/hydcoeffs.c | 24 ++---------------------- 1 file changed, 2 insertions(+), 22 deletions(-) diff --git a/src/hydcoeffs.c b/src/hydcoeffs.c index b4ba6cd..83b2dc3 100644 --- a/src/hydcoeffs.c +++ b/src/hydcoeffs.c @@ -62,7 +62,6 @@ static void tcvcoeff(Project *pr, int k); static void prvcoeff(Project *pr, int k, int n1, int n2); static void psvcoeff(Project *pr, int k, int n1, int n2); static void fcvcoeff(Project *pr, int k, int n1, int n2); -static void valveconnectcoeffs(Project *pr, int k, int n1, int n2); void resistcoeff(Project *pr, int k) @@ -318,11 +317,9 @@ void valvecoeffs(Project *pr) { case PRV: prvcoeff(pr, k, n1, n2); - valveconnectcoeffs(pr, k, n1, n2); break; case PSV: psvcoeff(pr, k, n1, n2); - valveconnectcoeffs(pr, k, n1, n2); break; case FCV: fcvcoeff(pr, k, n1, n2); @@ -332,25 +329,6 @@ void valvecoeffs(Project *pr) } } -void valveconnectcoeffs(Project *pr, int k, int n1, int n2) -/* -**-------------------------------------------------------------- -** Input: k = valve's link index -** n1 = upstream node of valve -** n2 = downstream node of valve -** Output: none -** Purpose: insures that the off-diagonals in the rows for a -** PRV/PSV are non-zero to preserve connectivity. -**-------------------------------------------------------------- -*/ -{ - Smatrix *sm = &pr->hydraul.smatrix; - double p = 1.0 / CBIG; - sm->Aij[sm->Ndx[k]] -= p; - sm->Aii[sm->Row[n1]] += p; - sm->Aii[sm->Row[n2]] += p; -} - void emittercoeffs(Project *pr) /* @@ -1057,6 +1035,8 @@ void psvcoeff(Project *pr, int k, int n1, int n2) { sm->F[j] += hyd->Xflow[n1]; } + sm->Aij[sm->Ndx[k]] -= 1.0 / CBIG; // Preserve connectivity + sm->Aii[j] += 1.0 / CBIG; return; } From b1396841642c8445510ac1710b3da1dd77f93fab Mon Sep 17 00:00:00 2001 From: Sam Hatchett Date: Mon, 8 Aug 2022 11:58:09 -0400 Subject: [PATCH 06/13] fixes non-camelcase convention and adds rich corinthian vintage-style (projectless) function declarations --- include/epanet2.h | 24 ++++++++------ include/epanet2_2.h | 4 +-- src/epanet.c | 76 ++++++++++++++++++++++----------------------- src/epanet2.c | 18 ++++++++--- 4 files changed, 68 insertions(+), 54 deletions(-) diff --git a/include/epanet2.h b/include/epanet2.h index 3779d66..99c0ad0 100644 --- a/include/epanet2.h +++ b/include/epanet2.h @@ -72,9 +72,9 @@ extern "C" { int DLLEXPORT ENopen(const char *inpFile, const char *rptFile, const char *outFile); - + int DLLEXPORT ENgettitle(char *line1, char *line2, char *line3); - + int DLLEXPORT ENsettitle(char *line1, char *line2, char *line3); int DLLEXPORT ENgetcomment(int object, int index, char *comment); @@ -156,9 +156,13 @@ extern "C" { int DLLEXPORT ENgeterror(int errcode, char *errmsg, int maxLen); int DLLEXPORT ENgetstatistic(int type, EN_API_FLOAT_TYPE* value); - + int DLLEXPORT ENgetresultindex(int type, int index, int *value); + int DLLEXPORT ENsetreportcallback(void (*callback)(void *userData, void *EN_projectHandle, char*)); + int DLLEXPORT ENsetreportcallbackuserdata(void *userData); + + /******************************************************************** Analysis Options Functions @@ -235,7 +239,7 @@ extern "C" { char *demandPattern, char *demandName); int DLLEXPORT ENdeletedemand(int nodeIndex, int demandIndex); - + int DLLEXPORT ENgetnumdemands(int nodeIndex, int *numDemands); int DLLEXPORT ENgetdemandindex(int nodeIndex, char *demandName, int *demandIndex); @@ -285,13 +289,13 @@ extern "C" { int DLLEXPORT ENsetpipedata(int index, EN_API_FLOAT_TYPE length, EN_API_FLOAT_TYPE diam, EN_API_FLOAT_TYPE rough, EN_API_FLOAT_TYPE mloss); - + int DLLEXPORT ENgetvertexcount(int index, int *count); - + int DLLEXPORT ENgetvertex(int index, int vertex, double *x, double *y); - + int DLLEXPORT ENsetvertex(int index, int vertex, double x, double y); - + int DLLEXPORT ENsetvertices(int index, double *x, double *y, int count); /******************************************************************** @@ -351,7 +355,7 @@ extern "C" { int DLLEXPORT ENgetcurvelen(int index, int *len); int DLLEXPORT ENgetcurvetype(int index, int *type); - + int DLLEXPORT ENsetcurvetype(int index, int type); int DLLEXPORT ENgetcurvevalue(int curveIndex, int pointIndex, @@ -425,7 +429,7 @@ extern "C" { int DLLEXPORT ENsetelseaction(int ruleIndex, int actionIndex, int linkIndex, int status, EN_API_FLOAT_TYPE setting); - + int DLLEXPORT ENsetrulepriority(int index, EN_API_FLOAT_TYPE priority); #if defined(__cplusplus) diff --git a/include/epanet2_2.h b/include/epanet2_2.h index ddd1db7..852eab2 100644 --- a/include/epanet2_2.h +++ b/include/epanet2_2.h @@ -526,8 +526,8 @@ typedef struct Project *EN_Project; use the userData parameter to pass any client context necessary (a context pointer or wrapper object perhaps). Leave un-set or set the report callback to NULL to revert to EPANET's default behavior. **/ - int DLLEXPORT EN_setReportCallback(EN_Project ph, void (*callback)(void *userData, void *EN_projectHandle, char*)); - int DLLEXPORT EN_setReportCallbackUserData(EN_Project ph, void *userData); + int DLLEXPORT EN_setreportcallback(EN_Project ph, void (*callback)(void *userData, void *EN_projectHandle, char*)); + int DLLEXPORT EN_setreportcallbackuserdata(EN_Project ph, void *userData); /** @brief Writes a line of text to a project's report file. diff --git a/src/epanet.c b/src/epanet.c index 6c2ba3d..4f026da 100644 --- a/src/epanet.c +++ b/src/epanet.c @@ -842,13 +842,13 @@ int DLLEXPORT EN_closeQ(EN_Project p) ********************************************************************/ - int DLLEXPORT EN_setReportCallback(EN_Project p, void (*callback)(void*,void*,char*)) + int DLLEXPORT EN_setreportcallback(EN_Project p, void (*callback)(void*,void*,char*)) { p->report.reportCallback = callback; return 0; } - int DLLEXPORT EN_setReportCallbackUserData(EN_Project p, void *userData) + int DLLEXPORT EN_setreportcallbackuserdata(EN_Project p, void *userData) { p->report.reportCallbackUserData = userData; return 0; @@ -1787,9 +1787,9 @@ int DLLEXPORT EN_addnode(EN_Project p, char *id, int nodeType, int *index) // Check if a node with same id already exists if (EN_getnodeindex(p, id, &i) == 0) return 215; - + // Check for valid node type - if (nodeType < EN_JUNCTION || nodeType > EN_TANK) return 251; + if (nodeType < EN_JUNCTION || nodeType > EN_TANK) return 251; // Grow node-related arrays to accomodate the new node size = (net->Nnodes + 2) * sizeof(Snode); @@ -1810,7 +1810,7 @@ int DLLEXPORT EN_addnode(EN_Project p, char *id, int nodeType, int *index) hashtable_update(net->NodeHashTable, net->Node[i].ID, i + 1); net->Node[i + 1] = net->Node[i]; } - + // set index of new Junction node net->Njuncs++; nIdx = net->Njuncs; @@ -2267,20 +2267,20 @@ int DLLEXPORT EN_getnodevalue(EN_Project p, int index, int property, double *val if (Node[index].Type != TANK) return 0; v = Tank[index - nJuncs].CanOverflow; break; - + case EN_DEMANDDEFICIT: if (index > nJuncs) return 0; // After an analysis, DemandFlow contains node's required demand // while NodeDemand contains delivered demand + emitter flow if (hyd->DemandFlow[index] < 0.0) return 0; - v = (hyd->DemandFlow[index] - + v = (hyd->DemandFlow[index] - (hyd->NodeDemand[index] - hyd->EmitterFlow[index])) * Ucf[FLOW]; break; - + case EN_NODE_INCONTROL: v = (double)incontrols(p, NODE, index); break; - + default: return 251; } @@ -2496,7 +2496,7 @@ int DLLEXPORT EN_setnodevalue(EN_Project p, int index, int property, double valu Tank[j].Vmin = tankvolume(p, j, Tank[j].Hmin); // new min. volume Tank[j].V0 = tankvolume(p, j, Tank[j].H0); // new init. volume Tank[j].Vmax = tankvolume(p, j, Tank[j].Hmax); // new max. volume - Tank[j].A = (curve->Y[n] - curve->Y[0]) / // nominal area + Tank[j].A = (curve->Y[n] - curve->Y[0]) / // nominal area (curve->X[n] - curve->X[0]); break; @@ -3806,7 +3806,7 @@ int DLLEXPORT EN_getlinkvalue(EN_Project p, int index, int property, double *val v = (double)Pump[findpump(&p->network, index)].Epat; } break; - + case EN_GPV_CURVE: if (Link[index].Type == GPV) { @@ -3817,7 +3817,7 @@ int DLLEXPORT EN_getlinkvalue(EN_Project p, int index, int property, double *val case EN_LINK_INCONTROL: v = (double)incontrols(p, LINK, index); break; - + default: return 251; } @@ -4024,7 +4024,7 @@ int DLLEXPORT EN_setlinkvalue(EN_Project p, int index, int property, double valu net->Pump[pumpIndex].Epat = patIndex; } break; - + case EN_GPV_CURVE: if (Link[index].Type == GPV) { @@ -4090,20 +4090,20 @@ int DLLEXPORT EN_getvertexcount(EN_Project p, int index, int *count) */ { Network *net = &p->network; - + Slink *Link = net->Link; Pvertices vertices; - + // Check that link exists *count = 0; if (!p->Openflag) return 102; if (index <= 0 || index > net->Nlinks) return 204; - + // Set count to number of vertices vertices = Link[index].Vertices; if (vertices) *count = vertices->Npts; return 0; -} +} int DLLEXPORT EN_getvertex(EN_Project p, int index, int vertex, double *x, double *y) /*---------------------------------------------------------------- @@ -4117,22 +4117,22 @@ int DLLEXPORT EN_getvertex(EN_Project p, int index, int vertex, double *x, doubl */ { Network *net = &p->network; - + Slink *Link = net->Link; Pvertices vertices; - + // Check that link exists *x = MISSING; *y = MISSING; if (!p->Openflag) return 102; if (index <= 0 || index > net->Nlinks) return 204; - + // Check that vertex exists vertices = Link[index].Vertices; if (vertices == NULL) return 255; if (vertex <= 0 || vertex > vertices->Npts) return 255; *x = vertices->X[vertex - 1]; - *y = vertices->Y[vertex - 1]; + *y = vertices->Y[vertex - 1]; return 0; } @@ -4148,23 +4148,23 @@ int DLLEXPORT EN_setvertex(EN_Project p, int index, int vertex, double x, double */ { Network *net = &p->network; - + Slink *Link = net->Link; Pvertices vertices; - + // Check that link exists if (!p->Openflag) return 102; if (index <= 0 || index > net->Nlinks) return 204; - + // Check that vertex exists vertices = Link[index].Vertices; if (vertices == NULL) return 255; if (vertex <= 0 || vertex > vertices->Npts) return 255; vertices->X[vertex - 1] = x; - vertices->Y[vertex - 1] = y; + vertices->Y[vertex - 1] = y; return 0; } - + int DLLEXPORT EN_setvertices(EN_Project p, int index, double *x, double *y, int count) /*---------------------------------------------------------------- ** Input: index = link index @@ -4177,11 +4177,11 @@ int DLLEXPORT EN_setvertices(EN_Project p, int index, double *x, double *y, int */ { Network *net = &p->network; - + Slink *link; int i; int err = 0; - + // Check that link exists if (!p->Openflag) return 102; if (index <= 0 || index > net->Nlinks) return 204; @@ -4189,7 +4189,7 @@ int DLLEXPORT EN_setvertices(EN_Project p, int index, double *x, double *y, int // Delete existing set of vertices freelinkvertices(link); - + // Add each new vertex to the link for (i = 0; i < count; i++) { @@ -4198,7 +4198,7 @@ int DLLEXPORT EN_setvertices(EN_Project p, int index, double *x, double *y, int } if (err) freelinkvertices(link); return err; -} +} /******************************************************************** @@ -4282,16 +4282,16 @@ int DLLEXPORT EN_setheadcurveindex(EN_Project p, int linkIndex, int curveIndex) pump = &p->network.Pump[pumpIndex]; oldCurveIndex = pump->Hcurve; newCurveType = p->network.Curve[curveIndex].Type; - + // Assign the new curve to the pump pump->Ptype = NOCURVE; pump->Hcurve = curveIndex; if (curveIndex == 0) return 0; - + // Update the pump's head curve parameters (which also changes // the new curve's Type to PUMP_CURVE) err = updatepumpparams(p, pumpIndex); - + // If the parameter updating failed (new curve was not a valid pump curve) // restore the pump's original curve and its parameters if (err > 0) @@ -4301,8 +4301,8 @@ int DLLEXPORT EN_setheadcurveindex(EN_Project p, int linkIndex, int curveIndex) pump->Hcurve = oldCurveIndex; if (oldCurveIndex == 0) return err; updatepumpparams(p, pumpIndex); - } - + } + // Convert the units of the updated pump parameters to feet and cfs if (pump->Ptype == POWER_FUNC) { @@ -4791,7 +4791,7 @@ int DLLEXPORT EN_setcurvetype(EN_Project p, int index, int type) Network *net = &p->network; if (!p->Openflag) return 102; if (index < 1 || index > net->Ncurves) return 206; - if (type < 0 || type > EN_GENERIC_CURVE) return 251; + if (type < 0 || type > EN_GENERIC_CURVE) return 251; net->Curve[index].Type = type; return 0; } @@ -4865,7 +4865,7 @@ int DLLEXPORT EN_setcurvevalue(EN_Project p, int curveIndex, int pointIndex, // Insert new point into curve curve->X[n] = x; curve->Y[n] = y; - + // Adjust parameters for pumps using curve as a head curve return adjustpumpparams(p, curveIndex); } @@ -4939,7 +4939,7 @@ int DLLEXPORT EN_setcurve(EN_Project p, int index, double *xValues, curve->X[j] = xValues[j]; curve->Y[j] = yValues[j]; } - + // Adjust parameters for pumps using curve as a head curve return adjustpumpparams(p, index); } diff --git a/src/epanet2.c b/src/epanet2.c index 1706936..5618550 100644 --- a/src/epanet2.c +++ b/src/epanet2.c @@ -212,6 +212,16 @@ int DLLEXPORT ENsetstatusreport(int level) return EN_setstatusreport(_defaultProject, level); } +int DLLEXPORT ENsetreportcallback(void (*callback)(void *userData, void *EN_projectHandle, char*)) +{ + return EN_setreportcallback(_defaultProject, callback); +} + +int DLLEXPORT ENsetreportcallbackuserdata(void *userData) +{ + return EN_setreportcallbackuserdata(_defaultProject, userData); +} + int DLLEXPORT ENgetversion(int *version) { return EN_getversion(version); } int DLLEXPORT ENgeterror(int errcode, char *errmsg, int maxLen) @@ -516,7 +526,7 @@ int DLLEXPORT ENgetvertexcount(int index, int *count) { return EN_getvertexcount(_defaultProject, index, count); } - + int DLLEXPORT ENgetvertex(int index, int vertex, double *x, double *y) { return EN_getvertex(_defaultProject, index, vertex, x, y); @@ -530,7 +540,7 @@ int DLLEXPORT ENsetvertex(int index, int vertex, double x, double y) int DLLEXPORT ENsetvertices(int index, double *x, double *y, int count) { return EN_setvertices(_defaultProject, index, x, y, count); -} +} /******************************************************************** @@ -714,10 +724,10 @@ int DLLEXPORT ENsetcurve(int index, EN_API_FLOAT_TYPE *xValues, double *xx = NULL; double *yy = NULL; int i, errcode = 0; - + if (xValues == NULL || yValues == NULL) return 206; if (nPoints < 1) return 202; - + xx = (double *)calloc(nPoints, sizeof(double)); yy = (double *)calloc(nPoints, sizeof(double)); if (xx && yy) From 2d783041a4506a1e3e0ddb35c9aa0aa375b4ac5f Mon Sep 17 00:00:00 2001 From: Lew Rossman Date: Mon, 8 Aug 2022 18:08:45 -0400 Subject: [PATCH 07/13] Fix for FCV problem This update prevents a solution where the computed loss coefficient for a FCV at its flow setting would be less than the user supplied fully opened coefficient. --- ReleaseNotes2_3.md | 2 ++ src/hydstatus.c | 11 ++++++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/ReleaseNotes2_3.md b/ReleaseNotes2_3.md index 3e3f924..7042988 100644 --- a/ReleaseNotes2_3.md +++ b/ReleaseNotes2_3.md @@ -22,6 +22,8 @@ This document describes the changes and updates that have been made in version 2 - changing the absolute tolerance used to compare the closeness of test results to benchmark values from 0 to 0.0001 - dropping the "correct decimal digits" test - dropping the check for identical status report content since it prevents accepting code changes that produce more accurate solutions in fewer iterations. + - A possible loss of network connectivity when evaluating a Pressure Sustaining Valve was prevented. + - Having the implied loss coefficient for an active Flow Control Valve be less than its fully opened value was prevented. diff --git a/src/hydstatus.c b/src/hydstatus.c index 4013ec9..d0f1d02 100644 --- a/src/hydstatus.c +++ b/src/hydstatus.c @@ -7,7 +7,7 @@ Description: updates hydraulic status of network elements Authors: see AUTHORS Copyright: see AUTHORS License: see LICENSE -Last Updated: 05/15/2019 +Last Updated: 08/08/2022 ****************************************************************************** */ @@ -394,6 +394,15 @@ StatusType fcvstatus(Project *pr, int k, StatusType s, double h1, double h2) { status = ACTIVE; } + + // Active valve's loss coeff. can't be < fully open loss coeff. + else if (status == ACTIVE) + { + if ((h1 - h2) / SQR(hyd->LinkFlow[k] < pr->network.Link[k].Km) + { + status = XFCV; + } + } return status; } From 6ccddb811d205ea4ec0fd0fce1c7b1b0acf7597d Mon Sep 17 00:00:00 2001 From: Lew Rossman Date: Mon, 8 Aug 2022 18:37:07 -0400 Subject: [PATCH 08/13] Update hydstatus.c --- src/hydstatus.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hydstatus.c b/src/hydstatus.c index d0f1d02..f3cae46 100644 --- a/src/hydstatus.c +++ b/src/hydstatus.c @@ -398,7 +398,7 @@ StatusType fcvstatus(Project *pr, int k, StatusType s, double h1, double h2) // Active valve's loss coeff. can't be < fully open loss coeff. else if (status == ACTIVE) { - if ((h1 - h2) / SQR(hyd->LinkFlow[k] < pr->network.Link[k].Km) + if ((h1 - h2) / SQR(hyd->LinkFlow[k]) < pr->network.Link[k].Km) { status = XFCV; } From 3287cc3b849762ba44eb9a3a790c9e6ef40f8f74 Mon Sep 17 00:00:00 2001 From: Elad Salomons Date: Sun, 14 Aug 2022 09:02:20 +0300 Subject: [PATCH 09/13] Remove travis-ci.org badge Closes #693 --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 65fa407..513ce49 100755 --- a/README.md +++ b/README.md @@ -3,7 +3,6 @@ OWA-EPANET ## Build Status [![Build status](https://ci.appveyor.com/api/projects/status/19wpg4g2cmj3oihl?svg=true)](https://ci.appveyor.com/project/OpenWaterAnalytics/epanet) -[![Build Status](https://travis-ci.org/OpenWaterAnalytics/EPANET.svg)](https://travis-ci.org/OpenWaterAnalytics/EPANET) [![codecov](https://codecov.io/gh/OpenWaterAnalytics/EPANET/branch/master/graph/badge.svg)](https://codecov.io/gh/OpenWaterAnalytics/EPANET) From 3485551f221b5c93dde87cc626d7f1837b0e5341 Mon Sep 17 00:00:00 2001 From: Elad Salomons Date: Sun, 14 Aug 2022 09:06:44 +0300 Subject: [PATCH 10/13] Create workflow build bagges As suggested in #690 --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 513ce49..af87356 100755 --- a/README.md +++ b/README.md @@ -6,6 +6,10 @@ OWA-EPANET [![codecov](https://codecov.io/gh/OpenWaterAnalytics/EPANET/branch/master/graph/badge.svg)](https://codecov.io/gh/OpenWaterAnalytics/EPANET) +[![linux](https://github.com/OpenWaterAnalytics/EPANET/actions/workflows/ccpp.yml/badge.svg)](https://github.com/OpenWaterAnalytics/EPANET/actions/workflows/ccpp.yml) +[![epanet2-win32](https://github.com/OpenWaterAnalytics/EPANET/actions/workflows/win32.yml/badge.svg)](https://github.com/OpenWaterAnalytics/EPANET/actions/workflows/win32.yml) +[![epanet2-win64](https://github.com/OpenWaterAnalytics/EPANET/actions/workflows/win64.yml/badge.svg)](https://github.com/OpenWaterAnalytics/EPANET/actions/workflows/win64.yml) + ## DESCRIPTION **EPANET** is an industry-standard program for modeling the hydraulic and water quality behavior of water distribution system pipe networks. The EPANET Programmer's Toolkit is a library of functions (or API) written in C that allow programmers to customize the use of EPANET's solution engine for their own applications. Both EPANET and its toolkit were originally developed by the U.S. Environmental Protection Agency (USEPA). If you are interested in using/extending the EPANET engine and its API for academic, personal, or commercial use, then you've come to the right place. [Read more about EPANET on Wikipedia](https://en.wikipedia.org/wiki/EPANET). (Please note that this project covers only the EPANET hydraulic and water quality solver engine, not the graphical user interface.) From 6db17193f51149e1c4659d473f9a90a801d21e37 Mon Sep 17 00:00:00 2001 From: Lew Rossman Date: Tue, 16 Aug 2022 11:47:44 -0400 Subject: [PATCH 11/13] New positional control valve added --- ReleaseNotes2_3.md | 1 + doc/input-file.dox | 12 +++- include/epanet2_enums.h | 11 ++-- src/enumstxt.h | 5 +- src/epanet.c | 24 +++++++- src/funcs.h | 3 +- src/hydcoeffs.c | 121 +++++++++++++++++++++++++++++++++++++++- src/hydraul.c | 4 +- src/inpfile.c | 7 ++- src/input3.c | 20 ++++++- src/output.c | 3 +- src/project.c | 28 +++++++--- src/text.h | 3 +- src/types.h | 9 ++- tests/CMakeLists.txt | 1 + tests/test_valve.cpp | 73 ++++++++++++++++++++++++ 16 files changed, 292 insertions(+), 33 deletions(-) create mode 100644 tests/test_valve.cpp diff --git a/ReleaseNotes2_3.md b/ReleaseNotes2_3.md index 7042988..5a3b2d3 100644 --- a/ReleaseNotes2_3.md +++ b/ReleaseNotes2_3.md @@ -24,6 +24,7 @@ This document describes the changes and updates that have been made in version 2 - dropping the check for identical status report content since it prevents accepting code changes that produce more accurate solutions in fewer iterations. - A possible loss of network connectivity when evaluating a Pressure Sustaining Valve was prevented. - Having the implied loss coefficient for an active Flow Control Valve be less than its fully opened value was prevented. + - A new type of valve, a Positional Control Valve (PCV), was added that uses a valve characteristic curve to relate its loss coefficient to its fraction open setting. diff --git a/doc/input-file.dox b/doc/input-file.dox index 8bbe5c1..c0b69f5 100644 --- a/doc/input-file.dox +++ b/doc/input-file.dox @@ -92,9 +92,10 @@ __Remarks:__ - Head v. Flow for pumps - Efficiency v. Flow for pumps - Volume v. Depth for tanks + - Fraction of fully open flow v. fraction open for Positional Control Valves - Head Loss v. Flow for General Purpose Valves 2. The points of a curve must be entered in order of increasing X-values (lower to higher). -3. If the input file will be used with the Windows version of EPANET, then adding a comment which contains the curve type and description, separated by a colon, directly above the first entry for a curve will ensure that these items appear correctly in EPANET's Curve Editor. Curve types include PUMP, EFFICIENCY, VOLUME, and HEADLOSS. See the examples below. +3. If the input file will be used with the Windows version of EPANET, then adding a comment which contains the curve type and description, separated by a colon, directly above the first entry for a curve will ensure that these items appear correctly in EPANET's Curve Editor. Curve types include PUMP, EFFICIENCY, VOLUME, VALVE, and HEADLOSS. See the examples below. __Example:__ ``` @@ -1028,7 +1029,8 @@ One line for each valve containing: - Diameter, inches (mm) - Valve type - Valve setting -- Minor loss coefficient +- Minor loss coefficient when fully open +- ID of valve characteristic curve (PCVs only) __Remarks:__ 1. Valve types and settings include: @@ -1038,9 +1040,13 @@ __Remarks:__ |PSV (pressure sustaining valve) | Pressure, psi (m) | |PBV (pressure breaker valve) | Pressure, psi (m) | |FCV (flow control valve) | Flow (flow units) | -|TCV (throttle control valve) | Loss Coefficient | +|TCV (throttle control valve) | Partially open loss coefficient | +|PCV (positional control valve) | Fraction open | |GPV (general purpose valve) | ID of head loss curve | 2. Shutoff valves and check valves are considered to be part of a pipe, not a separate control valve component (see @ref PipesPage). +3. The loss coefficient setting for a TCV should not be less than its fully open loss coefficient. +4. The characteristic curve for a PCV relates the valve's fraction of fully open flow to the fraction open. +5. The head loss curve for a GPV relates head loss across the valve to the flow rate through it. */ /** diff --git a/include/epanet2_enums.h b/include/epanet2_enums.h index a000b0c..b3347bc 100644 --- a/include/epanet2_enums.h +++ b/include/epanet2_enums.h @@ -9,7 +9,7 @@ Authors: see AUTHORS Copyright: see AUTHORS License: see LICENSE - Last Updated: 02/01/2020 + Last Updated: 08/13/2022 ****************************************************************************** */ @@ -97,7 +97,8 @@ typedef enum { EN_PUMP_ECOST = 21, //!< Pump average energy price EN_PUMP_EPAT = 22, //!< Pump energy price time pattern index EN_LINK_INCONTROL = 23, //!< Is present in any simple or rule-based control (= 1) or not (= 0) - EN_GPV_CURVE = 24 //!< GPV head loss v. flow curve index + EN_GPV_CURVE = 24, //!< GPV head loss v. flow curve index + EN_PCV_CURVE = 25 //!< PCV loss coeff. curve index } EN_LinkProperty; /// Time parameters @@ -191,7 +192,8 @@ typedef enum { EN_PBV = 5, //!< Pressure breaker valve EN_FCV = 6, //!< Flow control valve EN_TCV = 7, //!< Throttle control valve - EN_GPV = 8 //!< General purpose valve + EN_GPV = 8, //!< General purpose valve + EN_PCV = 9 //!< Positional control valve } EN_LinkType; /// Link status @@ -397,7 +399,8 @@ typedef enum { EN_PUMP_CURVE = 1, //!< Pump head v. flow curve EN_EFFIC_CURVE = 2, //!< Pump efficiency v. flow curve EN_HLOSS_CURVE = 3, //!< Valve head loss v. flow curve - EN_GENERIC_CURVE = 4 //!< Generic curve + EN_GENERIC_CURVE = 4, //!< Generic curve + EN_VALVE_CURVE = 5 //!< Valve loss coeff. v. frac. open } EN_CurveType; /// Deletion action codes diff --git a/src/enumstxt.h b/src/enumstxt.h index fdb9311..ccc12e8 100755 --- a/src/enumstxt.h +++ b/src/enumstxt.h @@ -7,7 +7,7 @@ Authors: see AUTHORS Copyright: see AUTHORS License: see LICENSE - Last Updated: 06/20/2019 + Last Updated: 08/13/2022 ****************************************************************************** */ @@ -27,7 +27,8 @@ char *LinkTxt[] = {w_CV, w_PBV, w_FCV, w_TCV, - w_GPV}; + w_GPV, + w_PCV}; char *StatTxt[] = {t_XHEAD, t_TEMPCLOSED, diff --git a/src/epanet.c b/src/epanet.c index 9a3c194..1635b36 100644 --- a/src/epanet.c +++ b/src/epanet.c @@ -7,7 +7,7 @@ Authors: see AUTHORS Copyright: see AUTHORS License: see LICENSE - Last Updated: 11/08/2020 + Last Updated: 08/13/2022 ****************************************************************************** */ @@ -3149,7 +3149,7 @@ int DLLEXPORT EN_addlink(EN_Project p, char *id, int linkType, if (EN_getlinkindex(p, id, &i) == 0) return 215; // Check for valid link type - if (linkType < CVPIPE || linkType > GPV) return 251; + if (linkType < CVPIPE || linkType > PCV) return 251; // Lookup the link's from and to nodes n1 = hashtable_find(net->NodeHashTable, fromNode); @@ -3209,6 +3209,7 @@ int DLLEXPORT EN_addlink(EN_Project p, char *id, int linkType, size = (net->Nvalves + 1) * sizeof(Svalve); net->Valve = (Svalve *)realloc(net->Valve, size); net->Valve[net->Nvalves].Link = n; + net->Valve[net->Nvalves].Curve = 0; } link->Type = linkType; @@ -3468,7 +3469,7 @@ int DLLEXPORT EN_setlinktype(EN_Project p, int *index, int linkType, int actionC if (p->hydraul.OpenHflag || p->quality.OpenQflag) return 262; // Check for valid input parameters - if (linkType < 0 || linkType > GPV || actionCode < EN_UNCONDITIONAL || + if (linkType < 0 || linkType > PCV || actionCode < EN_UNCONDITIONAL || actionCode > EN_CONDITIONAL) { return 251; @@ -3794,6 +3795,13 @@ int DLLEXPORT EN_getlinkvalue(EN_Project p, int index, int property, double *val } break; + case EN_PCV_CURVE: + if (Link[index].Type == PCV) + { + v = net->Valve[findvalve(&p->network, index)].Curve; + } + break; + case EN_GPV_CURVE: if (Link[index].Type == GPV) { @@ -3916,6 +3924,7 @@ int DLLEXPORT EN_setlinkvalue(EN_Project p, int index, int property, double valu value /= Ucf[FLOW]; break; case TCV: + case PCV: break; case GPV: return 207; // Cannot modify setting for GPV @@ -4012,6 +4021,15 @@ int DLLEXPORT EN_setlinkvalue(EN_Project p, int index, int property, double valu } break; + case EN_PCV_CURVE: + if (Link[index].Type == PCV) + { + curveIndex = ROUND(value); + if (curveIndex < 0 || curveIndex > net->Ncurves) return 206; + net->Valve[findvalve(&p->network, index)].Curve = curveIndex; + } + break; + case EN_GPV_CURVE: if (Link[index].Type == GPV) { diff --git a/src/funcs.h b/src/funcs.h index 366e5c7..f3e5ea4 100755 --- a/src/funcs.h +++ b/src/funcs.h @@ -7,7 +7,7 @@ Authors: see AUTHORS Copyright: see AUTHORS License: see LICENSE - Last Updated: 02/03/2020 + Last Updated: 08/13/2022 ****************************************************************************** */ #ifndef FUNCS_H @@ -166,6 +166,7 @@ void headlosscoeffs(Project *); void matrixcoeffs(Project *); void emitterheadloss(Project *, int, double *, double *); void demandheadloss(Project *, int, double, double, double *, double *); +double pcvlosscoeff(Project *, int, double); // ------- QUALITY.C -------------------- diff --git a/src/hydcoeffs.c b/src/hydcoeffs.c index 83b2dc3..b45f032 100644 --- a/src/hydcoeffs.c +++ b/src/hydcoeffs.c @@ -7,7 +7,7 @@ Authors: see AUTHORS Copyright: see AUTHORS License: see LICENSE - Last Updated: 03/30/2022 + Last Updated: 08/13/2022 ****************************************************************************** */ @@ -36,6 +36,7 @@ const double CBIG = 1.e8; // Exported functions //void resistcoeff(Project *, int ); +//double pcvlosscoeff(Project *, int, double); //void headlosscoeffs(Project *); //void matrixcoeffs(Project *); //void emitterheadloss(Project *, int, double *, double *); @@ -59,6 +60,7 @@ static void valvecoeff(Project *pr, int k); static void gpvcoeff(Project *pr, int k); static void pbvcoeff(Project *pr, int k); static void tcvcoeff(Project *pr, int k); +static void pcvcoeff(Project *pr, int k); static void prvcoeff(Project *pr, int k, int n1, int n2); static void psvcoeff(Project *pr, int k, int n1, int n2); static void fcvcoeff(Project *pr, int k, int n1, int n2); @@ -107,6 +109,10 @@ void resistcoeff(Project *pr, int k) case PUMP: link->R = CBIG; break; + + case PCV: + link->R = pcvlosscoeff(pr, k, link->Kc); + break; // ... For all other links (e.g. valves) use a small resistance default: @@ -116,6 +122,86 @@ void resistcoeff(Project *pr, int k) } +double pcvlosscoeff(Project* pr, int k, double s) +/* +**-------------------------------------------------------------- +** Input: k = link index +** s = valve fraction open setting +** Output: returns a valve loss coefficient +** Purpose: finds a Positional Control Valve's loss +** coefficient from its fraction open setting. +**-------------------------------------------------------------- +*/ +{ + Network* net = &pr->network; + + int v = findvalve(net, k); // valve index + int c = net->Valve[v].Curve; // Kv curve index + double d; // valve diameter + double kmo; // fully open loss coeff. + double km; // partly open loss coeff. + double kvr; // Kv / Kvo (Kvo = Kv at fully open) + double *x, *y; // points on kvr v. frac. open curve + int k1, k2, npts; + Scurve *curve; + + // Valve has no setting so return 0 + if (s == MISSING) return 0.0; + + // Valve is completely open so return its Km value + d = net->Link[k].Diam; + kmo = net->Link[k].Km; + if (s >= 1.0) return kmo; + + // Valve is completely closed so return a large coeff. + if (s <= 0.0) return CBIG; + + // Valve has no assigned curve so assume a linear one + if (c == 0) kvr = s; + + else + { + // Valve curve data + curve = &net->Curve[c]; + npts = curve->Npts; + x = curve->X; // x = frac. open + y = curve->Y; // y = Kv / Kvo + + // s lies below first point of curve + if (s < x[0]) + kvr = s / x[0] * y[0]; + + // s lies above last point of curve + else if (s > x[npts-1]) + { + k2 = npts - 1; + kvr = (s - x[k2]) / (1. - x[k2]) * (1. - y[k2]) + y[k2]; + } + + // Otherwise interpolate over curve segment that brackets s + else + { + k2 = 0; + while (k2 < npts && x[k2] < s) k2++; + if (k2 == 0) k2++; + else if (k2 == npts) k2--; + k1 = k2 - 1; + kvr = (y[k2] - y[k1]) / (x[k2] - x[k1]); + kvr = y[k1] + kvr * (s - x[k1]); + } + } + + // kvr can't be > 1 or <= 0 + kvr = MIN(kvr, 1.0); + kvr = MAX(kvr, CSMALL); + + // Convert from Kv ratio to minor loss coeff. + km = kmo / (kvr * kvr); + km = MIN(km, CBIG); + return km; +} + + void headlosscoeffs(Project *pr) /* **-------------------------------------------------------------- @@ -148,6 +234,9 @@ void headlosscoeffs(Project *pr) case TCV: tcvcoeff(pr, k); break; + case PCV: + pcvcoeff(pr, k); + break; case GPV: gpvcoeff(pr, k); break; @@ -945,6 +1034,36 @@ void tcvcoeff(Project *pr, int k) } +void pcvcoeff(Project *pr, int k) +/* +**-------------------------------------------------------------- +** Input: k = link index +** Output: none +** Purpose: computes P & Y coeffs. for positional control valve +**-------------------------------------------------------------- +*/ +{ + double km; + Hydraul *hyd = &pr->hydraul; + Slink *link = &pr->network.Link[k]; + + // Save original loss coeff. for open valve + km = link->Km; + + // If valve not fixed OPEN or CLOSED, compute its loss coeff. + if (hyd->LinkSetting[k] != MISSING) + { + link->Km = link->R; + } + + // Then apply usual valve formula + valvecoeff(pr, k); + + // Restore original loss coeff. + link->Km = km; +} + + void prvcoeff(Project *pr, int k, int n1, int n2) /* **-------------------------------------------------------------- diff --git a/src/hydraul.c b/src/hydraul.c index 36cd67b..fe9999a 100755 --- a/src/hydraul.c +++ b/src/hydraul.c @@ -7,7 +7,7 @@ Authors: see AUTHORS Copyright: see AUTHORS License: see LICENSE - Last Updated: 03/19/2022 + Last Updated: 08/13/2022 ****************************************************************************** */ @@ -450,6 +450,7 @@ void setlinksetting(Project *pr, int index, double value, StatusType *s, else { if (*k == MISSING && *s <= CLOSED) *s = OPEN; + if (t == PCV) link->R = pcvlosscoeff(pr, index, link->Kc); *k = value; } } @@ -605,6 +606,7 @@ int controls(Project *pr) { hyd->LinkStatus[k] = s2; hyd->LinkSetting[k] = k2; + if (link->Type == PCV) link->R = pcvlosscoeff(pr, k, k2); if (pr->report.Statflag) writecontrolaction(pr,k,i); setsum++; } diff --git a/src/inpfile.c b/src/inpfile.c index bc5d4e6..c1fcc48 100644 --- a/src/inpfile.c +++ b/src/inpfile.c @@ -7,7 +7,7 @@ Description: saves network data to an EPANET formatted text file Authors: see AUTHORS Copyright: see AUTHORS License: see LICENSE -Last Updated: 10/29/2019 +Last Updated: 08/13/2022 ****************************************************************************** */ @@ -312,6 +312,11 @@ int saveinpfile(Project *pr, const char *fname) { sprintf(s1, "%-31s %12.4f", net->Curve[j].ID, km); } + // For PCV add loss curve if present + else if (link->Type == PCV && (j = net->Valve[i].Curve) > 0) + { + sprintf(s1, "%12.4f %12.4f %-31s", kc, km, net->Curve[j].ID); + } else sprintf(s1, "%12.4f %12.4f", kc, km); fprintf(f, "\n%s %s", s, s1); if (link->Comment) fprintf(f, " ;%s", link->Comment); diff --git a/src/input3.c b/src/input3.c index c32eaf7..9534b35 100644 --- a/src/input3.c +++ b/src/input3.c @@ -7,7 +7,7 @@ Description: parses network data from a line of an EPANET input file Authors: see AUTHORS Copyright: see AUTHORS License: see LICENSE -Last Updated: 03/20/2022 +Last Updated: 08/13/2022 ****************************************************************************** */ @@ -471,7 +471,7 @@ int valvedata(Project *pr) ** Purpose: processes valve data ** Format: ** [VALVE] -** id node1 node2 diam type setting (lcoeff) +** id node1 node2 diam type setting (lcoeff lcurve) **-------------------------------------------------------------- */ { @@ -488,7 +488,8 @@ int valvedata(Project *pr) setting, // Valve setting lcoeff = 0.0; // Minor loss coeff. Slink *link; - int err = 0; + int err = 0, + losscurve = 0; // Loss coeff. curve // Add new valve to data base n = parser->Ntokens; @@ -521,6 +522,8 @@ int valvedata(Project *pr) type = TCV; else if (match(parser->Tok[4], w_GPV)) type = GPV; + else if (match(parser->Tok[4], w_PCV)) + type = PCV; else return setError(parser, 4, 213); @@ -538,6 +541,16 @@ int valvedata(Project *pr) } else if (!getfloat(parser->Tok[5], &setting)) return setError(parser, 5, 202); if (n >= 7 && !getfloat(parser->Tok[6], &lcoeff)) return setError(parser, 6, 202); + + // Find loss coeff. curve for PCV + if (type == PCV && n >= 8) + { + c = findcurve(net, parser->Tok[7]); + if (c == 0) return setError(parser, 7, 206); + losscurve = c; + net->Curve[c].Type = VALVE_CURVE; + if (setting > 1.0) setting = 1.0; + } // Check for illegal connections if (valvecheck(pr, net->Nlinks, type, j1, j2)) @@ -563,6 +576,7 @@ int valvedata(Project *pr) link->ResultIndex = 0; link->Comment = xstrcpy(&link->Comment, parser->Comment, MAXMSG); net->Valve[net->Nvalves].Link = net->Nlinks; + net->Valve[net->Nvalves].Curve = losscurve; return 0; } diff --git a/src/output.c b/src/output.c index 1e9a6ab..51685e2 100644 --- a/src/output.c +++ b/src/output.c @@ -7,7 +7,7 @@ Description: binary file read/write routines Authors: see AUTHORS Copyright: see AUTHORS License: see LICENSE -Last Updated: 05/13/2019 +Last Updated: 08/13/2022 ****************************************************************************** */ @@ -567,6 +567,7 @@ int linkoutput(Project *pr, int j, REAL4 *x, double ucf) case FCV: x[i] = (REAL4)(setting * pr->Ucf[FLOW]); break; case TCV: + case PCV: x[i] = (REAL4)setting; break; default: x[i] = 0.0f; } diff --git a/src/project.c b/src/project.c index b470ec3..4b8a971 100644 --- a/src/project.c +++ b/src/project.c @@ -7,7 +7,7 @@ Authors: see AUTHORS Copyright: see AUTHORS License: see LICENSE - Last Updated: 02/03/2020 + Last Updated: 08/13/2022 ****************************************************************************** */ @@ -860,7 +860,7 @@ int findtank(Network *network, int index) /*---------------------------------------------------------------- ** Input: index = node index ** Output: none -** Returns: index of tank with given node id, or NOTFOUND if tank not found +** Returns: index of tank with given node index, or NOTFOUND if tank not found ** Purpose: for use in the deletenode function **---------------------------------------------------------------- */ @@ -877,7 +877,7 @@ int findpump(Network *network, int index) /*---------------------------------------------------------------- ** Input: index = link ID ** Output: none -** Returns: index of pump with given link id, or NOTFOUND if pump not found +** Returns: index of pump with given link index, or NOTFOUND if pump not found ** Purpose: for use in the deletelink function **---------------------------------------------------------------- */ @@ -894,7 +894,7 @@ int findvalve(Network *network, int index) /*---------------------------------------------------------------- ** Input: index = link ID ** Output: none -** Returns: index of valve with given link id, or NOTFOUND if valve not found +** Returns: index of valve with given link index, or NOTFOUND if valve not found ** Purpose: for use in the deletelink function **---------------------------------------------------------------- */ @@ -1010,7 +1010,7 @@ void adjustcurves(Network *network, int index) **---------------------------------------------------------------- */ { - int j, k, setting; + int j, k, curve; // Adjust tank volume curves for (j = 1; j <= network->Ntanks; j++) @@ -1025,15 +1025,25 @@ void adjustcurves(Network *network, int index) adjustcurve(&network->Pump[j].Ecurve, index); } - // Adjust GPV curves + // Adjust PCV & GPV curves for (j = 1; j <= network->Nvalves; j++) { k = network->Valve[j].Link; + if (network->Link[k].Type == PCV) + { + if ((curve = network->Valve[j].Curve) > 0) + { + adjustcurve(&curve, index); + network->Valve[j].Curve = curve; + if (curve == 0) + network->Link[k].Kc = 0.0; + } + } if (network->Link[k].Type == GPV) { - setting = INT(network->Link[k].Kc); - adjustcurve(&setting, index); - network->Link[k].Kc = setting; + curve = INT(network->Link[k].Kc); + adjustcurve(&curve, index); + network->Link[k].Kc = curve; } } } diff --git a/src/text.h b/src/text.h index a4ac84b..72fef68 100755 --- a/src/text.h +++ b/src/text.h @@ -7,7 +7,7 @@ Authors: see AUTHORS Copyright: see AUTHORS License: see LICENSE - Last Updated: 07/15/2019 + Last Updated: 08/13/2022 ****************************************************************************** */ @@ -40,6 +40,7 @@ #define w_FCV "FCV" #define w_TCV "TCV" #define w_GPV "GPV" +#define w_PCV "PCV" #define w_OPEN "OPEN" #define w_CLOSED "CLOSED" diff --git a/src/types.h b/src/types.h index b0e91b5..281e012 100755 --- a/src/types.h +++ b/src/types.h @@ -7,7 +7,7 @@ Authors: see AUTHORS Copyright: see AUTHORS License: see LICENSE - Last Updated: 07/11/2020 + Last Updated: 08/13/2022 ****************************************************************************** */ @@ -145,7 +145,8 @@ typedef enum { PBV, // pressure breaker valve FCV, // flow control valve TCV, // throttle control valve - GPV // general purpose valve + GPV, // general purpose valve + PCV // positional control valve } LinkType; typedef enum { @@ -166,7 +167,8 @@ typedef enum { PUMP_CURVE, // pump curve EFFIC_CURVE, // efficiency curve HLOSS_CURVE, // head loss curve - GENERIC_CURVE // generic curve + GENERIC_CURVE, // generic curve + VALVE_CURVE // positional valve loss curve } CurveType; typedef enum { @@ -455,6 +457,7 @@ typedef struct // Pump Object typedef struct // Valve Object { int Link; // link index of valve + int Curve; // positional loss coeff. curve } Svalve; typedef struct // Control Statement diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 995d01f..946e9f6 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -39,6 +39,7 @@ set(toolkit_test_srcs test_control.cpp test_overflow.cpp test_pda.cpp + test_valve.cpp ) add_executable(test_toolkit ${toolkit_test_srcs}) diff --git a/tests/test_valve.cpp b/tests/test_valve.cpp new file mode 100644 index 0000000..a4c3c89 --- /dev/null +++ b/tests/test_valve.cpp @@ -0,0 +1,73 @@ +/* + ****************************************************************************** + Project: OWA EPANET + Version: 2.2 + Module: test_valve.cpp + Description: Tests EPANET toolkit api functions + Authors: see AUTHORS + Copyright: see AUTHORS + License: see LICENSE + Last Updated: 07/28/2022 + ****************************************************************************** +*/ + +/* + Tests PCV valve with position curve +*/ + +#include + +#include "test_toolkit.hpp" + +BOOST_AUTO_TEST_SUITE (test_valve) + +BOOST_FIXTURE_TEST_CASE(test_PCV_valve, FixtureOpenClose) + +{ + int npts = 5; + double x[] = { 0.0, 0.25, 0.5, 0.75, 1. }; + double y[] = {0.0, 0.089, 0.184, 0.406, 1.0}; + double v; + int linkIndex, curveIndex; + + // Make steady state run + error = EN_settimeparam(ph, EN_DURATION, 0); + BOOST_REQUIRE(error == 0); + + // Convert pipe 22 to a PCV + error = EN_getlinkindex(ph, (char*)"22", &linkIndex); + BOOST_REQUIRE(error == 0); + error = EN_setlinktype(ph, &linkIndex, EN_PCV, EN_UNCONDITIONAL); + BOOST_REQUIRE(error == 0); + error = EN_setlinkvalue(ph, linkIndex, EN_DIAMETER, 12); + BOOST_REQUIRE(error == 0); + error = EN_setlinkvalue(ph, linkIndex, EN_MINORLOSS, 0.19); + + // Create the PCV's position-loss curve + error = EN_addcurve(ph, (char*)"ValveCurve"); + BOOST_REQUIRE(error == 0); + error = EN_getcurveindex(ph, (char*)"ValveCurve", &curveIndex); + BOOST_REQUIRE(error == 0); + error = EN_setcurve(ph, curveIndex, x, y, npts); + BOOST_REQUIRE(error == 0); + + // Assign curve & initial setting to PCV + error = EN_setlinkvalue(ph, linkIndex, EN_PCV_CURVE, curveIndex); + BOOST_REQUIRE(error == 0); + error = EN_setlinkvalue(ph, linkIndex, EN_INITSETTING, 0.35); + BOOST_REQUIRE(error == 0); + + // Solve for hydraulics + error = EN_solveH(ph); + BOOST_REQUIRE(error == 0); + + // The PCV interpolated relative flow coeff. at 0.35 open is 0.127. + // This translates to a minor loss coeff. of 0.19 / 0.127^2 = 11.78. + // If the PCV were replaced with a TCV at that setting the resulting + // head loss would be 0.0255 ft which should equal the PCV result. + error = EN_getlinkvalue(ph, linkIndex, EN_HEADLOSS, &v); + BOOST_REQUIRE(error == 0); + BOOST_REQUIRE(abs(v - 0.0255) < 0.001); +} + +BOOST_AUTO_TEST_SUITE_END() From 5b3d75cc0da9d3e80cba2dc180d9e4c1e3e63da6 Mon Sep 17 00:00:00 2001 From: Lew Rossman Date: Tue, 16 Aug 2022 13:13:39 -0400 Subject: [PATCH 12/13] Updated language binding files added. --- doc/input-file.dox | 5 +++-- include/epanet.cs | 5 +++++ include/epanet2.bas | 5 ++++- include/epanet2.pas | 7 +++++-- include/epanet2.vb | 5 ++++- 5 files changed, 21 insertions(+), 6 deletions(-) diff --git a/doc/input-file.dox b/doc/input-file.dox index c0b69f5..f4d36ce 100644 --- a/doc/input-file.dox +++ b/doc/input-file.dox @@ -1045,8 +1045,9 @@ __Remarks:__ |GPV (general purpose valve) | ID of head loss curve | 2. Shutoff valves and check valves are considered to be part of a pipe, not a separate control valve component (see @ref PipesPage). 3. The loss coefficient setting for a TCV should not be less than its fully open loss coefficient. -4. The characteristic curve for a PCV relates the valve's fraction of fully open flow to the fraction open. -5. The head loss curve for a GPV relates head loss across the valve to the flow rate through it. +4. The characteristic curve for a PCV relates the valve's fraction of fully open flow to the fraction open. If not supplied then a linear curve is assumed. +5. The partially opened loss coefficient for a PCV is the inverse of the squared value from its characteristic curve times the fully open loss coefficient. +6. The head loss curve for a GPV relates head loss across the valve to the flow rate through it. */ /** diff --git a/include/epanet.cs b/include/epanet.cs index 2cec376..68489e6 100644 --- a/include/epanet.cs +++ b/include/epanet.cs @@ -73,6 +73,9 @@ namespace EpanetCSharpLibrary public const int EN_PUMP_ECURVE = 20; public const int EN_PUMP_ECOST = 21; public const int EN_PUMP_EPAT = 22; + public const int EN_LINK_INCONTROL = 23; + public const int EN_GPV_CURVE = 24; + public const int EN_PCV_CURVE = 25; public const int EN_DURATION = 0; //Time parameters public const int EN_HYDSTEP = 1; @@ -126,6 +129,7 @@ namespace EpanetCSharpLibrary public const int EN_FCV = 6; public const int EN_TCV = 7; public const int EN_GPV = 8; + public const int EN_PCV = 9; public const int EN_NONE = 0; //Quality analysis types public const int EN_CHEM = 1; @@ -209,6 +213,7 @@ namespace EpanetCSharpLibrary public const int EN_EFFIC_CURVE = 2; //Efficiency curve public const int EN_HLOSS_CURVE = 3; //Head loss curve public const int EN_GENERIC_CURVE = 4; //Generic curve + public const int EN_VALVE_CURVE = 5; //Valve position curve public const int EN_UNCONDITIONAL = 0; //Unconditional object deletion public const int EN_CONDITIONAL = 1; //Conditional object deletion diff --git a/include/epanet2.bas b/include/epanet2.bas index c38003e..bc2ce1e 100644 --- a/include/epanet2.bas +++ b/include/epanet2.bas @@ -5,7 +5,7 @@ Attribute VB_Name = "Module1" 'Declarations of functions in the EPANET PROGRAMMERs TOOLKIT '(EPANET2.DLL) -'Last updated on 02/01/2020 +'Last updated on 07/28/2022 ' These are codes used by the DLL functions Public Const EN_ELEVATION = 0 ' Node parameters @@ -63,6 +63,7 @@ Public Const EN_PUMP_ECOST = 21 Public Const EN_PUMP_EPAT = 22 Public Const EN_LINK_INCONTROL = 23 Public Const EN_GPV_CURVE = 24 +Public Const EN_PCV_CURVE= 25 Public Const EN_DURATION = 0 ' Time parameters Public Const EN_HYDSTEP = 1 @@ -117,6 +118,7 @@ Public Const EN_PBV = 5 Public Const EN_FCV = 6 Public Const EN_TCV = 7 Public Const EN_GPV = 8 +Public Const EN_PCV = 9 Public Const EN_CLOSED = 0 ' Link status types Public Const EN_OPEN = 1 @@ -209,6 +211,7 @@ Public Const EN_PUMP_CURVE = 1 ' Pump curve Public Const EN_EFFIC_CURVE = 2 ' Efficiency curve Public Const EN_HLOSS_CURVE = 3 ' Head loss curve Public Const EN_GENERIC_CURVE = 4 ' Generic curve +Public Const EN_VALVE_CURVE = 5 ' Valve position curve Public Const EN_UNCONDITIONAL = 0 ' Unconditional object deletion Public Const EN_CONDITIONAL = 1 ' Conditional object deletion diff --git a/include/epanet2.pas b/include/epanet2.pas index cbba41e..fba8147 100644 --- a/include/epanet2.pas +++ b/include/epanet2.pas @@ -3,7 +3,7 @@ unit epanet2; { Declarations of imported procedures from the EPANET PROGRAMMERs TOOLKIT } { (EPANET2.DLL) } -{Last updated on 02/01/2020} +{Last updated on 07/28/2022} interface @@ -69,6 +69,7 @@ const EN_PUMP_EPAT = 22; EN_LINK_INCONTROL = 23; EN_GPV_CURVE = 24; + EN_PCV_CURVE = 25; EN_DURATION = 0; { Time parameters } EN_HYDSTEP = 1; @@ -123,6 +124,7 @@ const EN_FCV = 6; EN_TCV = 7; EN_GPV = 8; + EN_PCV = 9; EN_CLOSED = 0; { Link status types } EN_OPEN = 1; @@ -214,7 +216,8 @@ const EN_PUMP_CURVE = 1; EN_EFFIC_CURVE = 2; EN_HLOSS_CURVE = 3; - EN_GENERIC_CURVE = 4; + EN_GENERIC_CURVE = 4; + EN_VALVE_CURVE = 5; EN_UNCONDITIONAL = 0; { Deletion action codes } EN_CONDITIONAL = 1; diff --git a/include/epanet2.vb b/include/epanet2.vb index bbd365e..7c283f0 100644 --- a/include/epanet2.vb +++ b/include/epanet2.vb @@ -4,7 +4,7 @@ 'Declarations of functions in the EPANET PROGRAMMERs TOOLKIT '(EPANET2.DLL) for use with VB.Net. -'Last updated on 02/01/2020 +'Last updated on 07/28/2022 Imports System.Runtime.InteropServices Imports System.Text @@ -67,6 +67,7 @@ Public Const EN_PUMP_ECOST = 21 Public Const EN_PUMP_EPAT = 22 Public Const EN_LINK_INCONTROL = 23 Public Const EN_GPV_CURVE = 24 +Public Const EN_PCV_CURVE = 25 Public Const EN_DURATION = 0 ' Time parameters Public Const EN_HYDSTEP = 1 @@ -120,6 +121,7 @@ Public Const EN_PBV = 5 Public Const EN_FCV = 6 Public Const EN_TCV = 7 Public Const EN_GPV = 8 +Public Const EN_PCV = 9 Public Const EN_NONE = 0 ' Quality analysis types Public Const EN_CHEM = 1 @@ -203,6 +205,7 @@ Public Const EN_PUMP_CURVE = 1 ' Pump curve Public Const EN_EFFIC_CURVE = 2 ' Efficiency curve Public Const EN_HLOSS_CURVE = 3 ' Head loss curve Public Const EN_GENERIC_CURVE = 4 ' Generic curve +Public Const EN_VALVE_CURVE = 5 ' Valve position curve Public Const EN_UNCONDITIONAL = 0 ' Unconditional object deletion Public Const EN_CONDITIONAL = 1 ' Conditional object deletion From 86085d4550bf04c7b7f68e2100ebcac3e0e931be Mon Sep 17 00:00:00 2001 From: "Marios S. Kyriakou" Date: Tue, 17 Jan 2023 00:32:39 +0200 Subject: [PATCH 13/13] Add a cron schedule in actions per month to create artifacts without any commit at 2023-02-01 00:00:00 then at 2023-03-01 00:00:00 then at 2023-04-01 00:00:00 then at 2023-05-01 00:00:00 then at 2023-06-01 00:00:00 ... Co-Authored-By: Elad Salomons --- .github/workflows/ccpp.yml | 6 +++++- .github/workflows/win32.yml | 6 +++++- .github/workflows/win64.yml | 6 +++++- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ccpp.yml b/.github/workflows/ccpp.yml index 9dc80b2..dc121b5 100644 --- a/.github/workflows/ccpp.yml +++ b/.github/workflows/ccpp.yml @@ -1,6 +1,10 @@ name: linux -on: [push] +on: + push: + pull_request: + schedule: + - cron: '0 0 1 * *' jobs: build: diff --git a/.github/workflows/win32.yml b/.github/workflows/win32.yml index 94b09ae..dd673c8 100644 --- a/.github/workflows/win32.yml +++ b/.github/workflows/win32.yml @@ -1,6 +1,10 @@ name: epanet2-win32 -on: [push] +on: + push: + pull_request: + schedule: + - cron: '0 0 1 * *' jobs: build: diff --git a/.github/workflows/win64.yml b/.github/workflows/win64.yml index c6802ae..560a4a3 100644 --- a/.github/workflows/win64.yml +++ b/.github/workflows/win64.yml @@ -1,6 +1,10 @@ name: epanet2-win64 -on: [push] +on: + push: + pull_request: + schedule: + - cron: '0 0 1 * *' jobs: build: