From 4beebbd470bbdad45055e0defcc1857f444be144 Mon Sep 17 00:00:00 2001 From: Luke Butler Date: Thu, 16 Mar 2023 16:13:49 -0400 Subject: [PATCH 1/4] Add EN_PRESS_UNITS to EN_getoption & EN_setoption --- doc/input-file.dox | 3 ++ doc/modules.dox | 1 + doc/toolkit-units.dox | 2 +- include/epanet.cs | 5 +++ include/epanet2.bas | 5 +++ include/epanet2.vb | 5 +++ include/epanet2_enums.h | 16 ++++++++- src/epanet.c | 13 +++++++- tests/test_analysis.cpp | 8 ++--- tests/test_project.cpp | 73 ++++++++++++++++++++++++++++++++++++++++- 10 files changed, 123 insertions(+), 8 deletions(-) diff --git a/doc/input-file.dox b/doc/input-file.dox index be4c977..1c35f6f 100644 --- a/doc/input-file.dox +++ b/doc/input-file.dox @@ -279,6 +279,7 @@ __Formats:__ + @@ -321,6 +322,8 @@ __Definitions:__ For CFS, GPM, MGD, IMGD, and AFD other input quantities are expressed in US Customary Units. If flow units are in liters or cubic meters then Metric Units must be used for all other input quantities as well. (See the @ref Units topic). The default flow units are \b GPM. +\b PRESSURE sets the units in which pressure is expressed, for networks using metric units, as determined by the \b UNITS option, the choices are: (\b KPA), or (\b METERS). For networks using US Customary Units, you can only use (\b PSI). + \b HEADLOSS selects a formula to use for computing head loss for flow through a pipe. The choices are the Hazen-Williams (\b H-W ), Darcy-Weisbach (\b D-W ), or Chezy-Manning (\b C-M ) formulas. The default is \b H-W. The \b HYDRAULICS option allows you to either SAVE the current hydraulics solution to a file or \b USE a previously saved hydraulics solution. This is useful when studying factors that only affect water quality behavior. diff --git a/doc/modules.dox b/doc/modules.dox index 249c0ec..a2ad22e 100644 --- a/doc/modules.dox +++ b/doc/modules.dox @@ -289,6 +289,7 @@ These are the toolkit's enumerated types whose members are used as function argu \enum EN_TimeParameter \enum EN_Option \enum EN_FlowUnits +\enum EN_PressUnits \enum EN_DemandModel \enum EN_MixingModel \enum EN_StatisticType diff --git a/doc/toolkit-units.dox b/doc/toolkit-units.dox index 259726e..db30aa2 100644 --- a/doc/toolkit-units.dox +++ b/doc/toolkit-units.dox @@ -25,7 +25,7 @@ The toolkit can use data expressed in either US Customary of SI Metric units. A |Length | feet | meters | |Minor Loss Coeff. | unitless | unitless | |Power | horsepower | kwatts | -|Pressure | psi | meters | +|Pressure | psi | meters or kPa | |Reaction Coeff. (Bulk) | 1/day (1st-order)| 1/day (1st-order) | |Reaction Coeff. (Wall) | mass/sq-ft/day (0-order) | mass/sq-m/day (0-order) | | | ft/day (1st-order) | meters/day (1st-order) | diff --git a/include/epanet.cs b/include/epanet.cs index f3283b8..c9cb2d4 100644 --- a/include/epanet.cs +++ b/include/epanet.cs @@ -157,6 +157,10 @@ namespace EpanetCSharpLibrary public const int EN_CMD = 9; public const int EN_CMS = 10; + public const int EN_PSI = 0; //Pressure units types + public const int EN_KPA = 1; + public const int EN_METERS = 2; + public const int EN_DDA = 0; //Demand driven analysis public const int EN_PDA = 1; //Pressure driven analysis @@ -185,6 +189,7 @@ namespace EpanetCSharpLibrary public const int EN_CONCENLIMIT = 22; public const int EN_DEMANDPATTERN = 23; public const int EN_EMITBACKFLOW = 24; + public const int EN_PRESS_UNITS = 25; public const int EN_LOWLEVEL = 0; //Control types public const int EN_HILEVEL = 1; diff --git a/include/epanet2.bas b/include/epanet2.bas index 74e1d09..63e5db3 100644 --- a/include/epanet2.bas +++ b/include/epanet2.bas @@ -154,6 +154,10 @@ Public Const EN_CMH = 8 Public Const EN_CMD = 9 Public Const EN_CMS = 10 +Public Const EN_PSI = 0 ' Pressure units types +Public Const EN_KPA = 1 +Public Const EN_METERS = 2 + Public Const EN_DDA = 0 ' Demand driven analysis Public Const EN_PDA = 1 ' Pressure driven analysis @@ -182,6 +186,7 @@ Public Const EN_TANKORDER = 21 Public Const EN_CONCENLIMIT = 22 Public Const EN_DEMANDPATTERN = 23 Public Const EN_EMITBACKFLOW = 24 +Public Const EN_PRESS_UNITS = 25 Public Const EN_LOWLEVEL = 0 ' Control types Public Const EN_HILEVEL = 1 diff --git a/include/epanet2.vb b/include/epanet2.vb index e05e1d5..daf5703 100644 --- a/include/epanet2.vb +++ b/include/epanet2.vb @@ -149,6 +149,10 @@ Public Const EN_CMH = 8 Public Const EN_CMD = 9 Public Const EN_CMS = 10 +Public Const EN_PSI = 0 ' Pressure units types +Public Const EN_KPA = 1 +Public Const EN_METERS = 2 + Public Const EN_DDA = 0 ' Demand driven analysis Public Const EN_PDA = 1 ' Pressure driven analysis @@ -177,6 +181,7 @@ Public Const EN_TANKORDER = 21 Public Const EN_CONCENLIMIT = 22 Public Const EN_DEMANDPATTERN = 23 Public Const EN_EMITBACKFLOW = 24 +Public Const EN_PRESS_UNITS = 25 Public Const EN_LOWLEVEL = 0 ' Control types Public Const EN_HILEVEL = 1 diff --git a/include/epanet2_enums.h b/include/epanet2_enums.h index ff62b38..4263eb6 100644 --- a/include/epanet2_enums.h +++ b/include/epanet2_enums.h @@ -291,6 +291,19 @@ typedef enum { EN_CMS = 10 //!< Cubic meters per second } EN_FlowUnits; +/// Pressure units +/** +The available choices for pressure units for the \b EN_PRESS_UNITS option in @ref EN_getoption +and @ref EN_setoption. For networks using US Customary units for flow ( \b EN_CFS through +\b EN_AFD ) pressure units can only be set as PSI. For network using metric units, you can +select either \b EN_MTR or \b EN_KPA. +*/ +typedef enum { + EN_PSI = 0, //!< Pounds per square inch + EN_KPA = 1, //!< Kilopascals + EN_METERS = 2 //!< Meters +} EN_PressUnits; + /// Demand models /** These choices for modeling consumer demands are used with @ref EN_getdemandmodel @@ -337,7 +350,8 @@ typedef enum { EN_TANKORDER = 21, //!< Bulk water reaction order for tanks EN_CONCENLIMIT = 22, //!< Limiting concentration for growth reactions EN_DEMANDPATTERN = 23, //!< Name of default demand pattern - EN_EMITBACKFLOW = 24 //!< 1 if emitters can backflow, 0 if not + EN_EMITBACKFLOW = 24, //!< 1 if emitters can backflow, 0 if not + EN_PRESS_UNITS = 25 //!< Pressure units (see @ref EN_PressUnits) } EN_Option; /// Simple control types diff --git a/src/epanet.c b/src/epanet.c index 115e87a..b4027cb 100644 --- a/src/epanet.c +++ b/src/epanet.c @@ -1208,6 +1208,9 @@ int DLLEXPORT EN_getoption(EN_Project p, int option, double *value) case EN_EMITBACKFLOW: v = hyd->EmitBackFlag; break; + case EN_PRESS_UNITS: + v = (double)p->parser.Pressflag; + break; default: return 251; } @@ -1231,7 +1234,7 @@ int DLLEXPORT EN_setoption(EN_Project p, int option, double value) int Njuncs = net->Njuncs; double *Ucf = p->Ucf; - int i, j, pat; + int i, j, pat, unit; double Ke, n, ucf; if (!p->Openflag) return 102; @@ -1375,6 +1378,14 @@ int DLLEXPORT EN_setoption(EN_Project p, int option, double value) else return 213; break; + case EN_PRESS_UNITS: + unit = ROUND(value); + if (unit < 0 || unit > METERS) return 205; + if (p->parser.Unitsflag == US && unit > PSI) return 0; + p->parser.Pressflag = unit; + initunits(p); + break; + default: return 251; } diff --git a/tests/test_analysis.cpp b/tests/test_analysis.cpp index b3c9a93..e366cd7 100644 --- a/tests/test_analysis.cpp +++ b/tests/test_analysis.cpp @@ -23,11 +23,11 @@ BOOST_FIXTURE_TEST_CASE(test_anlys_getoption, FixtureOpenClose) { int i; - std::vector test(23); + std::vector test(26); double *array = test.data(); std::vector ref = {40.0, 0.001, 0.01, 0.5, 1.0, 0.0, 0.0, 0.0, 75.0, 0.0, 0.0, 0.0, - 1.0, 1.0, 10.0, 2.0, 10.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.0}; + 1.0, 1.0, 10.0, 2.0, 10.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.0, 1.0, 1.0, 0.0}; error = EN_solveH(ph); BOOST_REQUIRE(error == 0); @@ -36,7 +36,7 @@ BOOST_FIXTURE_TEST_CASE(test_anlys_getoption, FixtureOpenClose) BOOST_REQUIRE(error == 0); - for (i=EN_TRIALS; i<=EN_CONCENLIMIT; i++) { + for (i=EN_TRIALS; i<=EN_PRESS_UNITS; i++) { error = EN_getoption(ph, i, array++); BOOST_REQUIRE(error == 0); } @@ -44,7 +44,7 @@ BOOST_FIXTURE_TEST_CASE(test_anlys_getoption, FixtureOpenClose) BOOST_CHECK_EQUAL_COLLECTIONS(ref.begin(), ref.end(), test.begin(), test.end()); double temp; - error = EN_getoption(ph, 25, &temp); + error = EN_getoption(ph, 26, &temp); BOOST_CHECK(error == 251); } diff --git a/tests/test_project.cpp b/tests/test_project.cpp index 627afbd..c1635cd 100644 --- a/tests/test_project.cpp +++ b/tests/test_project.cpp @@ -116,8 +116,79 @@ BOOST_AUTO_TEST_CASE(test_run) EN_deleteproject(ph); } -BOOST_AUTO_TEST_SUITE_END() +BOOST_FIXTURE_TEST_CASE(test_pressure_units, FixtureInitClose) +{ + int index; + long t; + double p, units; + // Create basic network + error = EN_addnode(ph, "R1", EN_RESERVOIR, &index); + BOOST_REQUIRE(error == 0); + error = EN_setnodevalue(ph, index, EN_ELEVATION, 100); + BOOST_REQUIRE(error == 0); + error = EN_addnode(ph, "J1", EN_JUNCTION, &index); + BOOST_REQUIRE(error == 0); + error = EN_addlink(ph, "P1", EN_PIPE, "R1", "J1", &index); + BOOST_REQUIRE(error == 0); + + // Run simulation and get junction pressure + error = EN_openH(ph); + BOOST_REQUIRE(error == 0); + error = EN_initH(ph, EN_NOSAVE); + BOOST_REQUIRE(error == 0); + error = EN_runH(ph, &t); + BOOST_REQUIRE(error == 0); + error = EN_getnodevalue(ph, 1, EN_PRESSURE, &p); + BOOST_REQUIRE(error == 0); + BOOST_CHECK(abs(p - 43.33) < 1.e-5); + + // Get pressure unit and check that it is PSI + error = EN_getoption(ph, EN_PRESS_UNITS, &units); + BOOST_REQUIRE(error == 0); + BOOST_CHECK(units == EN_PSI); + + // Check that pressure unit is PSI + error = EN_setoption(ph, EN_PRESS_UNITS, EN_METERS); + BOOST_REQUIRE(error == 0); + + // Change to meters and confirm that units are still PSI + error = EN_getoption(ph, EN_PRESS_UNITS, &units); + BOOST_REQUIRE(error == 0); + BOOST_CHECK(units == EN_PSI); + + // Change flow units to LPS to change to metric units and rerun simulation + error = EN_setflowunits(ph, EN_LPS); + BOOST_REQUIRE(error == 0); + error = EN_initH(ph, EN_NOSAVE); + BOOST_REQUIRE(error == 0); + error = EN_runH(ph, &t); + BOOST_REQUIRE(error == 0); + + // Confirm that pressure is now in meters + error = EN_getnodevalue(ph, 1, EN_PRESSURE, &p); + BOOST_REQUIRE(error == 0); + BOOST_CHECK(abs(p - 30.48) < 1.e-5); + error = EN_getoption(ph, EN_PRESS_UNITS, &units); + BOOST_REQUIRE(error == 0); + BOOST_CHECK(units == EN_METERS); + + // Set and check that pressure units are in kPa + error = EN_setoption(ph, EN_PRESS_UNITS, EN_KPA); + BOOST_REQUIRE(error == 0); + error = EN_getoption(ph, EN_PRESS_UNITS, &units); + BOOST_REQUIRE(error == 0); + BOOST_CHECK(units == EN_KPA); + error = EN_getnodevalue(ph, 1, EN_PRESSURE, &p); + BOOST_REQUIRE(error == 0); + BOOST_CHECK(abs(p - 298.76035) < 1.e-5); + + error = EN_closeH(ph); + BOOST_REQUIRE(error == 0); + +} + +BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE(test_proj_fixture) From 6bc1efaf1c97314b42bd6fc3d1475db00ee9d288 Mon Sep 17 00:00:00 2001 From: Luke Butler Date: Thu, 16 Mar 2023 16:21:55 -0400 Subject: [PATCH 2/4] Check pressure units in metric are not set to PSI --- src/epanet.c | 1 + tests/test_project.cpp | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/src/epanet.c b/src/epanet.c index b4027cb..7e7b994 100644 --- a/src/epanet.c +++ b/src/epanet.c @@ -1382,6 +1382,7 @@ int DLLEXPORT EN_setoption(EN_Project p, int option, double value) unit = ROUND(value); if (unit < 0 || unit > METERS) return 205; if (p->parser.Unitsflag == US && unit > PSI) return 0; + if (p->parser.Unitsflag == SI && unit == PSI) return 0; p->parser.Pressflag = unit; initunits(p); break; diff --git a/tests/test_project.cpp b/tests/test_project.cpp index c1635cd..6b102d3 100644 --- a/tests/test_project.cpp +++ b/tests/test_project.cpp @@ -183,6 +183,13 @@ BOOST_FIXTURE_TEST_CASE(test_pressure_units, FixtureInitClose) BOOST_REQUIRE(error == 0); BOOST_CHECK(abs(p - 298.76035) < 1.e-5); + // Set pressure to PSI and check that it remains in kPa + error = EN_setoption(ph, EN_PRESS_UNITS, EN_PSI); + BOOST_REQUIRE(error == 0); + error = EN_getoption(ph, EN_PRESS_UNITS, &units); + BOOST_REQUIRE(error == 0); + BOOST_CHECK(units == EN_KPA); + error = EN_closeH(ph); BOOST_REQUIRE(error == 0); From 56c569b56f48a2d87959a37ad32533b985f5719e Mon Sep 17 00:00:00 2001 From: Luke Butler Date: Thu, 16 Mar 2023 19:53:25 -0400 Subject: [PATCH 3/4] Update epanet2_enums.h Fixed option and spacing --- include/epanet2_enums.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/epanet2_enums.h b/include/epanet2_enums.h index 4263eb6..79039ef 100644 --- a/include/epanet2_enums.h +++ b/include/epanet2_enums.h @@ -296,11 +296,11 @@ typedef enum { The available choices for pressure units for the \b EN_PRESS_UNITS option in @ref EN_getoption and @ref EN_setoption. For networks using US Customary units for flow ( \b EN_CFS through \b EN_AFD ) pressure units can only be set as PSI. For network using metric units, you can -select either \b EN_MTR or \b EN_KPA. +select either \b EN_METERS or \b EN_KPA. */ typedef enum { - EN_PSI = 0, //!< Pounds per square inch - EN_KPA = 1, //!< Kilopascals + EN_PSI = 0, //!< Pounds per square inch + EN_KPA = 1, //!< Kilopascals EN_METERS = 2 //!< Meters } EN_PressUnits; From 9cde32080a1730cb8830ec4c87ff785e75dcce7f Mon Sep 17 00:00:00 2001 From: Luke Butler Date: Thu, 30 Mar 2023 16:22:58 -0400 Subject: [PATCH 4/4] added function updateruleunits --- src/epanet.c | 27 ++++- src/funcs.h | 1 + src/rules.c | 120 ++++++++++++++++++ tests/CMakeLists.txt | 1 + tests/test_project.cpp | 79 ------------ tests/test_units.cpp | 267 +++++++++++++++++++++++++++++++++++++++++ 6 files changed, 415 insertions(+), 80 deletions(-) create mode 100644 tests/test_units.cpp diff --git a/src/epanet.c b/src/epanet.c index 7e7b994..db47e2f 100644 --- a/src/epanet.c +++ b/src/epanet.c @@ -1237,6 +1237,9 @@ int DLLEXPORT EN_setoption(EN_Project p, int option, double value) int i, j, pat, unit; double Ke, n, ucf; + double qfactor, hfactor, pfactor, dfactor; + double dcf, pcf, hcf, qcf; + if (!p->Openflag) return 102; // The EN_UNBALANCED option can be < 0 indicating that the simulation @@ -1384,7 +1387,19 @@ int DLLEXPORT EN_setoption(EN_Project p, int option, double value) if (p->parser.Unitsflag == US && unit > PSI) return 0; if (p->parser.Unitsflag == SI && unit == PSI) return 0; p->parser.Pressflag = unit; + + dfactor = Ucf[DEMAND]; + pfactor = Ucf[PRESSURE]; + hfactor = Ucf[HEAD]; + qfactor = Ucf[FLOW]; initunits(p); + + // Update units in rules + dcf = Ucf[DEMAND] / dfactor; + pcf = Ucf[PRESSURE] / pfactor; + hcf = Ucf[HEAD] / hfactor; + qcf = Ucf[FLOW] / qfactor; + updateruleunits(p, dcf, pcf, hcf, qcf); break; default: @@ -1420,7 +1435,8 @@ int DLLEXPORT EN_setflowunits(EN_Project p, int units) Network *net = &p->network; int i, j; - double qfactor, vfactor, hfactor, efactor, xfactor, yfactor; + double qfactor, vfactor, hfactor, efactor, pfactor, dfactor, xfactor, yfactor; + double dcf, pcf, hcf, qcf; double *Ucf = p->Ucf; if (!p->Openflag) return 102; @@ -1430,6 +1446,8 @@ int DLLEXPORT EN_setflowunits(EN_Project p, int units) vfactor = Ucf[VOLUME]; hfactor = Ucf[HEAD]; efactor = Ucf[ELEV]; + pfactor = Ucf[PRESSURE]; + dfactor = Ucf[DEMAND]; p->parser.Flowflag = units; switch (units) @@ -1452,6 +1470,13 @@ int DLLEXPORT EN_setflowunits(EN_Project p, int units) else if (p->parser.Pressflag == PSI) p->parser.Pressflag = METERS; initunits(p); + // Update pressure units in rules + dcf = Ucf[DEMAND] / dfactor; + pcf = Ucf[PRESSURE] / pfactor; + hcf = Ucf[HEAD] / hfactor; + qcf = Ucf[FLOW] / qfactor; + updateruleunits(p, dcf, pcf, hcf, qcf); + //update curves for (i = 1; i <= net->Ncurves; i++) { diff --git a/src/funcs.h b/src/funcs.h index 2391477..3fe38fe 100755 --- a/src/funcs.h +++ b/src/funcs.h @@ -124,6 +124,7 @@ Spremise *getpremise(Spremise *, int); Saction *getaction(Saction *, int); int writerule(Project *, FILE *, int); int checkrules(Project *, long); +void updateruleunits(Project *pr, double dcf, double pcf, double hcf, double qcf); // ------- REPORT.C ----------------- diff --git a/src/rules.c b/src/rules.c index 68b8de6..4eaf3f5 100644 --- a/src/rules.c +++ b/src/rules.c @@ -549,6 +549,126 @@ int checkrules(Project *pr, long dt) return actionCount; } +void updateruleunits(Project *pr, double dcf, double pcf, double hcf, double qcf) +//----------------------------------------------------------- +// Updates the units of a rule's premises and actions. +//----------------------------------------------------------- +{ + Network *net = &pr->network; + Slink *Link = net->Link; + + int i, k; + double x; + Spremise *p; + Saction *a; + + for (i = 1; i <= net->Nrules; i++) + { + p = net->Rule[i].Premises; + while (p != NULL) + { + + switch (p->variable) + { + case r_DEMAND: + p->value *= dcf; + break; + + case r_HEAD: + case r_GRADE: + p->value *= hcf; + break; + + case r_PRESSURE: + p->value *= pcf; + break; + + case r_LEVEL: + p->value *= hcf; + break; + + case r_FLOW: + p->value *= qcf; + break; + + case r_SETTING: + + switch (Link[p->index].Type) + { + case PRV: + case PSV: + case PBV: + p->value *= pcf; + break; + case FCV: + p->value *= qcf; + break; + default: + break; + } + break; + + default: + break; + + } + p = p->next; + } + + a = net->Rule[i].ThenActions; + while (a != NULL) + { + k = a->link; + x = a->setting; + + // Change link's setting + if (x != MISSING) + { + switch (net->Link[k].Type) + { + case PRV: + case PSV: + case PBV: + a->setting *= pcf; + break; + case FCV: + a->setting *= qcf; + break; + default: + break; + } + } + a = a->next; + } + a = net->Rule[i].ElseActions; + while (a != NULL) + { + k = a->link; + x = a->setting; + + // Change link's setting + if (x != MISSING) + { + switch (net->Link[k].Type) + { + case PRV: + case PSV: + case PBV: + a->setting *= pcf; + break; + case FCV: + a->setting *= qcf; + break; + default: + break; + } + } + a = a->next; + } + } +} + + void newrule(Project *pr) //---------------------------------------------------------- // Adds a new rule to the project diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 946e9f6..d90167f 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -40,6 +40,7 @@ set(toolkit_test_srcs test_overflow.cpp test_pda.cpp test_valve.cpp + test_units.cpp ) add_executable(test_toolkit ${toolkit_test_srcs}) diff --git a/tests/test_project.cpp b/tests/test_project.cpp index 6b102d3..903cd0a 100644 --- a/tests/test_project.cpp +++ b/tests/test_project.cpp @@ -116,85 +116,6 @@ BOOST_AUTO_TEST_CASE(test_run) EN_deleteproject(ph); } -BOOST_FIXTURE_TEST_CASE(test_pressure_units, FixtureInitClose) -{ - int index; - long t; - double p, units; - - // Create basic network - error = EN_addnode(ph, "R1", EN_RESERVOIR, &index); - BOOST_REQUIRE(error == 0); - error = EN_setnodevalue(ph, index, EN_ELEVATION, 100); - BOOST_REQUIRE(error == 0); - error = EN_addnode(ph, "J1", EN_JUNCTION, &index); - BOOST_REQUIRE(error == 0); - error = EN_addlink(ph, "P1", EN_PIPE, "R1", "J1", &index); - BOOST_REQUIRE(error == 0); - - // Run simulation and get junction pressure - error = EN_openH(ph); - BOOST_REQUIRE(error == 0); - error = EN_initH(ph, EN_NOSAVE); - BOOST_REQUIRE(error == 0); - error = EN_runH(ph, &t); - BOOST_REQUIRE(error == 0); - error = EN_getnodevalue(ph, 1, EN_PRESSURE, &p); - BOOST_REQUIRE(error == 0); - BOOST_CHECK(abs(p - 43.33) < 1.e-5); - - // Get pressure unit and check that it is PSI - error = EN_getoption(ph, EN_PRESS_UNITS, &units); - BOOST_REQUIRE(error == 0); - BOOST_CHECK(units == EN_PSI); - - // Check that pressure unit is PSI - error = EN_setoption(ph, EN_PRESS_UNITS, EN_METERS); - BOOST_REQUIRE(error == 0); - - // Change to meters and confirm that units are still PSI - error = EN_getoption(ph, EN_PRESS_UNITS, &units); - BOOST_REQUIRE(error == 0); - BOOST_CHECK(units == EN_PSI); - - // Change flow units to LPS to change to metric units and rerun simulation - error = EN_setflowunits(ph, EN_LPS); - BOOST_REQUIRE(error == 0); - error = EN_initH(ph, EN_NOSAVE); - BOOST_REQUIRE(error == 0); - error = EN_runH(ph, &t); - BOOST_REQUIRE(error == 0); - - // Confirm that pressure is now in meters - error = EN_getnodevalue(ph, 1, EN_PRESSURE, &p); - BOOST_REQUIRE(error == 0); - BOOST_CHECK(abs(p - 30.48) < 1.e-5); - error = EN_getoption(ph, EN_PRESS_UNITS, &units); - BOOST_REQUIRE(error == 0); - BOOST_CHECK(units == EN_METERS); - - // Set and check that pressure units are in kPa - error = EN_setoption(ph, EN_PRESS_UNITS, EN_KPA); - BOOST_REQUIRE(error == 0); - error = EN_getoption(ph, EN_PRESS_UNITS, &units); - BOOST_REQUIRE(error == 0); - BOOST_CHECK(units == EN_KPA); - error = EN_getnodevalue(ph, 1, EN_PRESSURE, &p); - BOOST_REQUIRE(error == 0); - BOOST_CHECK(abs(p - 298.76035) < 1.e-5); - - // Set pressure to PSI and check that it remains in kPa - error = EN_setoption(ph, EN_PRESS_UNITS, EN_PSI); - BOOST_REQUIRE(error == 0); - error = EN_getoption(ph, EN_PRESS_UNITS, &units); - BOOST_REQUIRE(error == 0); - BOOST_CHECK(units == EN_KPA); - - error = EN_closeH(ph); - BOOST_REQUIRE(error == 0); - -} - BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE(test_proj_fixture) diff --git a/tests/test_units.cpp b/tests/test_units.cpp new file mode 100644 index 0000000..ffdfc26 --- /dev/null +++ b/tests/test_units.cpp @@ -0,0 +1,267 @@ +/* + ****************************************************************************** + Project: OWA EPANET + Version: 2.2 + Module: test_units.cpp + Description: Tests EPANET toolkit api functions + Authors: see AUTHORS + Copyright: see AUTHORS + License: see LICENSE + Last Updated: 03/30/2023 + ****************************************************************************** +*/ + +/* +This is a test for the API functions that change the units of a project. +*/ + +#include + +#include "test_toolkit.hpp" + +/* +---------------------------------------------- + Flow units conversion factors +---------------------------------------------- +*/ +double GPMperCFS = 448.831; +double LPSperCFS = 28.317; +double MperFT = 0.3048; +double PSIperFT = 0.4333; +double KPAperPSI = 6.895; + +char unitrules[] = "RULE 1\n IF NODE 10 DEMAND > 10 \n" + "AND NODE 10 HEAD > 20 \n" + "AND NODE 10 PRESSURE > 30 \n" + "AND NODE 10 LEVEL > 40 \n" + "AND LINK 10 FLOW > 50 \n" + "AND LINK PRV1 SETTING > 60 \n" + "AND LINK FCV1 SETTING > 70 \n" + "THEN LINK PRV1 SETTING = 80\n ELSE LINK FCV1 SETTING = 90"; + + +BOOST_AUTO_TEST_SUITE (test_units) + +BOOST_FIXTURE_TEST_CASE(test_pressure_units, FixtureInitClose) +{ + int index; + long t; + double p, units; + + // Create basic network + error = EN_addnode(ph, "R1", EN_RESERVOIR, &index); + BOOST_REQUIRE(error == 0); + error = EN_setnodevalue(ph, index, EN_ELEVATION, 100); + BOOST_REQUIRE(error == 0); + error = EN_addnode(ph, "J1", EN_JUNCTION, &index); + BOOST_REQUIRE(error == 0); + error = EN_addlink(ph, "P1", EN_PIPE, "R1", "J1", &index); + BOOST_REQUIRE(error == 0); + + // Run simulation and get junction pressure + error = EN_openH(ph); + BOOST_REQUIRE(error == 0); + error = EN_initH(ph, EN_NOSAVE); + BOOST_REQUIRE(error == 0); + error = EN_runH(ph, &t); + BOOST_REQUIRE(error == 0); + error = EN_getnodevalue(ph, 1, EN_PRESSURE, &p); + BOOST_REQUIRE(error == 0); + BOOST_CHECK(abs(p - 43.33) < 1.e-5); + + // Get pressure unit and check that it is PSI + error = EN_getoption(ph, EN_PRESS_UNITS, &units); + BOOST_REQUIRE(error == 0); + BOOST_CHECK(units == EN_PSI); + + // Change to pressure from PSI to meters and check it's still PSI + error = EN_setoption(ph, EN_PRESS_UNITS, EN_METERS); + BOOST_REQUIRE(error == 0); + + error = EN_getoption(ph, EN_PRESS_UNITS, &units); + BOOST_REQUIRE(error == 0); + BOOST_CHECK(units == EN_PSI); + + // Change flow units to LPS to change to metric units and rerun simulation + error = EN_setflowunits(ph, EN_LPS); + BOOST_REQUIRE(error == 0); + error = EN_initH(ph, EN_NOSAVE); + BOOST_REQUIRE(error == 0); + error = EN_runH(ph, &t); + BOOST_REQUIRE(error == 0); + + // Confirm that pressure is now in meters + error = EN_getnodevalue(ph, 1, EN_PRESSURE, &p); + BOOST_REQUIRE(error == 0); + BOOST_CHECK(abs(p - 30.48) < 1.e-5); + error = EN_getoption(ph, EN_PRESS_UNITS, &units); + BOOST_REQUIRE(error == 0); + BOOST_CHECK(units == EN_METERS); + + // Set and check that pressure units are in kPa + error = EN_setoption(ph, EN_PRESS_UNITS, EN_KPA); + BOOST_REQUIRE(error == 0); + error = EN_getoption(ph, EN_PRESS_UNITS, &units); + BOOST_REQUIRE(error == 0); + BOOST_CHECK(units == EN_KPA); + error = EN_getnodevalue(ph, 1, EN_PRESSURE, &p); + BOOST_REQUIRE(error == 0); + BOOST_CHECK(abs(p - 298.76035) < 1.e-5); + + // Set pressure to PSI and check that it remains in kPa + error = EN_setoption(ph, EN_PRESS_UNITS, EN_PSI); + BOOST_REQUIRE(error == 0); + error = EN_getoption(ph, EN_PRESS_UNITS, &units); + BOOST_REQUIRE(error == 0); + BOOST_CHECK(units == EN_KPA); + + error = EN_closeH(ph); + BOOST_REQUIRE(error == 0); + +} + +BOOST_FIXTURE_TEST_CASE(test_pda_unit_change, FixtureOpenClose) +{ + int type; + double pmin, preq, pexp; + + // Switch to PDA with pressure limits of 20 - 100 psi + error = EN_setdemandmodel(ph, EN_PDA, 20, 100, 0.5); + BOOST_REQUIRE(error == 0); + + error = EN_setflowunits(ph, EN_LPS); + BOOST_REQUIRE(error == 0); + + error = EN_getdemandmodel(ph, &type, &pmin, &preq, &pexp); + BOOST_REQUIRE(error == 0); + BOOST_CHECK(abs(pmin - (20/PSIperFT*MperFT)) < 1.e-5); + BOOST_CHECK(abs(preq - (100/PSIperFT*MperFT)) < 1.e-5); + +} + +BOOST_FIXTURE_TEST_CASE(test_rule_unit_change, FixtureOpenClose) +{ + int index, node22, link12; + double units; + + // Rule variables + int r_logop, r_object, r_objIndex, r_variable, r_relop, r_status; + double r_value; + + // Control variables + int c_index, c_type, c_linkIndex, c_nodeIndex; + double c_setting, c_level; + + // Add new PRV and FCV to test rules + error = EN_addlink(ph, (char *)"PRV1", EN_PRV, (char *)"10", (char *)"11", &index); + BOOST_REQUIRE(error == 0); + error = EN_addlink(ph, (char *)"FCV1", EN_FCV, (char *)"12", (char *)"13", &index); + BOOST_REQUIRE(error == 0); + + // Add the rule to the project + error = EN_addrule(ph, unitrules); + BOOST_REQUIRE(error == 0); + + // Add control that checks junction pressure + EN_getnodeindex(ph, (char *)"22", &node22); + EN_getlinkindex(ph, (char *)"12", &link12); + error = EN_addcontrol(ph, EN_HILEVEL, link12, 0, node22, 250, &c_index); + BOOST_REQUIRE(error == 0); + + // Check that rules and controls are in US units + error = EN_getpremise(ph, 1, 3, &r_logop, &r_object, &r_objIndex, &r_variable, &r_relop, &r_status, &r_value); + BOOST_REQUIRE(error == 0); + BOOST_CHECK(r_value == 30); + + error = EN_getcontrol(ph, c_index, &c_type, &c_linkIndex, &c_setting, &c_nodeIndex, &c_level); + BOOST_REQUIRE(error == 0); + BOOST_CHECK(c_level == 250); + + // Change flow units to lps and pressure to meters + error = EN_setflowunits(ph, EN_LPS); + BOOST_REQUIRE(error == 0); + + error = EN_getoption(ph, EN_PRESS_UNITS, &units); + BOOST_REQUIRE(error == 0); + BOOST_CHECK(units == EN_METERS); + + // Check that rules and controls are in meters + + // Simple Control - 250 psi to meters + error = EN_getcontrol(ph, c_index, &c_type, &c_linkIndex, &c_setting, &c_nodeIndex, &c_level); + BOOST_REQUIRE(error == 0); + BOOST_CHECK(abs(c_level - (250/PSIperFT*MperFT)) < 1.e-5); // 250 PSI to M + + // Premise 1 - Demand GPM to LPS + error = EN_getpremise(ph, 1, 1, &r_logop, &r_object, &r_objIndex, &r_variable, &r_relop, &r_status, &r_value); + BOOST_REQUIRE(error == 0); + BOOST_CHECK(abs(r_value - (10/GPMperCFS*LPSperCFS)) < 1.e-5); //10 GPM to LPS + + // Premise 2 - Head FT to Meters + error = EN_getpremise(ph, 1, 2, &r_logop, &r_object, &r_objIndex, &r_variable, &r_relop, &r_status, &r_value); + BOOST_REQUIRE(error == 0); + BOOST_CHECK(abs(r_value - (20*MperFT)) < 1.e-5); //20 FT to M + + // Premise 3 - Pressure PSI to Meters + error = EN_getpremise(ph, 1, 3, &r_logop, &r_object, &r_objIndex, &r_variable, &r_relop, &r_status, &r_value); + BOOST_REQUIRE(error == 0); + BOOST_CHECK(abs(r_value - (30/PSIperFT*MperFT)) < 1.e-5); //30 PSI to M + + // Premise 4 - Level FT to Meters + error = EN_getpremise(ph, 1, 4, &r_logop, &r_object, &r_objIndex, &r_variable, &r_relop, &r_status, &r_value); + BOOST_REQUIRE(error == 0); + BOOST_CHECK(abs(r_value - (40*MperFT)) < 1.e-5); //40 FT to M + + // Premise 5 - Flow GPM to LPS + error = EN_getpremise(ph, 1, 5, &r_logop, &r_object, &r_objIndex, &r_variable, &r_relop, &r_status, &r_value); + BOOST_REQUIRE(error == 0); + BOOST_CHECK(abs(r_value - (50/GPMperCFS*LPSperCFS)) < 1.e-5); //50 GPM to LPS + + // Premise 6 - Setting PSI to Meters + error = EN_getpremise(ph, 1, 6, &r_logop, &r_object, &r_objIndex, &r_variable, &r_relop, &r_status, &r_value); + BOOST_REQUIRE(error == 0); + BOOST_CHECK(abs(r_value - (60/PSIperFT*MperFT)) < 1.e-5); //60 PSI to M + + // Premise 7 - Setting GPM to LPS + error = EN_getpremise(ph, 1, 7, &r_logop, &r_object, &r_objIndex, &r_variable, &r_relop, &r_status, &r_value); + BOOST_REQUIRE(error == 0); + BOOST_CHECK(abs(r_value - (70/GPMperCFS*LPSperCFS)) < 1.e-5); //70 GPM to LPS + + // ThenAction - Setting PSI to Meters + error = EN_getthenaction(ph, 1, 1, &r_objIndex, &r_status, &r_value); + BOOST_REQUIRE(error == 0); + BOOST_CHECK(abs(r_value - (80/PSIperFT*MperFT)) < 1.e-5); //80 PSI to M + + // ElseAction - Setting GPM to LPS + error = EN_getelseaction(ph, 1, 1, &r_objIndex, &r_status, &r_value); + BOOST_REQUIRE(error == 0); + BOOST_CHECK(abs(r_value - (90/GPMperCFS*LPSperCFS)) < 1.e-5); //90 GPM to LPS + + // Change pressure units to kPa + error = EN_setoption(ph, EN_PRESS_UNITS, EN_KPA); + BOOST_REQUIRE(error == 0); + + // Simple Control - 250 psi to kPa + error = EN_getcontrol(ph, c_index, &c_type, &c_linkIndex, &c_setting, &c_nodeIndex, &c_level); + BOOST_REQUIRE(error == 0); + BOOST_CHECK(abs(c_level - (250*KPAperPSI)) < 1.e-5); //250 PSI to kPa + + // Premise 3 - Pressure PSI to kPa + error = EN_getpremise(ph, 1, 3, &r_logop, &r_object, &r_objIndex, &r_variable, &r_relop, &r_status, &r_value); + BOOST_REQUIRE(error == 0); + BOOST_CHECK(abs(r_value - (30*KPAperPSI)) < 1.e-5); //30 PSI to kPa + + // Premise 6 - Setting PSI to kPa + error = EN_getpremise(ph, 1, 6, &r_logop, &r_object, &r_objIndex, &r_variable, &r_relop, &r_status, &r_value); + BOOST_REQUIRE(error == 0); + BOOST_CHECK(abs(r_value - (60*KPAperPSI)) < 1.e-5); //60 PSI to kPa + + // ThenAction - Setting PSI to kPa + error = EN_getthenaction(ph, 1, 1, &r_objIndex, &r_status, &r_value); + BOOST_REQUIRE(error == 0); + BOOST_CHECK(abs(r_value - (80*KPAperPSI)) < 1.e-5); //80 PSI to kPa + + +} + +BOOST_AUTO_TEST_SUITE_END()
UNITSCFS / GPM / MGD / IMGD / AFD /
LPS / LPM / MLD / CMS / CMH / CMD
PRESSUREPSI / KPA / METERS
HEADLOSSH-W / D-W / C-M
HYDRAULICSUSE / SAVE  filename
VISCOSITYvalue