From 3e733f5a2ae25f49eb1ceb3d2dd771262f3afee1 Mon Sep 17 00:00:00 2001 From: Lew Rossman Date: Wed, 3 Apr 2019 21:17:28 -0400 Subject: [PATCH] Add more analysis options to the API (issue #425) --- include/epanet2.bas | 13 ++++- include/epanet2.vb | 13 ++++- include/epanet2_enums.h | 19 +++++-- src/epanet.c | 106 ++++++++++++++++++++++++++++++++++++---- tests/test_analysis.cpp | 9 ++-- 5 files changed, 140 insertions(+), 20 deletions(-) diff --git a/include/epanet2.bas b/include/epanet2.bas index ba94210..c09fd4a 100644 --- a/include/epanet2.bas +++ b/include/epanet2.bas @@ -140,7 +140,7 @@ Public Const EN_CMD = 9 Public Const EN_DDA = 0 ' Demand driven analysis Public Const EN_PDA = 1 ' Pressure driven analysis -Public Const EN_TRIALS = 0 ' Hydraulic options +Public Const EN_TRIALS = 0 ' Simulation options Public Const EN_ACCURACY = 1 Public Const EN_TOLERANCE = 2 Public Const EN_EMITEXPON = 3 @@ -152,6 +152,17 @@ Public Const EN_GLOBALEFFIC = 8 Public Const EN_GLOBALPRICE = 9 Public Const EN_GLOBALPATTERN = 10 Public Const EN_DEMANDCHARGE = 11 +Public Const EN_SP_GRAVITY = 12 +Public Const EN_SP_VISCOS = 13 +Public Const EN_UNBALANCED = 14 +Public Const EN_CHECKFREQ = 15 +Public Const EN_MAXCHECK = 16 +Public Const EN_DAMPLIMIT = 17 +Public Const EN_SP_DIFFUS = 18 +Public Const EN_BULKORDER = 19 +Public Const EN_WALLORDER = 20 +Public Const EN_TANKORDER = 21 +Public Const EN_CONCENLIMIT = 22 Public Const EN_LOWLEVEL = 0 ' Control types Public Const EN_HILEVEL = 1 diff --git a/include/epanet2.vb b/include/epanet2.vb index 6f71b26..58cad9f 100644 --- a/include/epanet2.vb +++ b/include/epanet2.vb @@ -145,7 +145,7 @@ Public Const EN_CMD = 9 Public Const EN_DDA = 0 ' Demand driven analysis Public Const EN_PDA = 1 ' Pressure driven analysis -Public Const EN_TRIALS = 0 ' Hydraulic options +Public Const EN_TRIALS = 0 ' Simulation options Public Const EN_ACCURACY = 1 Public Const EN_TOLERANCE = 2 Public Const EN_EMITEXPON = 3 @@ -157,6 +157,17 @@ Public Const EN_GLOBALEFFIC = 8 Public Const EN_GLOBALPRICE = 9 Public Const EN_GLOBALPATTERN = 10 Public Const EN_DEMANDCHARGE = 11 +Public Const EN_SP_GRAVITY = 12 +Public Const EN_SP_VISCOS = 13 +Public Const EN_UNBALANCED = 14 +Public Const EN_CHECKFREQ = 15 +Public Const EN_MAXCHECK = 16 +Public Const EN_DAMPLIMIT = 17 +Public Const EN_SP_DIFFUS = 18 +Public Const EN_BULKORDER = 19 +Public Const EN_WALLORDER = 20 +Public Const EN_TANKORDER = 21 +Public Const EN_CONCENLIMIT = 22 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 fa00688..33f353b 100644 --- a/include/epanet2_enums.h +++ b/include/epanet2_enums.h @@ -282,13 +282,13 @@ typedef enum { /// Simulation options /** -These options specify hydraulic convergence criteria, choice of head loss formula, and -several other parameters applied on a network-wide basis. They are accessed using the +These constants identify the hydraulic and water quality simulation options +that are applied on a network-wide basis. They are accessed using the @ref EN_getoption and @ref EN_setoption functions. */ typedef enum { - EN_TRIALS = 0, //!< Maximum hydraulic trials allowed - EN_ACCURACY = 1, //!< Maximum total relative flow change for hydraulic convergence + EN_TRIALS = 0, //!< Maximum hydraulic trials allowed for hydraulic convergence + EN_ACCURACY = 1, //!< Total normalized flow change for hydraulic convergence EN_TOLERANCE = 2, //!< Water quality tolerance EN_EMITEXPON = 3, //!< Exponent in emitter discharge formula EN_DEMANDMULT = 4, //!< Global demand multiplier @@ -299,6 +299,17 @@ typedef enum { EN_GLOBALPRICE = 9, //!< Global energy price per KWH EN_GLOBALPATTERN = 10, //!< Index of a global energy price pattern EN_DEMANDCHARGE = 11 //!< Energy charge per max. KW usage + EN_SP_GRAVITY = 12, //!< Specific gravity + EN_SP_VISCOS = 13, //!< Specific viscosity (relative to water at 20 deg C) + EN_UNBALANCED = 14, //!< Extra trials allowed if hydraulics don't converge + EN_CHECKFREQ = 15, //!< Frequency of hydraulic status checks + EN_MAXCHECK = 16, //!< Maximum trials for status checking + EN_DAMPLIMIT = 17, //!< Accuracy level where solution damping begins + EN_SP_DIFFUS = 18, //!< Specific diffusivity (relative to chlorine at 20 deg C) + EN_BULKORDER = 19, //!< Bulk water reaction order for pipes + EN_WALLORDER = 20, //!< Wall reaction order for pipes (either 0 or 1) + EN_TANKORDER = 21, //!< Bulk water reaction order for tanks + EN_CONCENLIMIT = 22 //!< Limiting concentration for growth reactions } EN_Option; /// Types of simple controls diff --git a/src/epanet.c b/src/epanet.c index 88a7669..30894fc 100644 --- a/src/epanet.c +++ b/src/epanet.c @@ -1108,8 +1108,7 @@ int DLLEXPORT EN_getoption(EN_Project p, int option, double *value) v = qual->Ctol * Ucf[QUALITY]; break; case EN_EMITEXPON: - if (hyd->Qexp > 0.0) - v = 1.0 / hyd->Qexp; + if (hyd->Qexp > 0.0) v = 1.0 / hyd->Qexp; break; case EN_DEMANDMULT: v = hyd->Dmult; @@ -1135,6 +1134,39 @@ int DLLEXPORT EN_getoption(EN_Project p, int option, double *value) case EN_DEMANDCHARGE: v = hyd->Dcost; break; + case EN_SP_GRAVITY: + v = hyd->SpGrav; + break; + case EN_SP_VISCOS: + v = hyd->Viscos / VISCOS; + break; + case EN_UNBALANCED: + v = hyd->ExtraIter; + break; + case EN_CHECKFREQ: + v = hyd->CheckFreq; + break; + case EN_MAXCHECK: + v = hyd->MaxCheck; + break; + case EN_DAMPLIMIT: + v = hyd->DampLimit; + break; + case EN_SP_DIFFUS: + v = qual->Diffus / DIFFUS; + break; + case EN_BULKORDER: + v = qual->BulkOrder; + break; + case EN_WALLORDER: + v = qual->WallOrder; + break; + case EN_TANKORDER: + v = qual->TankOrder; + break; + case EN_CONCENLIMIT: + v = qual->Climit * p->Ucf[QUALITY]; + break; default: return 251; @@ -1163,6 +1195,22 @@ int DLLEXPORT EN_setoption(EN_Project p, int option, double value) double Ke, n, ucf; if (!p->Openflag) return 102; + + // The EN_UNBALANCED option can be < 0 indicating that the simulation + // should be halted if no convergence is reached in EN_TRIALS. Other + // values set the number of additional trials to use with no more + // link status changes to achieve convergence. + if (option == EN_UNBALANCED) + { + hyd->ExtraIter = (int)value; + if (hyd->ExtraIter < 0) hyd->ExtraIter = -1; + return 0; + } + + // All other option values must be non-negative + if (value < 0.0) return 213; + + // Process the speficied option switch (option) { case EN_TRIALS: @@ -1171,12 +1219,11 @@ int DLLEXPORT EN_setoption(EN_Project p, int option, double value) break; case EN_ACCURACY: - if (value < 1.e-5 || value > 1.e-1) return 213; + if (value < 1.e-8 || value > 1.e-1) return 213; hyd->Hacc = value; break; case EN_TOLERANCE: - if (value < 0.0) return 213; qual->Ctol = value / Ucf[QUALITY]; break; @@ -1193,17 +1240,14 @@ int DLLEXPORT EN_setoption(EN_Project p, int option, double value) break; case EN_DEMANDMULT: - if (value <= 0.0) return 213; hyd->Dmult = value; break; case EN_HEADERROR: - if (value < 0.0) return 213; hyd->HeadErrorLimit = value / Ucf[HEAD]; break; case EN_FLOWCHANGE: - if (value < 0.0) return 213; hyd->FlowChangeLimit = value / Ucf[FLOW]; break; @@ -1218,12 +1262,11 @@ int DLLEXPORT EN_setoption(EN_Project p, int option, double value) break; case EN_GLOBALEFFIC: - if (value <= 0.0 || value > 100.0) return 213; + if (value <= 1.0 || value > 100.0) return 213; hyd->Epump = value; break; case EN_GLOBALPRICE: - if (value < 0.0) return 213; hyd->Ecost = value; break; @@ -1234,10 +1277,53 @@ int DLLEXPORT EN_setoption(EN_Project p, int option, double value) break; case EN_DEMANDCHARGE: - if (value < 0.0) return 213; hyd->Dcost = value; break; + case EN_SP_GRAVITY: + if (value <= 0.0) return 213; + Ucf[PRESSURE] *= (value / hyd->SpGrav); + hyd->SpGrav = value; + break; + + case EN_SP_VISCOS: + if (value <= 0.0) return 213; + hyd->Viscos = value * VISCOS; + break; + + case EN_CHECKFREQ: + hyd->CheckFreq = (int)value; + break; + + case EN_MAXCHECK: + hyd->MaxCheck = (int)value; + break; + + case EN_DAMPLIMIT: + hyd->DampLimit = value; + break; + + case EN_SP_DIFFUS: + qual->Diffus = value * DIFFUS; + break; + + case EN_BULKORDER: + qual->BulkOrder = value; + break; + + case EN_WALLORDER: + if (value == 0.0 || value == 1.0) qual->WallOrder = value; + else return 213; + break; + + case EN_TANKORDER: + qual->TankOrder = value; + break; + + case EN_CONCENLIMIT: + qual->Climit = value / p->Ucf[QUALITY]; + break; + default: return 251; } diff --git a/tests/test_analysis.cpp b/tests/test_analysis.cpp index 0787c4e..b3c9a93 100644 --- a/tests/test_analysis.cpp +++ b/tests/test_analysis.cpp @@ -23,10 +23,11 @@ BOOST_FIXTURE_TEST_CASE(test_anlys_getoption, FixtureOpenClose) { int i; - std::vector test(12); + std::vector test(23); 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}; + 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}; error = EN_solveH(ph); BOOST_REQUIRE(error == 0); @@ -35,7 +36,7 @@ BOOST_FIXTURE_TEST_CASE(test_anlys_getoption, FixtureOpenClose) BOOST_REQUIRE(error == 0); - for (i=EN_TRIALS; i<=EN_DEMANDCHARGE; i++) { + for (i=EN_TRIALS; i<=EN_CONCENLIMIT; i++) { error = EN_getoption(ph, i, array++); BOOST_REQUIRE(error == 0); } @@ -43,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, 18, &temp); + error = EN_getoption(ph, 25, &temp); BOOST_CHECK(error == 251); }