From cf97cf8c9ca0cda4f732b5bc43d6f9e9d23307cc Mon Sep 17 00:00:00 2001 From: Michael Tryby Date: Tue, 23 Apr 2019 17:52:11 -0400 Subject: [PATCH] Work in progress code cleanup, addressed issue raised in review, and implemented EN_adddemand() --- include/epanet2_2.h | 11 ++++- src/epanet.c | 83 +++++++++++++++++++++++++------------- src/project.c | 18 ++++----- tests/test_demand.cpp | 14 +++++++ tests/test_demand_data.cpp | 1 + tests/test_toolkit.hpp | 20 +++++++++ 6 files changed, 108 insertions(+), 39 deletions(-) diff --git a/include/epanet2_2.h b/include/epanet2_2.h index 051e623..67bf91a 100644 --- a/include/epanet2_2.h +++ b/include/epanet2_2.h @@ -165,7 +165,7 @@ typedef struct Project *EN_Project; @return an error code */ int DLLEXPORT EN_setcomment(EN_Project ph, int object, int index, char *comment); - + /** @brief Retrieves the number of objects of a given type in a project. @param ph an EPANET project handle. @@ -851,7 +851,14 @@ typedef struct Project *EN_Project; These properties have units that depend on the units used for flow rate (see @ref Units). */ int DLLEXPORT EN_setjuncdata(EN_Project ph, int index, double elev, double dmnd, - char *dmndpat); + char *dmndpat); + + + int DLLEXPORT EN_adddemand(EN_Project p, int node_index, double demand, + char *demand_pattern, const char *category_name, int *demand_index); + + int DLLEXPORT EN_removedemand(EN_Project p, int node_index, int demand_index); + /** @brief Sets a group of properties for a tank node. diff --git a/src/epanet.c b/src/epanet.c index 0cc926c..3138be3 100644 --- a/src/epanet.c +++ b/src/epanet.c @@ -1888,15 +1888,9 @@ int DLLEXPORT EN_deletenode(EN_Project p, int index, int actionCode) // Free memory allocated to node's demands, WQ source & comment demand = node->D; - if (demand) - delete_list(demand); -// while (demand != NULL) - // { - // nextdemand = demand->next; - // free(demand->Name); - // free(demand); - // demand = nextdemand; - // } + if (demand) + delete_list(demand); + free(node->S); free(node->Comment); @@ -2063,7 +2057,6 @@ int DLLEXPORT EN_getnodevalue(EN_Project p, int index, int property, double *val Quality *qual = &p->quality; double v = 0.0; - //Pdemand demand; Psource source; Snode *Node = net->Node; @@ -2090,17 +2083,12 @@ int DLLEXPORT EN_getnodevalue(EN_Project p, int index, int property, double *val case EN_BASEDEMAND: v = 0.0; - // NOTE: primary demand category is last on demand list + // NOTE: primary demand category is first on demand list if (index <= nJuncs) { - list_t *demand = Node[index].D; - if (demand) - v = get_base_demand(tail_list(demand)); - - //for (demand = Node[index].D; demand != NULL; demand = demand->next) - //{ - // v = (demand->Base); - //} + list_t *demand = Node[index].D; + if (demand) + v = get_base_demand(head_list(demand, false)); } v *= Ucf[FLOW]; break; @@ -2110,9 +2098,9 @@ int DLLEXPORT EN_getnodevalue(EN_Project p, int index, int property, double *val // NOTE: primary demand category is first on demand list if (index <= nJuncs) { - list_t *demand = Node[index].D; - if (demand) - v = get_pattern_index(head_list(demand, false)); + list_t *demand = Node[index].D; + if (demand) + v = get_pattern_index(head_list(demand, false)); } else v = (double)(Tank[index - nJuncs].Pat); break; @@ -2267,7 +2255,6 @@ int DLLEXPORT EN_setnodevalue(EN_Project p, int index, int property, double valu double *Ucf = p->Ucf; int i, j, n; -// Pdemand demand; Psource source; double hTmp; @@ -2293,9 +2280,9 @@ int DLLEXPORT EN_setnodevalue(EN_Project p, int index, int property, double valu // NOTE: primary demand category is first on demand list if (index <= nJuncs) { - list_t *demand = Node[index].D; - if (demand) - set_base_demand(head_list(demand, false), value / Ucf[FLOW]); + list_t *demand = Node[index].D; + if (demand) + set_base_demand(head_list(demand, false), value / Ucf[FLOW]); } break; @@ -2504,7 +2491,6 @@ int DLLEXPORT EN_setjuncdata(EN_Project p, int index, double elev, int i, patIndex = 0; Snode *Node = net->Node; - //Pdemand demand; // Check that junction exists if (!p->Openflag) return 102; @@ -2532,7 +2518,7 @@ int DLLEXPORT EN_setjuncdata(EN_Project p, int index, double elev, demand_list = create_list(get_demand_data_size(), delete_demand_data); if (!demand_list) return 101; } - demand_data_t *demand_data = create_demand_data(dmnd / p->Ucf[FLOW], patIndex, NULL); + demand_data_t *demand_data = create_demand_data(dmnd/p->Ucf[FLOW], patIndex, NULL); if (!demand_data) return 101; append_list(demand_list, &demand_data); @@ -2540,6 +2526,47 @@ int DLLEXPORT EN_setjuncdata(EN_Project p, int index, double elev, return 0; } +int DLLEXPORT EN_adddemand(EN_Project p, int node_index, double demand, + char *demand_pattern, const char *category_name, int *demand_index) +{ + Network *net = &p->network; + + int pattern_index, error = 0; + *demand_index = -1; + + if (error = EN_getpatternindex(p, demand_pattern, &pattern_index) != 0) return error; + + Snode *Node = net->Node; + list_t *demand_list = Node[node_index].D; + if (!demand_list) { + demand_list = create_demand_list(demand/p->Ucf[FLOW], pattern_index, category_name); + if (!demand_list) return 101; + + Node[node_index].D = demand_list; + } + else { + demand_data_t *demand_data = create_demand_data(demand/p->Ucf[FLOW], pattern_index, category_name); + if (!demand_data) return 101; + + append_list(demand_list, &demand_data); + } + + *demand_index = size_list(demand_list); + return 0; +} + +int DLLEXPORT EN_removedemand(EN_Project p, int node_index, int demand_index) { + // Problem: Removing a demand will in most cases invalidate the index + // returned previously in EN_adddemand(). This occurs in all cases except + // when the demand at the tail of the list is removed. This is why indexing + // is a flawed strategy for random access to a list data structure. + // One possible solution is to have the user be responsible for creating a + // unique category name. Another possible solution would be for the + // application to create a unique key for each demand entry. They have + // random access based on searching for a key value in the list. + return 0; +} + int DLLEXPORT EN_settankdata(EN_Project p, int index, double elev, double initlvl, double minlvl, double maxlvl, double diam, diff --git a/src/project.c b/src/project.c index 1df2911..49696a3 100644 --- a/src/project.c +++ b/src/project.c @@ -791,12 +791,12 @@ void adjustpattern(int *pat, int index) } -void adjust_demand_pattern(list_node_t *lnode, int del_idx) +void adjust_demand_pattern(list_node_t *list_node, int deletion_index) { - int pat_idx = get_pattern_index(lnode); + int pat_idx = get_pattern_index(list_node); - if (pat_idx == del_idx) set_pattern_index(lnode, 0); - else if (pat_idx > del_idx) set_pattern_index(lnode, --pat_idx); + if (pat_idx == deletion_index) set_pattern_index(list_node, 0); + else if (pat_idx > deletion_index) set_pattern_index(list_node, --pat_idx); } @@ -816,11 +816,11 @@ void adjustpatterns(Network *network, int index) for (j = 1; j <= network->Nnodes; j++) { // Adjust demand patterns - list_t *dlist = network->Node[j].D; - if (dlist) { - for (list_node_t *lnode = first_list(dlist); done_list(lnode); lnode = next_list(lnode)) - adjust_demand_pattern(lnode, index); - } + list_t *dlist = network->Node[j].D; + if (dlist) { + for (list_node_t *lnode = first_list(dlist); done_list(lnode); lnode = next_list(lnode)) + adjust_demand_pattern(lnode, index); + } // Adjust WQ source patterns source = network->Node[j].S; if (source) adjustpattern(&source->Pat, index); diff --git a/tests/test_demand.cpp b/tests/test_demand.cpp index d9b0e69..fa3d4f9 100644 --- a/tests/test_demand.cpp +++ b/tests/test_demand.cpp @@ -82,5 +82,19 @@ BOOST_AUTO_TEST_CASE(test_categories_reopen, * boost::unit_test::depends_on("tes BOOST_REQUIRE(error == 0); } +BOOST_FIXTURE_TEST_CASE(test_adddemand, FixtureSingleNode) +{ + int demand_index; + + error = EN_adddemand(ph, node_qhut, 100.0, "PrimaryPattern", "PrimaryDemand", &demand_index); + BOOST_CHECK(error != 0); + + error = EN_addpattern(ph, (char *)"PrimaryPattern"); + BOOST_REQUIRE(error == 0); + + error = EN_adddemand(ph, node_qhut, 100.0, "PrimaryPattern", "PrimaryDemand", &demand_index); + BOOST_CHECK(error == 0); +} + BOOST_AUTO_TEST_SUITE_END() diff --git a/tests/test_demand_data.cpp b/tests/test_demand_data.cpp index 82fab3e..bbf8526 100644 --- a/tests/test_demand_data.cpp +++ b/tests/test_demand_data.cpp @@ -291,4 +291,5 @@ BOOST_FIXTURE_TEST_CASE(test_pattern_edits, FixtureSingleNode) + BOOST_AUTO_TEST_SUITE_END() diff --git a/tests/test_toolkit.hpp b/tests/test_toolkit.hpp index 08f6538..b682d5d 100644 --- a/tests/test_toolkit.hpp +++ b/tests/test_toolkit.hpp @@ -102,6 +102,26 @@ struct FixtureAfterStep{ EN_Project ph; }; +struct FixtureSingleNode { + FixtureSingleNode() { + error = 0; + ph = NULL; + + EN_createproject(&ph); + EN_init(ph, DATA_PATH_RPT, DATA_PATH_OUT, EN_GPM, EN_HW); + + EN_addnode(ph, (char *)"CUB_SCOUT_QUONSET_HUT", EN_JUNCTION, &node_qhut); + } + + ~FixtureSingleNode() { + EN_close(ph); + EN_deleteproject(&ph); + } + int error, index, node_qhut; + EN_Project ph; +}; + + boost::test_tools::predicate_result check_cdd_double(std::vector& test, std::vector& ref, long cdd_tol); boost::test_tools::predicate_result check_string(std::string test, std::string ref);