diff --git a/python/module/openstudio.py b/python/module/openstudio.py index 62e8260b440..ee9c1bad671 100644 --- a/python/module/openstudio.py +++ b/python/module/openstudio.py @@ -50,7 +50,10 @@ # When we're using system python to load the **installed** C:\openstudio-X.Y-Z\Python stuff (not PyPi package) # This allows finding openstudiolib.dll and the msvc ones in the bin/ folder while we're in the Python/ folder # Otherwise you'd have to manually copy these DLLs from bin/ to Python/ - os.add_dll_directory(os.path.abspath(os.path.join(os.path.dirname(__file__), '..', 'bin'))) + bin_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', 'bin')) + if os.path.isdir(bin_dir): + os.add_dll_directory(bin_dir) + import openstudioairflow as airflow import openstudioenergyplus as energyplus import openstudioepjson as epjson diff --git a/resources/model/OpenStudio.idd b/resources/model/OpenStudio.idd index c44237a3db5..28ea83b636c 100644 --- a/resources/model/OpenStudio.idd +++ b/resources/model/OpenStudio.idd @@ -17112,8 +17112,8 @@ OS:DistrictHeating:Steam, OS:GroundHeatExchanger:Vertical, \extensible:2 - \min-fields 22 - \max-fields 220 + \min-fields 23 + \max-fields 221 \memo Variable short time step vertical ground heat exchanger model based on \memo Yavuztruk, C., J.D.Spitler. 1999. A Short Time Step response Factor Model for \memo Vertical Ground Loop Heat Exchangers @@ -17143,67 +17143,72 @@ OS:GroundHeatExchanger:Vertical, N2, \field Number of Bore Holes \type integer \minimum> 0.0 - N3, \field Bore Hole Length + N3, \field Bore Hole Top Depth + \required-field + \type real + \units m + \minimum 0.0 + N4, \field Bore Hole Length \type real \units m \minimum> 0.0 - N4, \field Bore Hole Radius + N5, \field Bore Hole Radius \type real \units m \minimum> 0.0 - N5, \field Ground Thermal Conductivity + N6, \field Ground Thermal Conductivity \type real \units W/m-K \ip-units Btu-in/hr-ft2-R \minimum> 0.0 - N6, \field Ground Thermal Heat Capacity + N7, \field Ground Thermal Heat Capacity \type real \units J/m3-K \minimum> 0.0 - N7, \field Ground Temperature + N8, \field Ground Temperature \type real \units C \minimum> 0.0 - N8, \field Grout Thermal Conductivity + N9, \field Grout Thermal Conductivity \type real \units W/m-K \ip-units Btu-in/hr-ft2-R \minimum> 0.0 - N9, \field Pipe Thermal Conductivity + N10, \field Pipe Thermal Conductivity \type real \units W/m-K \ip-units Btu-in/hr-ft2-R \minimum> 0.0 - N10, \field Pipe Out Diameter + N11, \field Pipe Out Diameter \type real \units m \minimum> 0.0 \ip-units in - N11, \field U-Tube Distance + N12, \field U-Tube Distance \type real \units m \minimum> 0.0 - N12, \field Pipe Thickness + N13, \field Pipe Thickness \type real \units m \minimum> 0.0 \ip-units in - N13, \field Maximum Length of Simulation + N14, \field Maximum Length of Simulation \type real \minimum> 0.0 A5, \field Undisturbed Ground Temperature Model \required-field \type object-list \object-list UndisturbedGroundTempModels - N14, \field G-Function Reference Ratio + N15, \field G-Function Reference Ratio \type real \units dimensionless \minimum> 0.0 \default 0.0005 - N15, \field G-Function Ln(T/Ts) Value + N16, \field G-Function Ln(T/Ts) Value \type real \begin-extensible - N16; \field G-Function G Value + N17; \field G-Function G Value \type real OS:GroundHeatExchanger:HorizontalTrench, @@ -31790,7 +31795,16 @@ OS:WaterHeater:HeatPump, \note A schedule value of 1 denotes inlet air is drawn only from outdoors. \note Schedule values between 0 and 1 denote a mixture of zone and outdoor air \note proportional to the schedule value. - A19; \field Control Sensor Location In Stratified Tank + A19, \field Tank Element Control Logic + \type choice + \key MutuallyExclusive + \key Simultaneous + \required-field + \note MutuallyExclusive means that once the tank heating element is active the + \note heat pump is shut down until setpoint is reached. + \note Simultaneous (default) means that both the tank heating element and + \note heat pump are used at the same time recover the tank temperature. + A20; \field Control Sensor Location In Stratified Tank \note Used to indicate height of control sensor if Tank Object Type is WaterHeater:Stratified \type choice \key Heater1 diff --git a/ruby/engine/embedded_help.rb b/ruby/engine/embedded_help.rb index 95e205dd9db..6a9666a982c 100644 --- a/ruby/engine/embedded_help.rb +++ b/ruby/engine/embedded_help.rb @@ -3,6 +3,8 @@ # See also https://openstudio.net/license ######################################################################################################################## +require 'stringio' + module EmbeddedScripting @@fileNames = EmbeddedScripting::allFileNamesAsString.split(';') @@ -56,6 +58,17 @@ def self.preprocess_ruby_script(s) # DLM: ignore for now #Encoding.default_external = Encoding::ASCII +# We patch Kernel.open, and IO.open +# to read embedded files as a StringIO, not as a FileIO +# But Gem.open_file for eg expects it to be a File, not a StringIO, and on +# Windows it will call File::flock (file lock) to protect access, but StringIO +# does not have this method +class FakeFileAsStringIO < StringIO + def flock(operation) + return 0 + end +end + module Kernel # ":" is our root path to the embedded file system # make sure it is in the ruby load path @@ -329,7 +342,7 @@ def open(name, *args, **options) #puts "string = #{string}" if block_given? # if a block is given, then a new IO is created and closed - io = StringIO.open(string) + io = FakeFileAsStringIO.open(string) begin result = yield(io) ensure @@ -337,13 +350,13 @@ def open(name, *args, **options) end return result else - return StringIO.open(string) + return FakeFileAsStringIO.open(string) end else #puts "IO.open cannot find embedded file '#{absolute_path}' for '#{name}'" if block_given? # if a block is given, then a new IO is created and closed - io = StringIO.open("") + io = FakeFileAsStringIO.open("") begin result = yield(io) ensure @@ -459,7 +472,7 @@ def self.open(name, *args, **options) #puts "string = #{string}" if block_given? # if a block is given, then a new IO is created and closed - io = StringIO.open(string) + io = FakeFileAsStringIO.open(string) begin result = yield(io) ensure @@ -467,13 +480,13 @@ def self.open(name, *args, **options) end return result else - return StringIO.open(string) + return FakeFileAsStringIO.open(string) end else puts "IO.open cannot find embedded file '#{absolute_path}' for '#{name}'" if block_given? # if a block is given, then a new IO is created and closed - io = StringIO.open("") + io = FakeFileAsStringIO.open("") begin result = yield(io) ensure diff --git a/src/cli/CMakeLists.txt b/src/cli/CMakeLists.txt index e79684fcdfc..74c557322a6 100644 --- a/src/cli/CMakeLists.txt +++ b/src/cli/CMakeLists.txt @@ -203,6 +203,11 @@ if(BUILD_TESTING) PASS_REGULAR_EXPRESSION "Hello from -x, at National Renewable Energy Laboratory" ) + # Test for 5315 + add_test(NAME OpenStudioCLI.EmbeddedScripting.uuid + COMMAND $ ${CMAKE_CURRENT_SOURCE_DIR}/test/ensure_uuid_can_be_loaded.rb + ) + add_test(NAME OpenStudioCLI.Classic.Run_RubyOnly COMMAND $ classic run -w compact_ruby_only.osw WORKING_DIRECTORY "${PROJECT_BINARY_DIR}/resources/Examples/compact_osw/" diff --git a/src/cli/test/ensure_uuid_can_be_loaded.rb b/src/cli/test/ensure_uuid_can_be_loaded.rb new file mode 100644 index 00000000000..41e4cfd19b5 --- /dev/null +++ b/src/cli/test/ensure_uuid_can_be_loaded.rb @@ -0,0 +1,5 @@ +require 'uuid' + +u = UUID.new + +puts u.inspect diff --git a/src/energyplus/ForwardTranslator/ForwardTranslateGroundHeatExchangerVertical.cpp b/src/energyplus/ForwardTranslator/ForwardTranslateGroundHeatExchangerVertical.cpp index d573a1c6794..4465d3c06ad 100644 --- a/src/energyplus/ForwardTranslator/ForwardTranslateGroundHeatExchangerVertical.cpp +++ b/src/energyplus/ForwardTranslator/ForwardTranslateGroundHeatExchangerVertical.cpp @@ -146,7 +146,7 @@ namespace energyplus { auto propertiesObjectName = modelObject.nameString() + " Properties"; propertiesIdfObject.setName(propertiesObjectName); - propertiesIdfObject.setDouble(GroundHeatExchanger_Vertical_PropertiesFields::DepthofTopofBorehole, 1); + propertiesIdfObject.setDouble(GroundHeatExchanger_Vertical_PropertiesFields::DepthofTopofBorehole, modelObject.boreHoleTopDepth()); if ((value = modelObject.boreHoleLength())) { propertiesIdfObject.setDouble(GroundHeatExchanger_Vertical_PropertiesFields::BoreholeLength, value.get()); diff --git a/src/energyplus/ForwardTranslator/ForwardTranslateWaterHeaterHeatPump.cpp b/src/energyplus/ForwardTranslator/ForwardTranslateWaterHeaterHeatPump.cpp index d99af2bf178..867f2d929a3 100644 --- a/src/energyplus/ForwardTranslator/ForwardTranslateWaterHeaterHeatPump.cpp +++ b/src/energyplus/ForwardTranslator/ForwardTranslateWaterHeaterHeatPump.cpp @@ -141,6 +141,11 @@ namespace energyplus { idfObject.setString(WaterHeater_HeatPump_PumpedCondenserFields::ParasiticHeatRejectionLocation, value); } + { + auto value = modelObject.tankElementControlLogic(); + idfObject.setString(WaterHeater_HeatPump_PumpedCondenserFields::TankElementControlLogic, value); + } + { auto tank = modelObject.tank(); if (auto stratifiedTank = tank.optionalCast()) { diff --git a/src/energyplus/Test/GroundHeatExchangerVertical_GTest.cpp b/src/energyplus/Test/GroundHeatExchangerVertical_GTest.cpp index f7f3b7e853f..3dae57dfb38 100644 --- a/src/energyplus/Test/GroundHeatExchangerVertical_GTest.cpp +++ b/src/energyplus/Test/GroundHeatExchangerVertical_GTest.cpp @@ -42,6 +42,7 @@ TEST_F(EnergyPlusFixture, ForwardTranslator_GroundHeatExchangerVertical) { EXPECT_TRUE(ghx.setDesignFlowRate(0.004)); EXPECT_TRUE(ghx.setNumberofBoreHoles(100)); + EXPECT_TRUE(ghx.setBoreHoleTopDepth(1.35)); EXPECT_TRUE(ghx.setBoreHoleLength(80.0)); EXPECT_TRUE(ghx.setBoreHoleRadius(0.7E-01)); EXPECT_TRUE(ghx.setGroundThermalConductivity(0.7)); @@ -108,7 +109,7 @@ TEST_F(EnergyPlusFixture, ForwardTranslator_GroundHeatExchangerVertical) { EXPECT_EQ(100, response.getDouble(GroundHeatExchanger_ResponseFactorsFields::NumberofBoreholes).get()); EXPECT_EQ(0.001, response.getDouble(GroundHeatExchanger_ResponseFactorsFields::GFunctionReferenceRatio).get()); - EXPECT_EQ(1, properties.getDouble(GroundHeatExchanger_Vertical_PropertiesFields::DepthofTopofBorehole).get()); + EXPECT_EQ(1.35, properties.getDouble(GroundHeatExchanger_Vertical_PropertiesFields::DepthofTopofBorehole).get()); EXPECT_EQ(80.0, properties.getDouble(GroundHeatExchanger_Vertical_PropertiesFields::BoreholeLength).get()); EXPECT_EQ(0.7E-01 * 2, properties.getDouble(GroundHeatExchanger_Vertical_PropertiesFields::BoreholeDiameter).get()); EXPECT_EQ(0.7, properties.getDouble(GroundHeatExchanger_Vertical_PropertiesFields::GroutThermalConductivity).get()); diff --git a/src/model/GroundHeatExchangerVertical.cpp b/src/model/GroundHeatExchangerVertical.cpp index 75ffb69dd9f..39a03cde3b9 100644 --- a/src/model/GroundHeatExchangerVertical.cpp +++ b/src/model/GroundHeatExchangerVertical.cpp @@ -57,6 +57,12 @@ namespace model { return getInt(OS_GroundHeatExchanger_VerticalFields::NumberofBoreHoles, true); } + double GroundHeatExchangerVertical_Impl::boreHoleTopDepth() const { + boost::optional value = getDouble(OS_GroundHeatExchanger_VerticalFields::BoreHoleTopDepth, true); + OS_ASSERT(value); + return value.get(); + } + boost::optional GroundHeatExchangerVertical_Impl::boreHoleLength() const { return getDouble(OS_GroundHeatExchanger_VerticalFields::BoreHoleLength, true); } @@ -139,6 +145,11 @@ namespace model { OS_ASSERT(result); } + bool GroundHeatExchangerVertical_Impl::setBoreHoleTopDepth(double boreHoleTopDepth) { + bool result = setDouble(OS_GroundHeatExchanger_VerticalFields::BoreHoleTopDepth, boreHoleTopDepth); + return result; + } + bool GroundHeatExchangerVertical_Impl::setBoreHoleLength(boost::optional boreHoleLength) { bool result(false); if (boreHoleLength) { @@ -443,6 +454,7 @@ namespace model { OS_ASSERT(getImpl()); setNumberofBoreHoles(120); + setBoreHoleTopDepth(1); setBoreHoleLength(76.2); setBoreHoleRadius(0.635080E-01); setGroundThermalConductivity(0.692626); @@ -513,6 +525,7 @@ namespace model { << undisturbedGroundTemperatureModel.briefDescription() << "."); } setNumberofBoreHoles(120); + setBoreHoleTopDepth(1); setBoreHoleLength(76.2); setBoreHoleRadius(0.635080E-01); setGroundThermalConductivity(0.692626); @@ -596,6 +609,10 @@ namespace model { return getImpl()->numberofBoreHoles(); } + double GroundHeatExchangerVertical::boreHoleTopDepth() const { + return getImpl()->boreHoleTopDepth(); + } + boost::optional GroundHeatExchangerVertical::boreHoleLength() const { return getImpl()->boreHoleLength(); } @@ -664,6 +681,10 @@ namespace model { getImpl()->resetNumberofBoreHoles(); } + bool GroundHeatExchangerVertical::setBoreHoleTopDepth(double boreHoleTopDepth) { + return getImpl()->setBoreHoleTopDepth(boreHoleTopDepth); + } + bool GroundHeatExchangerVertical::setBoreHoleLength(double boreHoleLength) { return getImpl()->setBoreHoleLength(boreHoleLength); } diff --git a/src/model/GroundHeatExchangerVertical.hpp b/src/model/GroundHeatExchangerVertical.hpp index 984621e2a97..3f337ff6a31 100644 --- a/src/model/GroundHeatExchangerVertical.hpp +++ b/src/model/GroundHeatExchangerVertical.hpp @@ -69,6 +69,8 @@ namespace model { boost::optional numberofBoreHoles() const; + double boreHoleTopDepth() const; + boost::optional boreHoleLength() const; boost::optional boreHoleRadius() const; @@ -108,6 +110,8 @@ namespace model { void resetNumberofBoreHoles(); + bool setBoreHoleTopDepth(double boreHoleTopDepth); + bool setBoreHoleLength(double boreHoleLength); void resetBoreHoleLength(); diff --git a/src/model/GroundHeatExchangerVertical_Impl.hpp b/src/model/GroundHeatExchangerVertical_Impl.hpp index 307b508d2d1..739598f0dba 100644 --- a/src/model/GroundHeatExchangerVertical_Impl.hpp +++ b/src/model/GroundHeatExchangerVertical_Impl.hpp @@ -56,6 +56,8 @@ namespace model { boost::optional numberofBoreHoles() const; + double boreHoleTopDepth() const; + boost::optional boreHoleLength() const; boost::optional boreHoleRadius() const; @@ -94,6 +96,8 @@ namespace model { void resetNumberofBoreHoles(); + bool setBoreHoleTopDepth(double boreHoleTopDepth); + bool setBoreHoleLength(boost::optional boreHoleLength); void resetBoreHoleLength(); diff --git a/src/model/WaterHeaterHeatPump.cpp b/src/model/WaterHeaterHeatPump.cpp index f6eeb8e31f7..817185938a6 100644 --- a/src/model/WaterHeaterHeatPump.cpp +++ b/src/model/WaterHeaterHeatPump.cpp @@ -229,6 +229,12 @@ namespace model { return value.get(); } + std::string WaterHeaterHeatPump_Impl::tankElementControlLogic() const { + boost::optional value = getString(OS_WaterHeater_HeatPumpFields::TankElementControlLogic, true); + OS_ASSERT(value); + return value.get(); + } + std::string WaterHeaterHeatPump_Impl::controlSensorLocationInStratifiedTank() const { boost::optional value = getString(OS_WaterHeater_HeatPumpFields::ControlSensorLocationInStratifiedTank, true); OS_ASSERT(value); @@ -393,6 +399,11 @@ namespace model { return result; } + bool WaterHeaterHeatPump_Impl::setTankElementControlLogic(const std::string& tankElementControlLogic) { + bool result = setString(OS_WaterHeater_HeatPumpFields::TankElementControlLogic, tankElementControlLogic); + return result; + } + bool WaterHeaterHeatPump_Impl::setControlSensorLocationInStratifiedTank(const std::string& controlSensorLocationInStratifiedTank) { bool result = setString(OS_WaterHeater_HeatPumpFields::ControlSensorLocationInStratifiedTank, controlSensorLocationInStratifiedTank); return result; @@ -565,6 +576,7 @@ namespace model { setOnCycleParasiticElectricLoad(0.0); setOffCycleParasiticElectricLoad(0.0); setParasiticHeatRejectionLocation("Outdoors"); + setTankElementControlLogic("Simultaneous"); setControlSensorLocationInStratifiedTank("Heater1"); } @@ -621,6 +633,7 @@ namespace model { setOnCycleParasiticElectricLoad(0.0); setOffCycleParasiticElectricLoad(0.0); setParasiticHeatRejectionLocation("Outdoors"); + setTankElementControlLogic("Simultaneous"); setControlSensorLocationInStratifiedTank("Heater1"); } @@ -649,6 +662,10 @@ namespace model { OS_WaterHeater_HeatPumpFields::ControlSensorLocationInStratifiedTank); } + std::vector WaterHeaterHeatPump::tankElementControlLogicValues() { + return getIddKeyNames(IddFactory::instance().getObject(iddObjectType()).get(), OS_WaterHeater_HeatPumpFields::TankElementControlLogic); + } + boost::optional WaterHeaterHeatPump::availabilitySchedule() const { return getImpl()->availabilitySchedule(); } @@ -737,6 +754,10 @@ namespace model { return getImpl()->inletAirMixerSchedule(); } + std::string WaterHeaterHeatPump::tankElementControlLogic() const { + return getImpl()->tankElementControlLogic(); + } + std::string WaterHeaterHeatPump::controlSensorLocationInStratifiedTank() const { return getImpl()->controlSensorLocationInStratifiedTank(); } @@ -855,6 +876,10 @@ namespace model { return getImpl()->setInletAirMixerSchedule(schedule); } + bool WaterHeaterHeatPump::setTankElementControlLogic(const std::string& tankElementControlLogic) { + return getImpl()->setTankElementControlLogic(tankElementControlLogic); + } + bool WaterHeaterHeatPump::setControlSensorLocationInStratifiedTank(const std::string& controlSensorLocationInStratifiedTank) { return getImpl()->setControlSensorLocationInStratifiedTank(controlSensorLocationInStratifiedTank); } diff --git a/src/model/WaterHeaterHeatPump.hpp b/src/model/WaterHeaterHeatPump.hpp index 5ae484fb1b0..05520f78274 100644 --- a/src/model/WaterHeaterHeatPump.hpp +++ b/src/model/WaterHeaterHeatPump.hpp @@ -55,6 +55,8 @@ namespace model { static std::vector parasiticHeatRejectionLocationValues(); + static std::vector tankElementControlLogicValues(); + static std::vector controlSensorLocationInStratifiedTankValues(); /** @name Getters */ @@ -104,6 +106,8 @@ namespace model { Schedule inletAirMixerSchedule() const; + std::string tankElementControlLogic() const; + std::string controlSensorLocationInStratifiedTank() const; //@} @@ -166,6 +170,8 @@ namespace model { bool setInletAirMixerSchedule(Schedule& schedule); + bool setTankElementControlLogic(const std::string& tankElementControlLogic); + bool setControlSensorLocationInStratifiedTank(const std::string& controlSensorLocationInStratifiedTank); //@} diff --git a/src/model/WaterHeaterHeatPumpWrappedCondenser.cpp b/src/model/WaterHeaterHeatPumpWrappedCondenser.cpp index c287140ad20..8da4f2a26a7 100644 --- a/src/model/WaterHeaterHeatPumpWrappedCondenser.cpp +++ b/src/model/WaterHeaterHeatPumpWrappedCondenser.cpp @@ -610,7 +610,7 @@ namespace model { setOnCycleParasiticElectricLoad(0.0); setOffCycleParasiticElectricLoad(0.0); setParasiticHeatRejectionLocation("Outdoors"); - setTankElementControlLogic("MutuallyExclusive"); + setTankElementControlLogic("MutuallyExclusive"); // TODO: The E+ default is Simultaneous! setControlSensor1HeightInStratifiedTank(1.262); setControlSensor1Weight(0.75); setControlSensor2HeightInStratifiedTank(0.464); @@ -640,7 +640,7 @@ namespace model { setOnCycleParasiticElectricLoad(0.0); setOffCycleParasiticElectricLoad(0.0); setParasiticHeatRejectionLocation("Outdoors"); - setTankElementControlLogic("MutuallyExclusive"); + setTankElementControlLogic("MutuallyExclusive"); // TODO: The E+ default is Simultaneous! setControlSensor1HeightInStratifiedTank(1.262); setControlSensor1Weight(0.75); setControlSensor2HeightInStratifiedTank(0.464); diff --git a/src/model/WaterHeaterHeatPump_Impl.hpp b/src/model/WaterHeaterHeatPump_Impl.hpp index 089cda92e1d..7f2edc24372 100644 --- a/src/model/WaterHeaterHeatPump_Impl.hpp +++ b/src/model/WaterHeaterHeatPump_Impl.hpp @@ -95,6 +95,8 @@ namespace model { Schedule inletAirMixerSchedule() const; + std::string tankElementControlLogic() const; + std::string controlSensorLocationInStratifiedTank() const; boost::optional autosizedCondenserWaterFlowRate() const; @@ -165,6 +167,8 @@ namespace model { bool setInletAirMixerSchedule(Schedule& schedule); + bool setTankElementControlLogic(const std::string& tankElementControlLogic); + bool setControlSensorLocationInStratifiedTank(const std::string& controlSensorLocationInStratifiedTank); //@} diff --git a/src/model/test/GroundHeatExchangerVertical_GTest.cpp b/src/model/test/GroundHeatExchangerVertical_GTest.cpp index d89e056cfc6..3e659eed418 100644 --- a/src/model/test/GroundHeatExchangerVertical_GTest.cpp +++ b/src/model/test/GroundHeatExchangerVertical_GTest.cpp @@ -40,6 +40,7 @@ TEST_F(ModelFixture, GroundHeatExchangerVertical_GroundHeatExchangerVertical) { EXPECT_EQ(0.0033, gh.designFlowRate().get()); ASSERT_TRUE(gh.numberofBoreHoles()); EXPECT_EQ(120, gh.numberofBoreHoles().get()); + EXPECT_EQ(1.0, gh.boreHoleTopDepth()); ASSERT_TRUE(gh.boreHoleLength()); EXPECT_EQ(76.2, gh.boreHoleLength().get()); ASSERT_TRUE(gh.boreHoleRadius()); @@ -79,6 +80,7 @@ TEST_F(ModelFixture, GroundHeatExchangerVertical_GroundHeatExchangerVertical) { EXPECT_EQ(0.0033, gh.designFlowRate().get()); ASSERT_TRUE(gh.numberofBoreHoles()); EXPECT_EQ(120, gh.numberofBoreHoles().get()); + EXPECT_EQ(1.0, gh.boreHoleTopDepth()); ASSERT_TRUE(gh.boreHoleLength()); EXPECT_EQ(76.2, gh.boreHoleLength().get()); ASSERT_TRUE(gh.boreHoleRadius()); @@ -117,6 +119,7 @@ TEST_F(ModelFixture, GroundHeatExchangerVertical_SetGetFields) { EXPECT_TRUE(gh.setDesignFlowRate(0.004)); EXPECT_TRUE(gh.setNumberofBoreHoles(100)); + EXPECT_TRUE(gh.setBoreHoleTopDepth(1.25)); EXPECT_TRUE(gh.setBoreHoleLength(80.0)); EXPECT_TRUE(gh.setBoreHoleRadius(0.7E-01)); EXPECT_TRUE(gh.setGroundThermalConductivity(0.7)); @@ -137,6 +140,7 @@ TEST_F(ModelFixture, GroundHeatExchangerVertical_SetGetFields) { EXPECT_EQ(0.004, gh.designFlowRate().get()); ASSERT_TRUE(gh.numberofBoreHoles()); EXPECT_EQ(100, gh.numberofBoreHoles().get()); + EXPECT_EQ(1.25, gh.boreHoleTopDepth()); ASSERT_TRUE(gh.boreHoleLength()); EXPECT_EQ(80.0, gh.boreHoleLength().get()); ASSERT_TRUE(gh.boreHoleRadius()); @@ -174,6 +178,7 @@ TEST_F(ModelFixture, GroundHeatExchangerVertical_SetGetFields) { EXPECT_EQ(0.004, gh.designFlowRate().get()); ASSERT_TRUE(gh.numberofBoreHoles()); EXPECT_EQ(100, gh.numberofBoreHoles().get()); + EXPECT_EQ(1.25, gh.boreHoleTopDepth()); ASSERT_TRUE(gh.boreHoleLength()); EXPECT_EQ(80.0, gh.boreHoleLength().get()); ASSERT_TRUE(gh.boreHoleRadius()); diff --git a/src/model/test/WaterHeaterHeatPump_GTest.cpp b/src/model/test/WaterHeaterHeatPump_GTest.cpp index d7da8d7f487..f1d848735c3 100644 --- a/src/model/test/WaterHeaterHeatPump_GTest.cpp +++ b/src/model/test/WaterHeaterHeatPump_GTest.cpp @@ -254,6 +254,14 @@ TEST_F(ModelFixture, WaterHeaterHeatPump_GettersSetters) { EXPECT_EQ(obj, hpwh.inletAirMixerSchedule()); } + // Tank Element Control Logic: Required String + EXPECT_EQ("Simultaneous", hpwh.tankElementControlLogic()); + EXPECT_TRUE(hpwh.setTankElementControlLogic("MutuallyExclusive")); + EXPECT_EQ("MutuallyExclusive", hpwh.tankElementControlLogic()); + // Bad Value + EXPECT_FALSE(hpwh.setTankElementControlLogic("BADENUM")); + EXPECT_EQ("MutuallyExclusive", hpwh.tankElementControlLogic()); + // Control Sensor Location In Stratified Tank: Required String EXPECT_TRUE(hpwh.setControlSensorLocationInStratifiedTank("Heater1")); EXPECT_EQ("Heater1", hpwh.controlSensorLocationInStratifiedTank()); diff --git a/src/osversion/VersionTranslator.cpp b/src/osversion/VersionTranslator.cpp index 507dc9d1d86..bb579c75b45 100644 --- a/src/osversion/VersionTranslator.cpp +++ b/src/osversion/VersionTranslator.cpp @@ -145,7 +145,7 @@ namespace osversion { m_updateMethods[VersionString("3.7.0")] = &VersionTranslator::update_3_6_1_to_3_7_0; m_updateMethods[VersionString("3.8.0")] = &VersionTranslator::update_3_7_0_to_3_8_0; m_updateMethods[VersionString("3.9.0")] = &VersionTranslator::update_3_8_0_to_3_9_0; - m_updateMethods[VersionString("3.9.1")] = &VersionTranslator::defaultUpdate; + m_updateMethods[VersionString("3.9.1")] = &VersionTranslator::update_3_9_0_to_3_9_1; // m_updateMethods[VersionString("3.10.0")] = &VersionTranslator::defaultUpdate; // List of previous versions that may be updated to this one. @@ -9640,5 +9640,73 @@ namespace osversion { } // end update_3_8_0_to_3_9_0 + std::string VersionTranslator::update_3_9_0_to_3_9_1(const IdfFile& idf_3_9_0, const IddFileAndFactoryWrapper& idd_3_9_1) { + std::stringstream ss; + boost::optional value; + + ss << idf_3_9_0.header() << '\n' << '\n'; + IdfFile targetIdf(idd_3_9_1.iddFile()); + ss << targetIdf.versionObject().get(); + + for (const IdfObject& object : idf_3_9_0.objects()) { + auto iddname = object.iddObject().name(); + + if (iddname == "OS:WaterHeater:HeatPump") { + + // 1 Field has been inserted from 3.9.0 to 3.9.1: + // ---------------------------------------------- + // * Tank Element Control Logic * 25 + auto iddObject = idd_3_9_1.getObject(iddname); + IdfObject newObject(iddObject.get()); + + for (size_t i = 0; i < object.numFields(); ++i) { + if ((value = object.getString(i))) { + if (i < 25) { + newObject.setString(i, value.get()); + } else { + newObject.setString(i + 1, value.get()); + } + } + } + + newObject.setString(25, "Simultaneous"); + + ss << newObject; + m_refactored.emplace_back(std::move(object), std::move(newObject)); + + } else if (iddname == "OS:GroundHeatExchanger:Vertical") { + + // 1 Field has been inserted from 3.9.0 to 3.9.1: + // ---------------------------------------------- + // * Bore Hole Top Depth * 6 + + auto iddObject = idd_3_9_1.getObject(iddname); + IdfObject newObject(iddObject.get()); + + for (size_t i = 0; i < object.numFields(); ++i) { + if ((value = object.getString(i))) { + if (i < 6) { + newObject.setString(i, value.get()); + } else { + newObject.setString(i + 1, value.get()); + } + } + } + + newObject.setDouble(6, 1.0); // this value of 1 is what we previously had hardcoded in FT + + ss << newObject; + m_refactored.emplace_back(std::move(object), std::move(newObject)); + + // No-op + } else { + ss << object; + } + } + + return ss.str(); + + } // end update_3_9_0_to_3_9_1 + } // namespace osversion } // namespace openstudio diff --git a/src/osversion/VersionTranslator.hpp b/src/osversion/VersionTranslator.hpp index 4600c2faa75..9ea4e977e4c 100644 --- a/src/osversion/VersionTranslator.hpp +++ b/src/osversion/VersionTranslator.hpp @@ -237,6 +237,7 @@ namespace osversion { std::string update_3_6_1_to_3_7_0(const IdfFile& idf_3_6_1, const IddFileAndFactoryWrapper& idd_3_7_0); std::string update_3_7_0_to_3_8_0(const IdfFile& idf_3_7_0, const IddFileAndFactoryWrapper& idd_3_8_0); std::string update_3_8_0_to_3_9_0(const IdfFile& idf_3_8_0, const IddFileAndFactoryWrapper& idd_3_9_0); + std::string update_3_9_0_to_3_9_1(const IdfFile& idf_3_9_0, const IddFileAndFactoryWrapper& idd_3_9_1); IdfObject updateUrlField_0_7_1_to_0_7_2(const IdfObject& object, unsigned index); diff --git a/src/osversion/test/3_9_1/test_vt_GroundHeatExchangerVertical.osm b/src/osversion/test/3_9_1/test_vt_GroundHeatExchangerVertical.osm new file mode 100644 index 00000000000..2d894123b00 --- /dev/null +++ b/src/osversion/test/3_9_1/test_vt_GroundHeatExchangerVertical.osm @@ -0,0 +1,106 @@ + +OS:Version, + {c23de9a0-083e-49b9-843c-3008f64ac27b}, !- Handle + 3.9.0; !- Version Identifier + +OS:GroundHeatExchanger:Vertical, + {bc2f2f69-fa65-42c9-8f1b-5887bfebd17d}, !- Handle + Ground Heat Exchanger Vertical 1, !- Name + , !- Inlet Node Name + , !- Outlet Node Name + 0.0033, !- Design Flow Rate {m3/s} + 120, !- Number of Bore Holes + 76.2, !- Bore Hole Length {m} + 0.063508, !- Bore Hole Radius {m} + 0.692626, !- Ground Thermal Conductivity {W/m-K} + 2347000, !- Ground Thermal Heat Capacity {J/m3-K} + 13.375, !- Ground Temperature {C} + 0.692626, !- Grout Thermal Conductivity {W/m-K} + 0.391312, !- Pipe Thermal Conductivity {W/m-K} + 0.0266667, !- Pipe Out Diameter {m} + 0.0253977, !- U-Tube Distance {m} + 0.00241285, !- Pipe Thickness {m} + 2, !- Maximum Length of Simulation + {1da197d6-5c3f-4d28-af88-8d9f74332cd4}, !- Undisturbed Ground Temperature Model + 0.0005, !- G-Function Reference Ratio {dimensionless} + -15.2996, !- G-Function Ln(T/Ts) Value 1 + -0.348322, !- G-Function G Value 1 + -14.201, !- G-Function Ln(T/Ts) Value 2 + 0.022208, !- G-Function G Value 2 + -13.2202, !- G-Function Ln(T/Ts) Value 3 + 0.412345, !- G-Function G Value 3 + -12.2086, !- G-Function Ln(T/Ts) Value 4 + 0.867498, !- G-Function G Value 4 + -11.1888, !- G-Function Ln(T/Ts) Value 5 + 1.357839, !- G-Function G Value 5 + -10.1816, !- G-Function Ln(T/Ts) Value 6 + 1.852024, !- G-Function G Value 6 + -9.1815, !- G-Function Ln(T/Ts) Value 7 + 2.345656, !- G-Function G Value 7 + -8.6809, !- G-Function Ln(T/Ts) Value 8 + 2.593958, !- G-Function G Value 8 + -8.5, !- G-Function Ln(T/Ts) Value 9 + 2.679, !- G-Function G Value 9 + -7.8, !- G-Function Ln(T/Ts) Value 10 + 3.023, !- G-Function G Value 10 + -7.2, !- G-Function Ln(T/Ts) Value 11 + 3.32, !- G-Function G Value 11 + -6.5, !- G-Function Ln(T/Ts) Value 12 + 3.681, !- G-Function G Value 12 + -5.9, !- G-Function Ln(T/Ts) Value 13 + 4.071, !- G-Function G Value 13 + -5.2, !- G-Function Ln(T/Ts) Value 14 + 4.828, !- G-Function G Value 14 + -4.5, !- G-Function Ln(T/Ts) Value 15 + 6.253, !- G-Function G Value 15 + -3.963, !- G-Function Ln(T/Ts) Value 16 + 7.894, !- G-Function G Value 16 + -3.27, !- G-Function Ln(T/Ts) Value 17 + 11.82, !- G-Function G Value 17 + -2.864, !- G-Function Ln(T/Ts) Value 18 + 15.117, !- G-Function G Value 18 + -2.577, !- G-Function Ln(T/Ts) Value 19 + 18.006, !- G-Function G Value 19 + -2.171, !- G-Function Ln(T/Ts) Value 20 + 22.887, !- G-Function G Value 20 + -1.884, !- G-Function Ln(T/Ts) Value 21 + 26.924, !- G-Function G Value 21 + -1.191, !- G-Function Ln(T/Ts) Value 22 + 38.004, !- G-Function G Value 22 + -0.497, !- G-Function Ln(T/Ts) Value 23 + 49.919, !- G-Function G Value 23 + -0.274, !- G-Function Ln(T/Ts) Value 24 + 53.407, !- G-Function G Value 24 + -0.051, !- G-Function Ln(T/Ts) Value 25 + 56.632, !- G-Function G Value 25 + 0.196, !- G-Function Ln(T/Ts) Value 26 + 59.825, !- G-Function G Value 26 + 0.419, !- G-Function Ln(T/Ts) Value 27 + 62.349, !- G-Function G Value 27 + 0.642, !- G-Function Ln(T/Ts) Value 28 + 64.524, !- G-Function G Value 28 + 0.873, !- G-Function Ln(T/Ts) Value 29 + 66.412, !- G-Function G Value 29 + 1.112, !- G-Function Ln(T/Ts) Value 30 + 67.993, !- G-Function G Value 30 + 1.335, !- G-Function Ln(T/Ts) Value 31 + 69.162, !- G-Function G Value 31 + 1.679, !- G-Function Ln(T/Ts) Value 32 + 70.476, !- G-Function G Value 32 + 2.028, !- G-Function Ln(T/Ts) Value 33 + 71.361, !- G-Function G Value 33 + 2.275, !- G-Function Ln(T/Ts) Value 34 + 71.79, !- G-Function G Value 34 + 3.003, !- G-Function Ln(T/Ts) Value 35 + 72.511; !- G-Function G Value 35 + +OS:Site:GroundTemperature:Undisturbed:KusudaAchenbach, + {1da197d6-5c3f-4d28-af88-8d9f74332cd4}, !- Handle + Site Ground Temperature Undisturbed Kusuda Achenbach 1, !- Name + 0.692626, !- Soil Thermal Conductivity {W/m-K} + 920, !- Soil Density {kg/m3} + 2551.08695652174, !- Soil Specific Heat {J/kg-K} + 13.375, !- Average Soil Surface Temperature {C} + 3.2, !- Average Amplitude of Surface Temperature {deltaC} + 8; !- Phase Shift of Minimum Surface Temperature {days} + diff --git a/src/osversion/test/3_9_1/test_vt_GroundHeatExchangerVertical.rb b/src/osversion/test/3_9_1/test_vt_GroundHeatExchangerVertical.rb new file mode 100644 index 00000000000..a9d062e3208 --- /dev/null +++ b/src/osversion/test/3_9_1/test_vt_GroundHeatExchangerVertical.rb @@ -0,0 +1,9 @@ +#require '/usr/local/openstudio-3.9.0/Ruby/openstudio' + +include OpenStudio::Model + +m = Model.new + +ghe_vert = GroundHeatExchangerVertical.new(m) + +m.save('test_vt_GroundHeatExchangerVertical.osm', true) diff --git a/src/osversion/test/3_9_1/test_vt_WaterHeaterHeatPump.osm b/src/osversion/test/3_9_1/test_vt_WaterHeaterHeatPump.osm new file mode 100644 index 00000000000..a645294ee6f --- /dev/null +++ b/src/osversion/test/3_9_1/test_vt_WaterHeaterHeatPump.osm @@ -0,0 +1,380 @@ + +OS:Version, + {9fc26069-607c-49f9-a9f9-06c348919a6e}, !- Handle + 3.9.0; !- Version Identifier + +OS:WaterHeater:HeatPump, + {86c0fef7-5c22-4fac-9766-9c880b4dd43e}, !- Handle + Water Heater Heat Pump 1, !- Name + , !- Availability Schedule + {170bb840-b066-4bc0-ad04-79e4f616663c}, !- Compressor Setpoint Temperature Schedule + 5, !- Dead Band Temperature Difference {deltaC} + autosize, !- Condenser Water Flow Rate {m3/s} + autosize, !- Evaporator Air Flow Rate {m3/s} + Schedule, !- Inlet Air Configuration + , !- Air Inlet Node + , !- Air Outlet Node + , !- Outdoor Air Node + {9868d5f7-3b93-4786-b56f-567304422d2a}, !- Inlet Air Temperature Schedule + {7752e8ab-439d-4a4a-8e9e-4ebe2f47d34c}, !- Inlet Air Humidity Schedule + {80515266-3261-422f-8ea5-e42363a5ab5f}, !- Tank + {fcf0e2a4-67eb-49a0-8c8b-f8d44fc8e50a}, !- DX Coil + 10, !- Minimum Inlet Air Temperature for Compressor Operation {C} + 48.89, !- Maximum Inlet Air Temperature for Compressor Operation {C} + Schedule, !- Compressor Location + {4d0ad212-207e-4ad0-be44-dc85cde2f773}, !- Compressor Ambient Temperature Schedule + {427ec742-b5e4-4684-ab67-0efd2c2dfc62}, !- Fan + DrawThrough, !- Fan Placement + 0, !- On Cycle Parasitic Electric Load {W} + 0, !- Off Cycle Parasitic Electric Load {W} + Outdoors, !- Parasitic Heat Rejection Location + {d1edcd81-43ce-420e-a192-89dd610f1f05}, !- Inlet Air Mixer Schedule + Heater2; !- Control Sensor Location In Stratified Tank + +OS:Coil:WaterHeating:AirToWaterHeatPump, + {fcf0e2a4-67eb-49a0-8c8b-f8d44fc8e50a}, !- Handle + Coil Water Heating Air To Water Heat Pump 1, !- Name + 4000, !- Rated Heating Capacity {W} + 3.2, !- Rated COP {W/W} + 0.6956, !- Rated Sensible Heat Ratio + 29.44, !- Rated Evaporator Inlet Air Dry-Bulb Temperature {C} + 22.22, !- Rated Evaporator Inlet Air Wet-Bulb Temperature {C} + 55.72, !- Rated Condenser Inlet Water Temperature {C} + autosize, !- Rated Evaporator Air Flow Rate {m3/s} + autosize, !- Rated Condenser Water Flow Rate {m3/s} + No, !- Evaporator Fan Power Included in Rated COP + No, !- Condenser Pump Power Included in Rated COP + No, !- Condenser Pump Heat Included in Rated Heating Capacity and Rated COP + 150, !- Condenser Water Pump Power {W} + 0.1, !- Fraction of Condenser Pump Heat to Water + , !- Evaporator Air Inlet Node Name + , !- Evaporator Air Outlet Node Name + , !- Condenser Water Inlet Node + , !- Condenser Water Outlet Node + 100, !- Crankcase Heater Capacity {W} + , !- Crankcase Heater Capacity Function of Temperature Curve Name + 5, !- Maximum Ambient Temperature for Crankcase Heater Operation {C} + WetBulbTemperature, !- Evaporator Air Temperature Type for Curve Objects + {d4ed0d7e-d0ee-4604-b0ce-d02ac665ef3c}, !- Heating Capacity Function of Temperature Curve + {1d000c3a-a46f-4887-a5b7-4658e992eadb}, !- Heating Capacity Function of Air Flow Fraction Curve + {69123eb8-cc62-4508-b209-4621759c0f6f}, !- Heating Capacity Function of Water Flow Fraction Curve + {5982a871-e93a-4084-9222-beb018f55f51}, !- Heating COP Function of Temperature Curve + {3f07af59-c45c-4ccf-a498-3859c302ed70}, !- Heating COP Function of Air Flow Fraction Curve + {f6ceebd3-853b-4eb2-b98e-c9c17829ed96}, !- Heating COP Function of Water Flow Fraction Curve + {ee072dfe-f061-472d-a382-48531c0e839d}; !- Part Load Fraction Correlation Curve + +OS:Curve:Biquadratic, + {d4ed0d7e-d0ee-4604-b0ce-d02ac665ef3c}, !- Handle + Curve Biquadratic 1, !- Name + 0.369827, !- Coefficient1 Constant + 0.043341, !- Coefficient2 x + -0.00023, !- Coefficient3 x**2 + 0.000466, !- Coefficient4 y + 2.6e-05, !- Coefficient5 y**2 + -0.00027, !- Coefficient6 x*y + 0, !- Minimum Value of x + 40, !- Maximum Value of x + 20, !- Minimum Value of y + 90, !- Maximum Value of y + , !- Minimum Curve Output + , !- Maximum Curve Output + Temperature, !- Input Unit Type for X + Temperature, !- Input Unit Type for Y + Dimensionless; !- Output Unit Type + +OS:Curve:Quadratic, + {1d000c3a-a46f-4887-a5b7-4658e992eadb}, !- Handle + Curve Quadratic 1, !- Name + 1, !- Coefficient1 Constant + 0, !- Coefficient2 x + 0, !- Coefficient3 x**2 + 0, !- Minimum Value of x + 1; !- Maximum Value of x + +OS:Curve:Quadratic, + {69123eb8-cc62-4508-b209-4621759c0f6f}, !- Handle + Curve Quadratic 2, !- Name + 1, !- Coefficient1 Constant + 0, !- Coefficient2 x + 0, !- Coefficient3 x**2 + 0, !- Minimum Value of x + 1; !- Maximum Value of x + +OS:Curve:Biquadratic, + {5982a871-e93a-4084-9222-beb018f55f51}, !- Handle + Curve Biquadratic 2, !- Name + 1.19713, !- Coefficient1 Constant + 0.077849, !- Coefficient2 x + -1.6e-06, !- Coefficient3 x**2 + -0.02675, !- Coefficient4 y + 0.000296, !- Coefficient5 y**2 + -0.00112, !- Coefficient6 x*y + 0, !- Minimum Value of x + 40, !- Maximum Value of x + 20, !- Minimum Value of y + 90, !- Maximum Value of y + , !- Minimum Curve Output + , !- Maximum Curve Output + Temperature, !- Input Unit Type for X + Temperature, !- Input Unit Type for Y + Dimensionless; !- Output Unit Type + +OS:Curve:Quadratic, + {3f07af59-c45c-4ccf-a498-3859c302ed70}, !- Handle + Curve Quadratic 3, !- Name + 1, !- Coefficient1 Constant + 0, !- Coefficient2 x + 0, !- Coefficient3 x**2 + 0, !- Minimum Value of x + 1; !- Maximum Value of x + +OS:Curve:Quadratic, + {f6ceebd3-853b-4eb2-b98e-c9c17829ed96}, !- Handle + Curve Quadratic 4, !- Name + 1, !- Coefficient1 Constant + 0, !- Coefficient2 x + 0, !- Coefficient3 x**2 + 0, !- Minimum Value of x + 1; !- Maximum Value of x + +OS:Curve:Quadratic, + {ee072dfe-f061-472d-a382-48531c0e839d}, !- Handle + Curve Quadratic 5, !- Name + 0.75, !- Coefficient1 Constant + 0.25, !- Coefficient2 x + 0, !- Coefficient3 x**2 + 0, !- Minimum Value of x + 1; !- Maximum Value of x + +OS:WaterHeater:Mixed, + {80515266-3261-422f-8ea5-e42363a5ab5f}, !- Handle + Water Heater Mixed 1, !- Name + 0.3785, !- Tank Volume {m3} + {d165ff2b-a4bc-4dd2-a986-008cfd26096a}, !- Setpoint Temperature Schedule Name + 2, !- Deadband Temperature Difference {deltaC} + 82.22, !- Maximum Temperature Limit {C} + Cycle, !- Heater Control Type + 845000, !- Heater Maximum Capacity {W} + , !- Heater Minimum Capacity {W} + , !- Heater Ignition Minimum Flow Rate {m3/s} + , !- Heater Ignition Delay {s} + NaturalGas, !- Heater Fuel Type + 0.8, !- Heater Thermal Efficiency + , !- Part Load Factor Curve Name + 20, !- Off Cycle Parasitic Fuel Consumption Rate {W} + NaturalGas, !- Off Cycle Parasitic Fuel Type + 0.8, !- Off Cycle Parasitic Heat Fraction to Tank + , !- On Cycle Parasitic Fuel Consumption Rate {W} + NaturalGas, !- On Cycle Parasitic Fuel Type + , !- On Cycle Parasitic Heat Fraction to Tank + Schedule, !- Ambient Temperature Indicator + {2b6df6dd-c4c9-4ef2-a003-91e1cfb8e68a}, !- Ambient Temperature Schedule Name + , !- Ambient Temperature Thermal Zone Name + , !- Ambient Temperature Outdoor Air Node Name + 6, !- Off Cycle Loss Coefficient to Ambient Temperature {W/K} + , !- Off Cycle Loss Fraction to Thermal Zone + 6, !- On Cycle Loss Coefficient to Ambient Temperature {W/K} + , !- On Cycle Loss Fraction to Thermal Zone + , !- Peak Use Flow Rate {m3/s} + , !- Use Flow Rate Fraction Schedule Name + , !- Cold Water Supply Temperature Schedule Name + , !- Use Side Inlet Node Name + , !- Use Side Outlet Node Name + 1, !- Use Side Effectiveness + , !- Source Side Inlet Node Name + , !- Source Side Outlet Node Name + 1, !- Source Side Effectiveness + autosize, !- Use Side Design Flow Rate {m3/s} + autosize, !- Source Side Design Flow Rate {m3/s} + 1.5, !- Indirect Water Heating Recovery Time {hr} + IndirectHeatPrimarySetpoint, !- Source Side Flow Control Mode + , !- Indirect Alternate Setpoint Temperature Schedule Name + General; !- End-Use Subcategory + +OS:Schedule:Ruleset, + {2b6df6dd-c4c9-4ef2-a003-91e1cfb8e68a}, !- Handle + Schedule Ruleset 1, !- Name + {7618d9a8-9a72-4d93-8572-e246cc181e6d}, !- Schedule Type Limits Name + {32413f84-23b6-423c-8298-47885f192f55}; !- Default Day Schedule Name + +OS:Schedule:Day, + {32413f84-23b6-423c-8298-47885f192f55}, !- Handle + Schedule Day 1, !- Name + {7618d9a8-9a72-4d93-8572-e246cc181e6d}, !- Schedule Type Limits Name + , !- Interpolate to Timestep + 24, !- Hour 1 + 0, !- Minute 1 + 22; !- Value Until Time 1 + +OS:ScheduleTypeLimits, + {7618d9a8-9a72-4d93-8572-e246cc181e6d}, !- Handle + Temperature, !- Name + , !- Lower Limit Value + , !- Upper Limit Value + Continuous, !- Numeric Type + Temperature; !- Unit Type + +OS:Schedule:Ruleset, + {d165ff2b-a4bc-4dd2-a986-008cfd26096a}, !- Handle + Schedule Ruleset 2, !- Name + {7618d9a8-9a72-4d93-8572-e246cc181e6d}, !- Schedule Type Limits Name + {b7425088-0278-4f27-ac6b-2f33b3b751f6}; !- Default Day Schedule Name + +OS:Schedule:Day, + {b7425088-0278-4f27-ac6b-2f33b3b751f6}, !- Handle + Schedule Day 2, !- Name + {7618d9a8-9a72-4d93-8572-e246cc181e6d}, !- Schedule Type Limits Name + , !- Interpolate to Timestep + 24, !- Hour 1 + 0, !- Minute 1 + 60; !- Value Until Time 1 + +OS:WaterHeater:Sizing, + {c6e53721-60db-4fe5-86e4-a2fb4b6a4b05}, !- Handle + {80515266-3261-422f-8ea5-e42363a5ab5f}, !- WaterHeater Name + PeakDraw, !- Design Mode + 0.538503, !- Time Storage Can Meet Peak Draw {hr} + 0, !- Time for Tank Recovery {hr} + 1; !- Nominal Tank Volume for Autosizing Plant Connections {m3} + +OS:Fan:OnOff, + {427ec742-b5e4-4684-ab67-0efd2c2dfc62}, !- Handle + Fan On Off 1, !- Name + {ffe3c9b3-c46e-4801-851d-af35235b19d0}, !- Availability Schedule Name + 0.6, !- Fan Total Efficiency + 300, !- Pressure Rise {Pa} + autosize, !- Maximum Flow Rate {m3/s} + 0.8, !- Motor Efficiency + 1, !- Motor In Airstream Fraction + , !- Air Inlet Node Name + , !- Air Outlet Node Name + {e64c8d56-a78b-4adf-994a-49036b1b02dd}, !- Fan Power Ratio Function of Speed Ratio Curve Name + {d1e63cae-b8bb-4d63-997f-cf914bc1cc9d}, !- Fan Efficiency Ratio Function of Speed Ratio Curve Name + ; !- End-Use Subcategory + +OS:Schedule:Constant, + {ffe3c9b3-c46e-4801-851d-af35235b19d0}, !- Handle + Always On Discrete, !- Name + {c0caae58-b814-4f35-b1b2-d5c86b8d739d}, !- Schedule Type Limits Name + 1; !- Value + +OS:ScheduleTypeLimits, + {c0caae58-b814-4f35-b1b2-d5c86b8d739d}, !- Handle + OnOff, !- Name + 0, !- Lower Limit Value + 1, !- Upper Limit Value + Discrete, !- Numeric Type + Availability; !- Unit Type + +OS:Curve:Exponent, + {e64c8d56-a78b-4adf-994a-49036b1b02dd}, !- Handle + Fan On Off Power Curve, !- Name + 1, !- Coefficient1 Constant + 0, !- Coefficient2 Constant + 0, !- Coefficient3 Constant + 0, !- Minimum Value of x + 1, !- Maximum Value of x + , !- Minimum Curve Output + , !- Maximum Curve Output + , !- Input Unit Type for X + ; !- Output Unit Type + +OS:Curve:Cubic, + {d1e63cae-b8bb-4d63-997f-cf914bc1cc9d}, !- Handle + Fan On Off Efficiency Curve, !- Name + 1, !- Coefficient1 Constant + 0, !- Coefficient2 x + 0, !- Coefficient3 x**2 + 0, !- Coefficient4 x**3 + 0, !- Minimum Value of x + 1; !- Maximum Value of x + +OS:Schedule:Ruleset, + {170bb840-b066-4bc0-ad04-79e4f616663c}, !- Handle + Schedule Ruleset 3, !- Name + {7618d9a8-9a72-4d93-8572-e246cc181e6d}, !- Schedule Type Limits Name + {4f334873-4053-4c1d-9aaa-a84c9bd5d466}; !- Default Day Schedule Name + +OS:Schedule:Day, + {4f334873-4053-4c1d-9aaa-a84c9bd5d466}, !- Handle + Schedule Day 3, !- Name + {7618d9a8-9a72-4d93-8572-e246cc181e6d}, !- Schedule Type Limits Name + , !- Interpolate to Timestep + 24, !- Hour 1 + 0, !- Minute 1 + 60; !- Value Until Time 1 + +OS:Schedule:Ruleset, + {d1edcd81-43ce-420e-a192-89dd610f1f05}, !- Handle + HPWH Inlet Air Mixer Schedule, !- Name + {a9d7daaf-e76b-48a2-b017-3977bb48fcbe}, !- Schedule Type Limits Name + {6b875af1-5c3a-4f41-97bb-5f6074b1b800}; !- Default Day Schedule Name + +OS:Schedule:Day, + {6b875af1-5c3a-4f41-97bb-5f6074b1b800}, !- Handle + Schedule Day 4, !- Name + {a9d7daaf-e76b-48a2-b017-3977bb48fcbe}, !- Schedule Type Limits Name + , !- Interpolate to Timestep + 24, !- Hour 1 + 0, !- Minute 1 + 0.2; !- Value Until Time 1 + +OS:ScheduleTypeLimits, + {a9d7daaf-e76b-48a2-b017-3977bb48fcbe}, !- Handle + Fractional, !- Name + 0, !- Lower Limit Value + 1, !- Upper Limit Value + Continuous; !- Numeric Type + +OS:Schedule:Ruleset, + {9868d5f7-3b93-4786-b56f-567304422d2a}, !- Handle + Schedule Ruleset 5, !- Name + {7618d9a8-9a72-4d93-8572-e246cc181e6d}, !- Schedule Type Limits Name + {6e56ba9f-bcbc-4723-8fe9-4c09fa52513a}; !- Default Day Schedule Name + +OS:Schedule:Day, + {6e56ba9f-bcbc-4723-8fe9-4c09fa52513a}, !- Handle + Schedule Day 5, !- Name + {7618d9a8-9a72-4d93-8572-e246cc181e6d}, !- Schedule Type Limits Name + , !- Interpolate to Timestep + 24, !- Hour 1 + 0, !- Minute 1 + 19.7; !- Value Until Time 1 + +OS:Schedule:Ruleset, + {7752e8ab-439d-4a4a-8e9e-4ebe2f47d34c}, !- Handle + Schedule Ruleset 6, !- Name + {c3980eb2-a25c-4df1-9a0b-81c4934bb9bd}, !- Schedule Type Limits Name + {65947b68-f663-43f4-86ec-b6eb614ecf4c}; !- Default Day Schedule Name + +OS:Schedule:Day, + {65947b68-f663-43f4-86ec-b6eb614ecf4c}, !- Handle + Schedule Day 6, !- Name + {c3980eb2-a25c-4df1-9a0b-81c4934bb9bd}, !- Schedule Type Limits Name + , !- Interpolate to Timestep + 24, !- Hour 1 + 0, !- Minute 1 + 0.5; !- Value Until Time 1 + +OS:ScheduleTypeLimits, + {c3980eb2-a25c-4df1-9a0b-81c4934bb9bd}, !- Handle + Dimensionless, !- Name + 0, !- Lower Limit Value + , !- Upper Limit Value + Continuous; !- Numeric Type + +OS:Schedule:Ruleset, + {4d0ad212-207e-4ad0-be44-dc85cde2f773}, !- Handle + Schedule Ruleset 7, !- Name + {7618d9a8-9a72-4d93-8572-e246cc181e6d}, !- Schedule Type Limits Name + {43a1ced1-c811-4c6c-ae05-c864f5234b41}; !- Default Day Schedule Name + +OS:Schedule:Day, + {43a1ced1-c811-4c6c-ae05-c864f5234b41}, !- Handle + Schedule Day 7, !- Name + {7618d9a8-9a72-4d93-8572-e246cc181e6d}, !- Schedule Type Limits Name + , !- Interpolate to Timestep + 24, !- Hour 1 + 0, !- Minute 1 + 21; !- Value Until Time 1 + diff --git a/src/osversion/test/3_9_1/test_vt_WaterHeaterHeatPump.rb b/src/osversion/test/3_9_1/test_vt_WaterHeaterHeatPump.rb new file mode 100644 index 00000000000..23467677bdc --- /dev/null +++ b/src/osversion/test/3_9_1/test_vt_WaterHeaterHeatPump.rb @@ -0,0 +1,13 @@ +#require '/usr/local/openstudio-3.9.0/Ruby/openstudio' + +include OpenStudio::Model + +m = Model.new + +hpwh = WaterHeaterHeatPump.new(m) +# Field before insertion +hpwh.inletAirMixerSchedule.setName("HPWH Inlet Air Mixer Schedule") +# Field after insertion, also last field +hpwh.setControlSensorLocationInStratifiedTank("Heater2") + +m.save('test_vt_WaterHeaterHeatPump.osm', true) diff --git a/src/osversion/test/VersionTranslator_GTest.cpp b/src/osversion/test/VersionTranslator_GTest.cpp index de25c25a3ba..a6daebd33ee 100644 --- a/src/osversion/test/VersionTranslator_GTest.cpp +++ b/src/osversion/test/VersionTranslator_GTest.cpp @@ -1216,8 +1216,8 @@ TEST_F(OSVersionFixture, update_3_0_1_to_3_1_0_WaterHeaterHeatPump) { EXPECT_EQ("Outdoors", wh.getString(17, false, true).get()); // Last Field: Control Sensor Location In Stratified Tank - ASSERT_TRUE(wh.getString(25, false, true)); - EXPECT_EQ("Heater2", wh.getString(25, false, true).get()); + ASSERT_TRUE(wh.getString(26, false, true)); + EXPECT_EQ("Heater2", wh.getString(26, false, true).get()); } TEST_F(OSVersionFixture, update_3_0_1_to_3_1_0_ShadingControl_and_SubSurfaces) { @@ -1721,9 +1721,9 @@ TEST_F(OSVersionFixture, update_3_2_1_to_3_3_0_GroundHeatExchangerVertical) { EXPECT_EQ(1.15, ghe.getDouble(4).get()); // Field before deletion - EXPECT_EQ(13.385, ghe.getDouble(10).get()); + EXPECT_EQ(13.385, ghe.getDouble(11).get()); // Field right after deleted - EXPECT_EQ(0.71111, ghe.getDouble(11).get()); + EXPECT_EQ(0.71111, ghe.getDouble(12).get()); EXPECT_EQ(35, ghe.numExtensibleGroups()); } @@ -2515,19 +2515,20 @@ TEST_F(OSVersionFixture, update_3_6_1_to_3_7_0_GroundHeatExchangerVertical) { EXPECT_TRUE(ghx.isEmpty(3)); // Outlet Node Name EXPECT_EQ(0.0033, ghx.getDouble(4).get()); // Design Flow Rate EXPECT_EQ(120, ghx.getInt(5).get()); // Number of Bore Holes - EXPECT_EQ(76.2, ghx.getDouble(6).get()); // Bore Hole Length - EXPECT_EQ(0.063508, ghx.getDouble(7).get()); // Bore Hole Radius - EXPECT_EQ(0.692626, ghx.getDouble(8).get()); // Ground Thermal Conductivity - EXPECT_EQ(2347000, ghx.getDouble(9).get()); // Ground Thermal Heat Capacity - EXPECT_EQ(13.375, ghx.getDouble(10).get()); // Ground Temperature - EXPECT_EQ(0.692626, ghx.getDouble(11).get()); // Grout Thermal Conductivity - EXPECT_EQ(0.391312, ghx.getDouble(12).get()); // Pipe Thermal Conductivity - EXPECT_EQ(0.0266667, ghx.getDouble(13).get()); // Pipe Out Diameter - EXPECT_EQ(0.0253977, ghx.getDouble(14).get()); // U-Tube Distance - EXPECT_EQ(0.00241285, ghx.getDouble(15).get()); // Pipe Thickness - EXPECT_EQ(2, ghx.getInt(16).get()); // Maximum Length of Simulation - EXPECT_NE("", ghx.getString(17).get()); // Undisturbed Ground Temperature Model - EXPECT_EQ(0.0005, ghx.getDouble(18).get()); // G-Function Reference Ratio {dimensionless} + EXPECT_EQ(1.0, ghx.getDouble(6).get()); // Bore Hole Top Depth {m} (added 3.9.1) + EXPECT_EQ(76.2, ghx.getDouble(7).get()); // Bore Hole Length + EXPECT_EQ(0.063508, ghx.getDouble(8).get()); // Bore Hole Radius + EXPECT_EQ(0.692626, ghx.getDouble(9).get()); // Ground Thermal Conductivity + EXPECT_EQ(2347000, ghx.getDouble(10).get()); // Ground Thermal Heat Capacity + EXPECT_EQ(13.375, ghx.getDouble(11).get()); // Ground Temperature + EXPECT_EQ(0.692626, ghx.getDouble(12).get()); // Grout Thermal Conductivity + EXPECT_EQ(0.391312, ghx.getDouble(13).get()); // Pipe Thermal Conductivity + EXPECT_EQ(0.0266667, ghx.getDouble(14).get()); // Pipe Out Diameter + EXPECT_EQ(0.0253977, ghx.getDouble(15).get()); // U-Tube Distance + EXPECT_EQ(0.00241285, ghx.getDouble(16).get()); // Pipe Thickness + EXPECT_EQ(2, ghx.getInt(17).get()); // Maximum Length of Simulation + EXPECT_NE("", ghx.getString(18).get()); // Undisturbed Ground Temperature Model + EXPECT_EQ(0.0005, ghx.getDouble(19).get()); // G-Function Reference Ratio {dimensionless} EXPECT_EQ(35u, ghx.numExtensibleGroups()); auto eg1 = ghx.extensibleGroups()[0]; @@ -2540,9 +2541,9 @@ TEST_F(OSVersionFixture, update_3_6_1_to_3_7_0_GroundHeatExchangerVertical) { std::vector ukas = model->getObjectsByType("OS:Site:GroundTemperature:Undisturbed:KusudaAchenbach"); ASSERT_EQ(1u, ukas.size()); - ASSERT_TRUE(ghx.getTarget(17)); - WorkspaceObject uka = ghx.getTarget(17).get(); - EXPECT_EQ(uka.nameString(), ghx.getTarget(17)->nameString()); + ASSERT_TRUE(ghx.getTarget(18)); + WorkspaceObject uka = ghx.getTarget(18).get(); + EXPECT_EQ(uka.nameString(), ghx.getTarget(18)->nameString()); EXPECT_EQ(IddObjectType(IddObjectType::OS_Site_GroundTemperature_Undisturbed_KusudaAchenbach), uka.iddObject().type()); EXPECT_EQ("Site Ground Temperature Undisturbed Kusuda Achenbach 1", uka.getString(1).get()); // Name EXPECT_EQ(0.692626, uka.getDouble(2).get()); // Soil Thermal Conductivity @@ -4524,3 +4525,47 @@ TEST_F(OSVersionFixture, update_3_8_0_to_3_9_0_SizingZone) { EXPECT_EQ("Coincident", sizing_zone.getString(39).get()); // Sizing Option } + +TEST_F(OSVersionFixture, update_3_9_0_to_3_9_1_WaterHeaterHeatPump) { + openstudio::path path = resourcesPath() / toPath("osversion/3_9_1/test_vt_WaterHeaterHeatPump.osm"); + osversion::VersionTranslator vt; + boost::optional model = vt.loadModel(path); + ASSERT_TRUE(model) << "Failed to load " << path; + + openstudio::path outPath = resourcesPath() / toPath("osversion/3_9_1/test_vt_WaterHeaterHeatPump_updated.osm"); + model->save(outPath, true); + + std::vector hpwhs = model->getObjectsByType("OS:WaterHeater:HeatPump"); + ASSERT_EQ(1u, hpwhs.size()); + const auto& hpwh = hpwhs.front(); + + EXPECT_EQ("Water Heater Heat Pump 1", hpwh.getString(1).get()); // Name + + // Before insertion: Inlet Air Mixer Schedule + ASSERT_TRUE(hpwh.getTarget(24)); + EXPECT_EQ("HPWH Inlet Air Mixer Schedule", hpwh.getTarget(24)->nameString()); + + // New Field: Tank Element Control Logic + EXPECT_EQ("Simultaneous", hpwh.getString(25).get()); + + // After insertion and also last field: Control Sensor Location In Stratified Tank + EXPECT_EQ("Heater2", hpwh.getString(26).get()); +} + +TEST_F(OSVersionFixture, update_3_9_0_to_3_9_1_GroundHeatExchangerVertical) { + openstudio::path path = resourcesPath() / toPath("osversion/3_9_1/test_vt_GroundHeatExchangerVertical.osm"); + osversion::VersionTranslator vt; + boost::optional model = vt.loadModel(path); + ASSERT_TRUE(model) << "Failed to load " << path; + + openstudio::path outPath = resourcesPath() / toPath("osversion/3_9_1/test_vt_GroundHeatExchangerVertical_updated.osm"); + model->save(outPath, true); + + std::vector ghe_verts = model->getObjectsByType("OS:GroundHeatExchanger:Vertical"); + ASSERT_EQ(1u, ghe_verts.size()); + WorkspaceObject ghe_vert = ghe_verts[0]; + + EXPECT_EQ(120, ghe_vert.getInt(5).get()); // Number of Bore Holes + EXPECT_EQ(1.0, ghe_vert.getDouble(6).get()); // Bore Hole Top Depth + EXPECT_EQ(76.2, ghe_vert.getDouble(7).get()); // Bore Hole Length +} diff --git a/src/utilities/xml/XMLValidator.cpp b/src/utilities/xml/XMLValidator.cpp index daa458496a4..46964a8d290 100644 --- a/src/utilities/xml/XMLValidator.cpp +++ b/src/utilities/xml/XMLValidator.cpp @@ -73,7 +73,7 @@ openstudio::path XMLValidator::schematronToXslt(const openstudio::path& schemaPa // This replicates what happens when you do: // from lxml.isoschematron import Schematron; - // s = Schematron(file='schematron.sct', store_xslt=True) + // s = Schematron(file='schematron.sch', store_xslt=True) // with open('schematron.xslt', 'w') as f: // f.write(str(s._validator_xslt)) @@ -170,7 +170,7 @@ XMLValidator::XMLValidator(const openstudio::path& schemaPath) : m_schemaPath(op } else if (schemaPath.extension() == ".xslt") { m_validatorType = XMLValidatorType::XSLTSchematron; logAndStore(Trace, "Treating schema as a XLST StyleSheet that derives from a Schematron."); - } else if ((schemaPath.extension() == ".xml") || (schemaPath.extension() == ".sct")) { + } else if ((schemaPath.extension() == ".xml") || (schemaPath.extension() == ".sch")) { m_validatorType = XMLValidatorType::Schematron; logAndStore(Trace, "Treating schema as a Schematron, converting to an XSLT StyleSheet."); // Let's use a temporary directory for this, so we avoid having two instances trying to write to the same file and we avoid issues where the @@ -262,7 +262,7 @@ bool XMLValidator::validate(const openstudio::path& xmlPath) { LOG_AND_THROW(logMessage); } - if (xmlPath.extension() == ".xml") { + if (xmlPath.extension() == ".xml" || xmlPath.extension() == ".sch" || xmlPath.extension() == ".xsd") { auto t_xmlPath = openstudio::filesystem::system_complete(xmlPath); m_xmlPath = t_xmlPath; } else { diff --git a/src/utilities/xml/XMLValidator.hpp b/src/utilities/xml/XMLValidator.hpp index 6296523e316..a8ccd7c2ae1 100644 --- a/src/utilities/xml/XMLValidator.hpp +++ b/src/utilities/xml/XMLValidator.hpp @@ -43,7 +43,7 @@ class UTILITIES_API XMLValidator * * The schemaPath extension is used to determine the type of validation to perform: * - `*.xsd` => XSD - * - `*.xml` or `*.sct` => Schematron (convert to XSLT then validate) + * - `*.xml` or `*.sch` => Schematron (convert to XSLT then validate) * - `*.xslt` => Schematron that is already transformed to an XSLT stylesheet */ explicit XMLValidator(const openstudio::path& schemaPath); ~XMLValidator();