diff --git a/ReleaseNotes2_3.md b/ReleaseNotes2_3.md index 059b0d6..11f39d7 100644 --- a/ReleaseNotes2_3.md +++ b/ReleaseNotes2_3.md @@ -42,6 +42,8 @@ This document describes the changes and updates that have been made in version 2 - `EN_PRESS_UNITS` can now be used with `EN_getoption` and `EN_setoption` to get or set the pressure unit used in EPANET. + - Decoupled pressure units from the flow unit system, allowing them to be set independently to support mixed-unit conventions (e.g., using LPS for flow and PSI for pressure). + - The following constants can be used with EN_getnodevalue to retrieve the components of a node's total demand at a given point in time: - `EN_FULLDEMAND` - the consumer demand requested - `EN_DEMANDFLOW` - the consumer demand delivered diff --git a/src/epanet.c b/src/epanet.c index b872751..97603f9 100644 --- a/src/epanet.c +++ b/src/epanet.c @@ -1411,8 +1411,6 @@ int DLLEXPORT EN_setoption(EN_Project p, int option, double value) case EN_PRESS_UNITS: 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; dfactor = Ucf[DEMAND]; diff --git a/src/input1.c b/src/input1.c index ad15dba..f187ea5 100644 --- a/src/input1.c +++ b/src/input1.c @@ -103,7 +103,7 @@ void setdefaults(Project *pr) pr->Warnflag = FALSE; // Warning flag is off parser->Unitsflag = US; // US unit system parser->Flowflag = GPM; // Flow units are gpm - parser->Pressflag = PSI; // Pressure units are psi + parser->Pressflag = UNITDEFAULT; // Pressure units set based on unit system out->Hydflag = SCRATCH; // No external hydraulics file rpt->Tstatflag = SERIES; // Generate time series output @@ -269,8 +269,11 @@ void adjustdata(Project *pr) } // Revise pressure units depending on flow units - if (parser->Unitsflag != SI) parser->Pressflag = PSI; - else if (parser->Pressflag == PSI) parser->Pressflag = METERS; + if (parser->Pressflag == UNITDEFAULT) + { + if (parser->Unitsflag == SI) parser->Pressflag = METERS; + else parser->Pressflag = PSI; + } // Store value of viscosity & diffusivity ucf = 1.0; @@ -404,8 +407,6 @@ void initunits(Project *pr) strcpy(rpt->Field[DEMAND].Units, RptFlowUnitsTxt[parser->Flowflag]); strcpy(rpt->Field[ELEV].Units, u_METERS); strcpy(rpt->Field[HEAD].Units, u_METERS); - if (parser->Pressflag == METERS) strcpy(rpt->Field[PRESSURE].Units, u_METERS); - else strcpy(rpt->Field[PRESSURE].Units, u_KPA); strcpy(rpt->Field[LENGTH].Units, u_METERS); strcpy(rpt->Field[DIAM].Units, u_MMETERS); strcpy(rpt->Field[FLOW].Units, RptFlowUnitsTxt[parser->Flowflag]); @@ -423,8 +424,6 @@ void initunits(Project *pr) if (parser->Flowflag == CMS) qcf = CMSperCFS; hcf = MperFT; - if (parser->Pressflag == METERS) pcf = MperFT * hyd->SpGrav; - else pcf = KPAperPSI * PSIperFT * hyd->SpGrav; wcf = KWperHP; } else // US units @@ -448,10 +447,17 @@ void initunits(Project *pr) if (parser->Flowflag == IMGD) qcf = IMGDperCFS; if (parser->Flowflag == AFD) qcf = AFDperCFS; hcf = 1.0; - pcf = PSIperFT * hyd->SpGrav; wcf = 1.0; } + if (parser->Pressflag == METERS) strcpy(rpt->Field[PRESSURE].Units, u_METERS); + else if (parser->Pressflag == KPA) strcpy(rpt->Field[PRESSURE].Units, u_KPA); + else strcpy(rpt->Field[PRESSURE].Units, u_PSI); + + if (parser->Pressflag == METERS) pcf = MperFT * hyd->SpGrav; + else if (parser->Pressflag == KPA) pcf = KPAperPSI * PSIperFT * hyd->SpGrav; + else pcf = PSIperFT * hyd->SpGrav; + strcpy(rpt->Field[QUALITY].Units, ""); ccf = 1.0; if (qual->Qualflag == CHEM) diff --git a/src/types.h b/src/types.h index 2bd2629..b48f84e 100755 --- a/src/types.h +++ b/src/types.h @@ -238,7 +238,8 @@ typedef enum { typedef enum { PSI, // pounds per square inch KPA, // kiloPascals - METERS // meters + METERS, // meters + UNITDEFAULT // default based on unit system (SI or US) } PressureUnitsType; typedef enum { diff --git a/tests/test_units.cpp b/tests/test_units.cpp index 8397af9..53d8c5f 100644 --- a/tests/test_units.cpp +++ b/tests/test_units.cpp @@ -74,13 +74,13 @@ BOOST_FIXTURE_TEST_CASE(test_pressure_units, FixtureInitClose) BOOST_REQUIRE(error == 0); BOOST_CHECK(units == EN_PSI); - // Change to pressure from PSI to meters and check it's still PSI + // Change to pressure from PSI to meters and check it is meters 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); + BOOST_CHECK(units == EN_METERS); // Change flow units to LPS to change to metric units and rerun simulation error = EN_setflowunits(ph, EN_LPS); @@ -108,12 +108,12 @@ 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 + // Set pressure to PSI and check that it has changed to PSI 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); + BOOST_CHECK(units == EN_PSI); error = EN_closeH(ph); BOOST_REQUIRE(error == 0); @@ -264,4 +264,197 @@ BOOST_FIXTURE_TEST_CASE(test_rule_unit_change, FixtureOpenClose) } +BOOST_FIXTURE_TEST_CASE(test_decoupled_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); + + // Test 1: Start with US flow units (GPM) and change to PSI + error = EN_setflowunits(ph, EN_GPM); + BOOST_REQUIRE(error == 0); + + // Should succeed in setting PSI pressure units + 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_PSI); + + // Test 2: With US flow units, set pressure to meters (should now work) + 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_METERS); + + // Test 3: With US flow units, set pressure to kPa (should now work) + 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); + + // Test 4: Change to SI flow units (LPS) but keep kPa pressure + error = EN_setflowunits(ph, EN_LPS); + BOOST_REQUIRE(error == 0); + + // Pressure units should remain kPa (not auto-changed to meters) + error = EN_getoption(ph, EN_PRESS_UNITS, &units); + BOOST_REQUIRE(error == 0); + BOOST_CHECK(units == EN_KPA); + + // Test 5: With SI flow units, set pressure to PSI (should now work) + 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_PSI); + + // Test 6: Run simulation and check pressure values are correctly converted + 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); + + // Get pressure in PSI (should be ~43.33 PSI for 100 ft head) + error = EN_getnodevalue(ph, 1, EN_PRESSURE, &p); + BOOST_REQUIRE(error == 0); + BOOST_CHECK(abs(p - 43.33) < 1.e-5); + + // Change pressure units to meters during simulation + error = EN_setoption(ph, EN_PRESS_UNITS, EN_METERS); + BOOST_REQUIRE(error == 0); + + // Pressure should now be in meters (~30.48 m for 100 ft head) + error = EN_getnodevalue(ph, 1, EN_PRESSURE, &p); + BOOST_REQUIRE(error == 0); + BOOST_CHECK(abs(p - 30.48) < 1.e-5); + + error = EN_closeH(ph); + BOOST_REQUIRE(error == 0); +} + +BOOST_FIXTURE_TEST_CASE(test_automatic_pressure_unit_switching, FixtureInitClose) +{ + int index; + double pressure_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); + + // Test 1: Start with US flow units (CFS) - should have PSI pressure units + error = EN_setflowunits(ph, EN_CFS); + BOOST_REQUIRE(error == 0); + error = EN_getoption(ph, EN_PRESS_UNITS, &pressure_units); + BOOST_REQUIRE(error == 0); + BOOST_CHECK(pressure_units == EN_PSI); + + // Test 2: Change from US flow units (CFS) to metric flow units (LPS) + // Pressure units should automatically change from PSI to METERS + error = EN_setflowunits(ph, EN_LPS); + BOOST_REQUIRE(error == 0); + error = EN_getoption(ph, EN_PRESS_UNITS, &pressure_units); + BOOST_REQUIRE(error == 0); + BOOST_CHECK(pressure_units == EN_METERS); + + // Test 3: Change from metric flow units (LPS) back to US flow units (GPM) + // Pressure units should automatically change from METERS to PSI + error = EN_setflowunits(ph, EN_GPM); + BOOST_REQUIRE(error == 0); + error = EN_getoption(ph, EN_PRESS_UNITS, &pressure_units); + BOOST_REQUIRE(error == 0); + BOOST_CHECK(pressure_units == EN_PSI); + + // Test 4: Change from US flow units (GPM) to another metric flow unit (MLD) + // Pressure units should automatically change from PSI to METERS + error = EN_setflowunits(ph, EN_MLD); + BOOST_REQUIRE(error == 0); + error = EN_getoption(ph, EN_PRESS_UNITS, &pressure_units); + BOOST_REQUIRE(error == 0); + BOOST_CHECK(pressure_units == EN_METERS); + + // Test 5: Manually set pressure units to kPa while using metric flow units + error = EN_setoption(ph, EN_PRESS_UNITS, EN_KPA); + BOOST_REQUIRE(error == 0); + error = EN_getoption(ph, EN_PRESS_UNITS, &pressure_units); + BOOST_REQUIRE(error == 0); + BOOST_CHECK(pressure_units == EN_KPA); + + // Test 6: Change from metric flow units (MLD) to US flow units (MGD) + // Pressure units should automatically change from kPa to PSI + error = EN_setflowunits(ph, EN_MGD); + BOOST_REQUIRE(error == 0); + error = EN_getoption(ph, EN_PRESS_UNITS, &pressure_units); + BOOST_REQUIRE(error == 0); + BOOST_CHECK(pressure_units == EN_PSI); + + // Test 7: Change from US flow units (MGD) to metric flow units (CMH) + // Pressure units should automatically change from PSI to METERS + error = EN_setflowunits(ph, EN_CMH); + BOOST_REQUIRE(error == 0); + error = EN_getoption(ph, EN_PRESS_UNITS, &pressure_units); + BOOST_REQUIRE(error == 0); + BOOST_CHECK(pressure_units == EN_METERS); + + // Test 8: Set pressure to kPa again with metric flow units + error = EN_setoption(ph, EN_PRESS_UNITS, EN_KPA); + BOOST_REQUIRE(error == 0); + + // Test 9: Change between metric flow units (CMH to CMD) + // Pressure units should remain kPa (not changed to METERS since not switching from PSI) + error = EN_setflowunits(ph, EN_CMD); + BOOST_REQUIRE(error == 0); + error = EN_getoption(ph, EN_PRESS_UNITS, &pressure_units); + BOOST_REQUIRE(error == 0); + BOOST_CHECK(pressure_units == EN_KPA); + + // Test 10: Change from metric flow units (CMD) to US flow units (AFD) + // Pressure units should automatically change from kPa to PSI + error = EN_setflowunits(ph, EN_AFD); + BOOST_REQUIRE(error == 0); + error = EN_getoption(ph, EN_PRESS_UNITS, &pressure_units); + BOOST_REQUIRE(error == 0); + BOOST_CHECK(pressure_units == EN_PSI); + + // Test 11: Change between US flow units (AFD to IMGD) + // Pressure units should remain PSI + error = EN_setflowunits(ph, EN_IMGD); + BOOST_REQUIRE(error == 0); + error = EN_getoption(ph, EN_PRESS_UNITS, &pressure_units); + BOOST_REQUIRE(error == 0); + BOOST_CHECK(pressure_units == EN_PSI); + + // Test 12: Final test - metric flow units (CMS) should change PSI to METERS + error = EN_setflowunits(ph, EN_CMS); + BOOST_REQUIRE(error == 0); + error = EN_getoption(ph, EN_PRESS_UNITS, &pressure_units); + BOOST_REQUIRE(error == 0); + BOOST_CHECK(pressure_units == EN_METERS); +} + + BOOST_AUTO_TEST_SUITE_END()