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()