diff --git a/developer/ruby/GenerateClass.rb b/developer/ruby/GenerateClass.rb index bfb3ec335f..2360c354c9 100644 --- a/developer/ruby/GenerateClass.rb +++ b/developer/ruby/GenerateClass.rb @@ -229,10 +229,18 @@ def forwardReverseTranslatorTests(options) subprojectInTitleCase = sourceFolders[0].gsub(/\b\w/){$&.upcase} -hpp << "#include <" << sourceFolders[0] << "/" << subprojectInTitleCase << "API.hpp>\n" +if sourceFolders[0] == "model" + hpp << "#include \"ModelAPI.hpp\"\n" +else + hpp << "#include <" << sourceFolders[0] << "/" << subprojectInTitleCase << "API.hpp>\n" +end cpp << "#include \"" << className << ".hpp\"\n" if pImpl - implHpp << "#include <" << sourceFolders[0] << "/" << subprojectInTitleCase << "API.hpp>\n" + if sourceFolders[0] == "model" + implHpp << "#include \"ModelAPI.hpp\"\n" + else + implHpp << "#include <" << sourceFolders[0] << "/" << subprojectInTitleCase << "API.hpp>\n" + end cpp << "#include \"" << className << "_Impl.hpp\"\n\n" end diff --git a/developer/ruby/SubProjectClassGenerators/ModelClassGenerator.rb b/developer/ruby/SubProjectClassGenerators/ModelClassGenerator.rb index 3b37f467bd..25ca64a8bd 100644 --- a/developer/ruby/SubProjectClassGenerators/ModelClassGenerator.rb +++ b/developer/ruby/SubProjectClassGenerators/ModelClassGenerator.rb @@ -10,6 +10,7 @@ # ###################################################################### +require 'pathname' require File.dirname(__FILE__) + '/SubProjectClassGenerator.rb' class ModelObjectField @@ -191,9 +192,9 @@ def getterName def setterArgumentName result = getterName - if (isObjectList?) - result = OpenStudio::toLowerCamelCase(objectListClassName) - end + # if (isObjectList?) + # result = OpenStudio::toLowerCamelCase(objectListClassName) + # end return result end @@ -237,7 +238,13 @@ def getterReturnType(forceOptional=nil) result = "std::string" end elsif isObjectList? - result = objectListClassName + if ["UnivariateFunctions", "BivariateFunctions"].include?(objectListClassName) + result = "Curve" + elsif objectListClassName == "Connection" + result = "Node" + else + result = objectListClassName + end elsif isNode? result = "Node" end @@ -316,6 +323,10 @@ def publicClassSetterType elsif isObjectList? if isSchedule? result = objectListClassName + "&" + elsif ["UnivariateFunctions", "BivariateFunctions"].include?(objectListClassName) + result = "const Curve&" + elsif objectListClassName == "Connection" + result = "const Node&" else result = "const " + objectListClassName + "&" end @@ -406,7 +417,6 @@ class ModelClassGenerator < SubProjectClassGenerator def initialize(className, baseClassName, pImpl, qobject, iddObjectType) super(className, baseClassName, pImpl, qobject) - @iddObjectType = iddObjectType @hasRealFields = false @hasScheduleFields = false @@ -421,14 +431,25 @@ def initialize(className, baseClassName, pImpl, qobject, iddObjectType) 'WaterToWaterComponent', 'ZoneHVACComponent'].include?(baseClassName) - if not @iddObjectType.empty? + if not iddObjectType.empty? require 'openstudio' raise "ModelObjects follow the pImpl idiom. Add -p to the command line." if not @pImpl - @iddObjectType = @iddObjectType.to_IddObjectType - @idfObject = OpenStudio::IdfObject.new(@iddObjectType) - @iddObject = @idfObject.iddObject + begin + @iddObjectType = iddObjectType.to_IddObjectType + idfObject = OpenStudio::IdfObject.new(iddObjectType) + @iddObject = idfObject.iddObject + rescue + idd_path = (Pathname.new(Dir.pwd) / "../../resources/model/OpenStudio.idd").realpath + raise "Could not find IDD on disk at #{idd_path}" if not idd_path.file? + iddFile = OpenStudio::IddFile.load(idd_path.to_s).get() + iddObject_ = iddFile.getObject(iddObjectType) + raise "Could not find #{iddObjectType} in the OpenStudio.idd at #{idd_path}" if iddObject_.empty? + @iddObject = iddObject_.get + @iddObjectType = FakeIddObjectType.new(iddObjectType) + end + # Find required non-extensible fields, split out by object-list and data @nonextensibleFields = [] @@ -463,7 +484,7 @@ def implHppIncludes() def cppIncludes() result = String.new - if @idfObject + if @iddObject # include object list field classes # include IddFactory.hxx if there is a non-boolean choice field @@ -476,6 +497,11 @@ def cppIncludes() preamble = "// TODO: Check the following class names against object getters and setters.\n" @objectListClassNames.each { |className| + if ["UnivariateFunctions", "BivariateFunctions"].include?(className) + className = "Curve" + elsif className == "Connection" + className = "Node" + end result << preamble result << "#include \"" << className << ".hpp\"\n" result << "#include \"" << className << "_Impl.hpp\"\n" @@ -528,9 +554,14 @@ def implHppOSForwardDeclarations def hppSubProjectForwardDeclarations result = String.new - if @idfObject + if @iddObject preamble = " // TODO: Check the following class names against object getters and setters.\n" @objectListClassNames.each { |className| + if ["UnivariateFunctions", "BivariateFunctions"].include?(className) + className = "Curve" + elsif className == "Connection" + className = "Node" + end result << preamble result << " class " << className << ";\n" preamble = "" @@ -542,7 +573,7 @@ def hppSubProjectForwardDeclarations def implHppSubProjectForwardDeclarations result = String.new - if @idfObject + if @iddObject result = hppSubProjectForwardDeclarations end return result @@ -551,7 +582,7 @@ def implHppSubProjectForwardDeclarations def hppPreClass() result = String.new - if @idfObject + if @iddObject result << " /** " << className << " is a " << baseClassName result << " that wraps the OpenStudio IDD object '" << iddObjectType.valueDescription result << "'. */\n" @@ -565,7 +596,7 @@ def hppPreClass() def hppConstructors() result = String.new - if @idfObject + if @iddObject if (not @iddObject.properties.unique) result << " explicit " << className << "(const Model& model);\n\n" @@ -581,7 +612,7 @@ def hppConstructors() def implHppConstructors() result = String.new - if @idfObject + if @iddObject implConstructorStart = String.new implConstructorStart << " " << @className << "_Impl(" @@ -608,7 +639,7 @@ def implHppConstructors() def cppConstructors() result = String.new - if @idfObject + if @iddObject implConstructorStart = String.new @@ -641,7 +672,7 @@ def cppConstructors() def cppPublicClassConstructors() result = String.new - if @idfObject + if @iddObject if (not iddObject.properties.unique) @@ -686,7 +717,7 @@ def cppPublicClassConstructors() def hppPublicMethods() result = String.new - if @idfObject + if @iddObject result << " static IddObjectType iddObjectType();\n\n" @@ -720,8 +751,6 @@ def hppPublicMethods() if field.canAutosize? result << " bool " << field.isAutosizeName << "() const;\n\n" - # Get the autosized value from the sql file - result << " boost::optional " << field.autosizedName << "();\n\n" end if field.canAutocalculate? @@ -786,6 +815,15 @@ def hppPublicMethods() result << " /** @name Other */\n" result << " //@{\n\n" + + # If there are any autosizeable fields, need to add bulk autosize + if @autosizedGetterNames.size > 0 + result << " // Autosize methods\n\n" + @autosizedGetterNames.each do |autosizedGetterName| + result << " boost::optional " << autosizedGetterName << "() const;\n" + end + end + result << " //@}\n" else @@ -798,7 +836,7 @@ def hppPublicMethods() def implHppPublicMethods() result = String.new - if @idfObject + if @iddObject result << " /** @name Virtual Methods */\n" @@ -813,6 +851,78 @@ def implHppPublicMethods() result << " virtual std::vector getScheduleTypeKeys(const Schedule& schedule) const override;\n\n" end + if @derivesHVACComponent || @baseClassName == 'ParentObject' || @baseClassName == 'ResourceObject' + result << " // TODO: You may need to override these since base is " << @baseClassName << "\n" + result << " // virtual ModelObject clone(Model model) const override;\n\n" + + result << " // virtual std::vector children() const override;\n" + result << " // virtual std::vector allowableChildTypes() const override;\n\n" + + result << " // virtual std::vector remove() override;\n\n" + end + + if @derivesHVACComponent + result << " // Overrides from "<< @baseClassName << "\n" + if @baseClassName == 'StraightComponent' + result << " virtual unsigned inletPort() const override;\n" + result << " virtual unsigned outletPort() const override;\n\n" + + result << " virtual bool addToNode(Node& node) override;\n\n" + elsif @baseClassName == 'WaterToWaterComponent' + result << " virtual unsigned supplyInletPort() const override;\n" + result << " virtual unsigned supplyOutletPort() const override;\n\n" + + result << " virtual unsigned demandInletPort() const override;\n" + result << " virtual unsigned demandOutletPort() const override;\n\n" + + result << " virtual bool addToNode(Node& node) override;\n\n" + + result << " // TODO: You may need to override these if you have a tertiary loop (Heat Recovery for eg)\n" + result << " // virtual unsigned tertiaryInletPort() const override;\n" + result << " // virtual unsigned tertiaryOutletPort() const override;\n" + result << " // virtual bool addToTertiaryNode(Node& node) override;\n\n" + + result << " virtual bool addToNode(Node& node) override;\n\n" + elsif @baseClassName == 'AirToAirComponent' + result << " virtual unsigned primaryAirInletPort() const override;\n" + result << " virtual unsigned primaryAirOutletPort() const override;\n\n" + + result << " virtual unsigned secondaryAirInletPort() const override;\n" + result << " virtual unsigned secondaryAirOutletPort() const override;\n\n" + + result << " virtual bool addToNode(Node& node) override;\n\n" + elsif @baseClassName == 'WaterToAirComponent' + result << " virtual unsigned supplyInletPort() const override;\n" + result << " virtual unsigned supplyOutletPort() const override;\n\n" + + result << " virtual unsigned demandInletPort() const override;\n" + result << " virtual unsigned demandOutletPort() const override;\n\n" + + result << " virtual bool addToNode(Node& node) override;\n\n" + elsif @baseClassName == 'ZoneHVACComponent' + result << " virtual unsigned inletPort() const override;\n" + result << " virtual unsigned outletPort() const override;\n\n" + + result << " virtual bool addToNode(Node& node) override;\n\n" + else + result << " // TODO: You probably need to override at least this one\n" + result << " virtual bool addToNode(Node& node) override;\n\n" + end + + result << " // TODO: If your component can be contained, override these\n" + result << " // virtual boost::optional containingHVACComponent() const override;\n" + result << " // virtual boost::optional containingZoneHVACComponent() const override;\n" + result << " // virtual boost::optional containingStraightComponent() const override;\n" + end + + # If there are any autosizeable fields, need to add bulk autosize + # and applySizingValues methods. These methods are assumed + # to be overrides from the method declared in HVACComponent. + if @autosizedGetterNames.size > 0 + result << " virtual void autosize() override;\n\n" + result << " virtual void applySizingValues() override;\n\n" + end + if @derivesHVACComponent result << " virtual ComponentType componentType() const override;\n" result << " virtual std::vector coolingFuelTypes() const override;\n" @@ -843,8 +953,6 @@ def implHppPublicMethods() if field.canAutosize? result << " bool " << field.isAutosizeName << "() const;\n\n" - # Get the autosized value from the sql file - result << " boost::optional " << field.autosizedName << "();\n\n" end if field.canAutocalculate? @@ -899,16 +1007,7 @@ def implHppPublicMethods() end } - # If there are any autosizeable fields, need to add bulk autosize - # and applySizingValues methods. These methods are assumed - # to be overrides from the method declared in HVACComponent. - if @autosizedGetterNames.size > 0 - result << " virtual void autosize() override;\n\n" - result << " virtual void applySizingValues() override;\n\n" - end - # Extensible field setters - if (@iddObject.properties.extensible) result << " // TODO: Handle this object's extensible fields.\n\n" end @@ -917,6 +1016,15 @@ def implHppPublicMethods() result << " /** @name Other */\n" result << " //@{\n\n" + + # If there are any autosizeable fields, need to add bulk autosize + if @autosizedGetterNames.size > 0 + result << " // Autosize methods\n\n" + @autosizedGetterNames.each do |autosizedGetterName| + result << " boost::optional " << autosizedGetterName << "() const;\n" + end + end + result << " //@}\n" else @@ -929,7 +1037,7 @@ def implHppPublicMethods() def cppPublicMethods() result = String.new - if @idfObject + if @iddObject result << " const std::vector& " << @className << "_Impl::outputVariableNames() const {\n" result << " static std::vector result;\n" @@ -1054,7 +1162,7 @@ def cppPublicMethods() result << " }\n\n" # Get the autosized value from the sql file - result << " boost::optional " << @className << "_Impl::" << field.autosizedName << "() {\n" + result << " boost::optional " << @className << "_Impl::" << field.autosizedName << "() const {\n" result << " return getAutosizedValue(\"TODO_CHECK_SQL #{field.name}\", \"#{field.sqlUnitString}\");\n" result << " }\n\n" @@ -1195,7 +1303,7 @@ def cppPublicMethods() @autosizedGetterNames.each do |name| setter_name = name.gsub('autosized','set') result << " if (boost::optional val_ = #{name}()) {\n" - result << " #{setter_name}(*val_));\n" + result << " #{setter_name}(*val_);\n" result << " }\n\n" end result << " }\n\n" @@ -1212,7 +1320,7 @@ def cppPublicMethods() def cppPublicClassPublicMethods() result = String.new - if @idfObject + if @iddObject result << " IddObjectType " << @className << "::iddObjectType() {\n" result << " return {IddObjectType::" << @iddObjectType.valueName << "};\n" @@ -1252,7 +1360,7 @@ def cppPublicClassPublicMethods() result << " }\n\n" # Get the autosized value from the sql file - result << " boost::optional " << @className << "::" << field.autosizedName << "() {\n" + result << " boost::optional " << @className << "::" << field.autosizedName << "() const {\n" result << " return getImpl()->#{field.autosizedName}();\n" result << " }\n\n" end @@ -1306,7 +1414,7 @@ def cppPublicClassPublicMethods() def hppProtectedFriends() result = String.new - if @idfObject + if @iddObject result << " friend class Model;\n" result << " friend class IdfObject;\n" result << " friend class openstudio::detail::IdfObject_Impl;\n" @@ -1318,7 +1426,7 @@ def hppProtectedFriends() def hppProtectedMethods() result = String.new - if @idfObject + if @iddObject if (@iddObject.properties.unique) result << " explicit " << @className << "(Model& model);\n\n" @@ -1334,7 +1442,7 @@ def hppProtectedMethods() def implHppProtectedMethods() result = String.new - if @idfObject + if @iddObject else result = super @@ -1346,7 +1454,7 @@ def implHppProtectedMethods() def cppProtectedMethods() result = String.new - if @idfObject + if @iddObject else result = super @@ -1358,7 +1466,7 @@ def cppProtectedMethods() def cppPublicClassProtectedMethods() result = String.new - if @idfObject + if @iddObject if (iddObject.properties.unique) result << " " << @className << "::" << @className << "(Model& model)\n" @@ -1375,7 +1483,7 @@ def cppPublicClassProtectedMethods() def implHppPrivateMethods() result = String.new - if @idfObject + if @iddObject # Optional getters for required objects any = false @@ -1403,7 +1511,7 @@ def implHppPrivateMethods() def cppPrivateMethods() result = String.new - if @idfObject + if @iddObject # Optional getters for required objects @nonextensibleFields.each { |field| @@ -1431,6 +1539,11 @@ def gtestIncludes() # Check for ObjectList fields, to see which we need to include @objectListClassNames.each { |className| + if ["UnivariateFunctions", "BivariateFunctions"].include?(className) + className = "Curve" + elsif className == "Connection" + className = "Node" + end result << preamble result << "#include \"../" << className << ".hpp\"\n" result << "#include \"../" << className << "_Impl.hpp\"\n\n" diff --git a/developer/ruby/SubProjectClassGenerators/SubProjectClassGenerator.rb b/developer/ruby/SubProjectClassGenerators/SubProjectClassGenerator.rb index c237529c18..fdb5e03377 100644 --- a/developer/ruby/SubProjectClassGenerators/SubProjectClassGenerator.rb +++ b/developer/ruby/SubProjectClassGenerators/SubProjectClassGenerator.rb @@ -285,3 +285,11 @@ def aux() end end + +class FakeIddObjectType + attr_accessor :valueName, :valueDescription + def initialize(iddObjectTypeName) + @valueName = iddObjectTypeName.gsub(':', '_') + @valueDescription = iddObjectTypeName + end +end diff --git a/developer/ruby/SubProjectClassGenerators/TranslatorGenerator.rb b/developer/ruby/SubProjectClassGenerators/TranslatorGenerator.rb index a4e5371df3..2b23e23038 100644 --- a/developer/ruby/SubProjectClassGenerators/TranslatorGenerator.rb +++ b/developer/ruby/SubProjectClassGenerators/TranslatorGenerator.rb @@ -2,6 +2,7 @@ # 2018-10-17 require 'openstudio' +require 'pathname' require_relative 'ModelClassGenerator.rb' require_relative 'FileHeader.rb' @@ -17,11 +18,27 @@ class TranslatorGenerator # @param EPIddName [String] The E+ IDD class, eg 'WaterHeater:Mixed' # @return [] returns true if successful, false if not def initialize(epIddName) - @iddObjectType = epIddName.to_IddObjectType - @idfObject = OpenStudio::IdfObject.new(@iddObjectType) - @iddObject = @idfObject.iddObject + + if epIddName == 'HeatPump:AirToWater:Heating:SpeedData' + epIddName = 'HeatPump:AirToWater' + end + begin + @iddObjectType = epIddName.to_IddObjectType + idfObject = OpenStudio::IdfObject.new(iddObjectType) + @iddObject = idfObject.iddObject + rescue + idd_path = (Pathname.new(Dir.pwd) / "../../resources/energyplus/ProposedEnergy+.idd").realpath + raise "Could not find IDD on disk at #{idd_path}" if not idd_path.file? + iddFile = OpenStudio::IddFile.load(idd_path.to_s).get() + iddObject_ = iddFile.getObject(epIddName) + raise "Could not find #{epIddName} in the OpenStudio.idd at #{idd_path}" if iddObject_.empty? + @iddObject = iddObject_.get + @iddObjectType = FakeIddObjectType.new(epIddName) + end + @className = @iddObjectType.valueName.gsub(/^OS/,'').gsub(':', '').gsub('_','') + # Find required non-extensible fields, split out by object-list and data # And Determine if the object has any autosizable fields @autosizedGetterNames = [] diff --git a/resources/energyplus/ProposedEnergy+.idd b/resources/energyplus/ProposedEnergy+.idd index 286c87f64c..2addbd7779 100644 --- a/resources/energyplus/ProposedEnergy+.idd +++ b/resources/energyplus/ProposedEnergy+.idd @@ -52392,19 +52392,13 @@ PlantLoop, \key LoopFlowCorrection \key None \default None - N6, \field Loop Circulation Time + N6; \field Loop Circulation Time \note This field is only used to autocalculate the Plant Loop Volume. \note Loop Volume = Loop Circulation Time * Maximum Loop Flow Rate \type real \units minutes \minimum 0.0 \default 2.0 - A19; \field Water Loop Type - \type choice - \key HotWater - \key ChilledWater - \key None - \default None CondenserLoop, \memo Defines a central plant condenser loop. CondenserLoop and PlantLoop are nearly diff --git a/resources/model/OpenStudio.idd b/resources/model/OpenStudio.idd index 36a55a1651..2f52488eb3 100644 --- a/resources/model/OpenStudio.idd +++ b/resources/model/OpenStudio.idd @@ -14878,6 +14878,400 @@ OS:HeatPump:AirToWater:FuelFired:Cooling, \required-field \note Minimum modulation level of the gas-fired heat pump. Typically less than 1.0 and slightly higher than the minimum part load ratio. +OS:HeatPump:AirToWater, + \memo air-to-water heat pump system which provides either chilled or hot water with single- or variable speed compressors. + \min-fields 20 + A1, \field Handle + \required-field + \type handle + A2, \field Name + \required-field + \type alpha + \note Enter a unique name for this air-to-water heat pump + A3, \field Operating Mode Control Method + \required-field + \type choice + \key ScheduledModes + \key EMSControlled + \key Load + A4, \field Operating Mode Control Option for Multiple Unit + \required-field + \note this determines how the operation mode is assigned when the load + \note is too large to be met by multiple heat pump units + \type choice + \key SingleMode + \key CoolingPriority + \key HeatingPriority + \key Balanced + \note SingleMode: all units will operate in the same mode (either all + \note cooling or all heating). If the heating load is larger, then all unit + \note operates in heating mode; otherwise, all operate in cooling mode. + \note CoolingPriority: each unit will be staged one by one based on + \note cooling demand first, after all cooling demand is met, if there are + \note remaining heat pumps, they will be used to fulfill the remaining heating + \note demand. + \note HeatingPriority: similar to the previous cooling priority one, + \note but will prioritize to meet the heating demand first + \note Balanced: balance the percent satisfied heating or cooling load + A5, \field Operating Mode Control Schedule Name + \note This field is used if the control method is set to ScheduledModes + \note Schedule values control operating mode: 0=off, 1=cooling, 2=heating + \type object-list + \object-list ScheduleNames + N1, \field Minimum Part Load Ratio + \required-field + \note Below this operating limit compressor cycling will occur + \type real + \minimum 0.0 + A6, \field Air Inlet Node Name + \type alpha + \note The node from which the heat pump draws its inlet air. + A7, \field Air Outlet Node Name + \type alpha + \note The node to which the heat pump sends its outlet air. + N2, \field Maximum Outdoor Dry Bulb Temperature For Defrost Operation + \required-field + \note defrost operation will not be active above this outdoor temperature + A8, \field Heat Pump Defrost Control + \required-field + \type choice + \key None + \key Timed + \key OnDemand + \key TimedEmpirical + N3, \field Heat Pump Defrost Time Period Fraction + \required-field + \type real + \minimum 0.0 + \note Fraction of time in defrost mode + \note only applicable if timed defrost control is specified + N4, \field Resistive Defrost Heater Capacity + \required-field + \type real + \minimum 0.0 + \units W + \note only applicable if resistive defrost strategy is specified + \ip-units W + A9, \field Defrost Energy Input Ratio Function of Temperature Curve Name + \type object-list + \object-list BivariateFunctions + \note univariate curve = a + b*OAT is typical, other univariate curves may be used + \note bivariate curve = a + b*WB + c*WB**2 + d*OAT + e*OAT**2 + f*WB*OAT + \note OAT = outdoor air dry-bulb temperature (C) + \note WB = wet-bulb temperature (C) of air entering the indoor coil + \note only required if Timed or OnDemand defrost strategy is specified + N5, \field Heat Pump Multiplier + \required-field + \type integer + \note intend to model modular heat pumps + A10, \field Control Type + \required-field + \type choice + \key FixedSpeed + \key VariableSpeed + N6, \field Crankcase Heater Capacity + \required-field + \type real + \minimum 0.0 + \units W + \note The compressor crankcase heater only operates when the dry-bulb temperature of air + \note surrounding the compressor is below the Maximum Ambient Temperature for Crankcase + \note Heater Operation and the heat pump is off. The ambient temperature uses the outdoor air temperature. + A11, \field Crankcase Heater Capacity Function of Temperature Curve Name + \note A Curve:* or Table:Lookup object encoding the relationship between + \note the crankcase heater capacity and the outdoor air temperature. When this field is + \note missing or empty, constant crankcase heater capacity will be assumed. + \type object-list + \object-list UnivariateFunctions + N7, \field Maximum Ambient Temperature for Crankcase Heater Operation + \required-field + \type real + \minimum 0.0 + \units C + \note The compressor crankcase heater only operates when the dry-bulb temperature of air + \note surrounding the compressor is below the Maximum Outdoor Temperature for Crankcase + \note Heater Operation and the unit is off. + A12, \field Heating Operation Mode + \type object-list + \object-list HeatPumpAirToWaterHeating + \note A HeatPump:AirToWater:Heating object. If not specified, heating is disabled. + A13; \field Cooling Operation Mode + \type object-list + \object-list HeatPumpAirToWaterCooling + \note A HeatPump:AirToWater:Cooling object. If not specified, cooling is disabled. + +OS:HeatPump:AirToWater:Heating, + \memo air-to-water heat pump system which provides hot water with single or variable speed compressors. + \min-fields 16 + A1, \field Handle + \required-field + \type handle + A2, \field Name + \required-field + \type alpha + \note Enter a unique name for this air-to-water heat pump + \reference HeatPumpAirToWaterHeating + \reference ConnectionObject + A3, \field Availability Schedule Name + \required-field + \type object-list + \object-list ScheduleNames + \note Enter the name of a schedule that defines the availability of the unit in heating mode + \note Schedule values of 0 denote the unit is off. All other values denote the unit is available + \note If this field is left blank, the unit is available the entire simulation + N1, \field Rated Inlet Air Temperature + \required-field + \type real + \units C + \note inlet air dry-bulb temperature corresponding to rated heat pump performance + \note (capacity, COP). + N2, \field Rated Air Flow Rate + \required-field + \type real + \units m3/s + \minimum> 0.0 + \autosizable + \note air flow rate corresponding to rated heat pump performance + \note (capacity, COP). + N3, \field Rated Leaving Water Temperature + \required-field + \type real + \units C + \note outlet water temperature corresponding to rated heat pump performance + \note (heating capacity, COP). + N4 , \field Rated Water Flow Rate + \required-field + \type real + \units m3/s + \ip-units gal/min + \minimum> 0.0 + \autosizable + \note Condenser water flow rate corresponding to rated heat pump performance + \note (capacity, COP). + N5, \field Minimum Outdoor Air Temperature + \required-field + \type real + \units C + \note Enter the minimum outdoor temperature allowed for heating operation + \note Heating is disabled below this temperature + N6, \field Maximum Outdoor Air Temperature + \required-field + \type real + \units C + \note Enter the maximum outdoor temperature allowed for heating operation + \note Heating is disabled above this temperature + A4, \field Minimum Leaving Water Temperature Curve Name + \type object-list + \object-list UnivariateFunctions + \note quadratic curve = a + b*OAT is typical, other univariate curves may be used + \note OAT = Outdoor Dry-Bulb Temperature + A5, \field Maximum Leaving Water Temperature Curve Name + \type object-list + \object-list UnivariateFunctions + \note quadratic curve = a + b*OAT is typical, other univariate curves may be used + \note OAT = Outdoor Dry-Bulb Temperature + N7, \field Sizing Factor + \required-field + \note Multiplies the autosized capacity and flow rates + \type real + \minimum> 0.0 + A6, \field Hot Water Inlet Node Name + \required-field + \type object-list + \object-list ConnectionNames + \note The node connects to the hot water loop + A7, \field Hot Water Outlet Node Name + \required-field + \type object-list + \object-list ConnectionNames + \note The node connects to the hot water loop + A8, \field Speed Data List + \required-field + \type object-list + \object-list ModelObjectLists + \note The minimum number of speeds is 0 (no heating) and the maximum is 5. + A9; \field Booster Mode On Speed + \note This field indicates that the heat pump has a booster mode enabled for heating operation. + \note The heat pump will operate at a higher capacity during heating operation. + \note If omitted the booster mode is not enabled. + \type object-list + \object-list HeatPumpAirToWaterHeatingSpeedData + +OS:HeatPump:AirToWater:Cooling, + \memo air-to-water heat pump system which provides hot water with single or variable speed compressors. + \min-fields 16 + A1, \field Handle + \required-field + \type handle + A2, \field Name + \required-field + \type alpha + \note Enter a unique name for this air-to-water heat pump + \reference HeatPumpAirToWaterCooling + \reference ConnectionObject + A3, \field Availability Schedule Name + \required-field + \type object-list + \object-list ScheduleNames + \note Enter the name of a schedule that defines the availability of the unit in cooling mode + \note Schedule values of 0 denote the unit is off. All other values denote the unit is available + \note If this field is left blank, the unit is available the entire simulation + N1, \field Rated Inlet Air Temperature + \required-field + \type real + \units C + \note inlet air dry-bulb temperature corresponding to rated heat pump performance + \note (capacity, COP). + N2, \field Rated Air Flow Rate + \required-field + \type real + \units m3/s + \minimum> 0.0 + \autosizable + \note air flow rate corresponding to rated heat pump performance + \note (capacity, COP). + N3, \field Rated Leaving Water Temperature + \required-field + \type real + \units C + \note outlet water temperature corresponding to rated heat pump performance + \note (cooling capacity, COP). + N4, \field Rated Water Flow Rate + \required-field + \type real + \units m3/s + \ip-units gal/min + \minimum> 0.0 + \autosizable + \note Condenser water flow rate corresponding to rated heat pump performance + \note (capacity, COP). + N5, \field Minimum Outdoor Air Temperature + \required-field + \type real + \units C + \note Enter the minimum outdoor temperature allowed for cooling operation + \note Cooling is disabled below this temperature + N6, \field Maximum Outdoor Air Temperature + \required-field + \type real + \units C + \note Enter the maximum outdoor temperature allowed for cooling operation + \note Cooling is disabled above this temperature + A4, \field Minimum Leaving Water Temperature Curve Name + \type object-list + \object-list UnivariateFunctions + \note quadratic curve = a + b*OAT is typical, other univariate curves may be used + \note OAT = Outdoor Dry-Bulb Temperature + A5, \field Maximum Leaving Water Temperature Curve Name + \type object-list + \object-list UnivariateFunctions + \note quadratic curve = a + b*OAT is typical, other univariate curves may be used + \note OAT = Outdoor Dry-Bulb Temperature + N7, \field Sizing Factor + \required-field + \note Multiplies the autosized capacity and flow rates + \type real + \minimum> 0.0 + A6, \field Chilled Water Inlet Node Name + \required-field + \type object-list + \object-list ConnectionNames + \note The node connects to the hot water loop + A7, \field Chilled Water Outlet Node Name + \required-field + \type object-list + \object-list ConnectionNames + \note The node connects to the hot water loop + A8, \field Speed Data List + \required-field + \type object-list + \object-list ModelObjectLists + \note The minimum number of speeds is 0 (no cooling) and the maximum is 5. + A9; \field Booster Mode On Speed + \note This field indicates that the heat pump has a booster mode enabled for cooling operation. + \note The heat pump will operate at a higher capacity during cooling operation. + \note If omitted the booster mode is not enabled. + \type object-list + \object-list HeatPumpAirToWaterCoolingSpeedData + +OS:HeatPump:AirToWater:Heating:SpeedData, + \min-fields 7 + A1, \field Handle + \required-field + \type handle + A2, \field Name + \required-field + \type alpha + \reference HeatPumpAirToWaterHeatingSpeedData + N1, \field Rated Heating Capacity + \required-field + \type real + \units W + \minimum> 0.0 + \autosizable + N2, \field Rated COP for Heating + \required-field + \type real + \minimum> 0.0 + \units W/W + A3, \field Normalized Heating Capacity Function of Temperature Curve Name + \required-field + \type object-list + \object-list BivariateFunctions + \note: CAPFT - Normalized Capacity Function of Temperature Curve Name, + \note which is a biquadratic curve or a lookup table of air and water temperature. + A4, \field Heating Energy Input Ratio Function of Temperature Curve Name + \required-field + \type object-list + \object-list BivariateFunctions + \note EIRFT - Fuel Energy Input Ratio Function of Temperature Curve Name, + \note which is a biquadratic curve or a lookup table of air and water temperature. + A5; \field Heating Energy Input Ratio Function of PLR Curve Name + \required-field + \type object-list + \object-list UnivariateFunctions + \note EIRFPLR - Fuel Energy Input Ratio Function of Part Load Ratio(PLR) Curve Name, + \note which is a cubic curve or a lookup table. + +OS:HeatPump:AirToWater:Cooling:SpeedData, + \min-fields 7 + A1, \field Handle + \required-field + \type handle + A2, \field Name + \required-field + \type alpha + \reference HeatPumpAirToWaterCoolingSpeedData + N1, \field Rated Cooling Capacity + \required-field + \type real + \units W + \minimum> 0.0 + \autosizable + N2, \field Rated COP for Cooling + \required-field + \type real + \minimum> 0.0 + \units W/W + A3, \field Normalized Cooling Capacity Function of Temperature Curve Name + \required-field + \type object-list + \object-list BivariateFunctions + \note: CAPFT - Normalized Capacity Function of Temperature Curve Name, + \note which is a biquadratic curve or a lookup table of air and water temperature. + A4, \field Cooling Energy Input Ratio Function of Temperature Curve Name + \required-field + \type object-list + \object-list BivariateFunctions + \note EIRFT - Fuel Energy Input Ratio Function of Temperature Curve Name, + \note which is a biquadratic curve or a lookup table of air and water temperature. + A5; \field Cooling Energy Input Ratio Function of PLR Curve Name + \required-field + \type object-list + \object-list UnivariateFunctions + \note EIRFPLR - Fuel Energy Input Ratio Function of Part Load Ratio(PLR) Curve Name, + \note which is a cubic curve or a lookup table. + OS:Chiller:Electric:EIR, \memo This chiller model is the empirical model from the DOE-2 building Energy \memo simulation program. Chiller performance at off-reference conditions is modeled diff --git a/src/energyplus/CMakeLists.txt b/src/energyplus/CMakeLists.txt index 37e9662cb5..ff14f8e58e 100644 --- a/src/energyplus/CMakeLists.txt +++ b/src/energyplus/CMakeLists.txt @@ -228,6 +228,7 @@ set(${target_name}_src ForwardTranslator/ForwardTranslateHeatExchangerDesiccantBalancedFlow.cpp ForwardTranslator/ForwardTranslateHeatExchangerDesiccantBalancedFlowPerformanceDataType1.cpp ForwardTranslator/ForwardTranslateHeatExchangerFluidToFluid.cpp + ForwardTranslator/ForwardTranslateHeatPumpAirToWater.cpp ForwardTranslator/ForwardTranslateHeatPumpAirToWaterFuelFiredHeating.cpp ForwardTranslator/ForwardTranslateHeatPumpAirToWaterFuelFiredCooling.cpp ForwardTranslator/ForwardTranslateHeatPumpWaterToWaterEquationFitCooling.cpp @@ -546,6 +547,7 @@ set(${target_name}_src ReverseTranslator/ReverseTranslateGeneratorMicroTurbine.cpp ReverseTranslator/ReverseTranslateGeneratorWindTurbine.cpp ReverseTranslator/ReverseTranslateHeatBalanceAlgorithm.cpp + ReverseTranslator/ReverseTranslateHeatPumpAirToWater.cpp ReverseTranslator/ReverseTranslateHotWaterEquipment.cpp ReverseTranslator/ReverseTranslateInternalMass.cpp ReverseTranslator/ReverseTranslateLights.cpp @@ -771,6 +773,7 @@ set(${target_name}_test_src Test/GroundHeatExchangerVertical_GTest.cpp Test/HeatExchangerDesiccantBalancedFlow_GTest.cpp Test/HeatExchangerDesiccantBalancedFlowPerformanceDataType1_GTest.cpp + Test/HeatPumpAirToWater_GTest.cpp Test/HeatPumpAirToWaterFuelFired_GTest.cpp Test/HeatPumpWaterToWaterEquationFit_GTest.cpp Test/HeatPumpPlantLoopEIR_GTest.cpp diff --git a/src/energyplus/ForwardTranslator.cpp b/src/energyplus/ForwardTranslator.cpp index 98575da2a6..7d31bd94b9 100644 --- a/src/energyplus/ForwardTranslator.cpp +++ b/src/energyplus/ForwardTranslator.cpp @@ -1953,7 +1953,21 @@ namespace energyplus { retVal = translateHeatExchangerFluidToFluid(mo); break; } - + case openstudio::IddObjectType::OS_HeatPump_AirToWater: { + auto mo = modelObject.cast(); + retVal = translateHeatPumpAirToWater(mo); + break; + } + case openstudio::IddObjectType::OS_HeatPump_AirToWater_Cooling: { + // no-op, just Log a Trace message + LOG(Trace, "HeatPumpAirToWaterCooling is not translated by itself but in the parent HeatPumpAirToWater"); + break; + } + case openstudio::IddObjectType::OS_HeatPump_AirToWater_Heating: { + // no-op, just Log a Trace message + LOG(Trace, "HeatPumpAirToWaterHeating is not translated by itself but in the parent HeatPumpAirToWater"); + break; + } case openstudio::IddObjectType::OS_HeatPump_AirToWater_FuelFired_Heating: { auto mo = modelObject.cast(); retVal = translateHeatPumpAirToWaterFuelFiredHeating(mo); diff --git a/src/energyplus/ForwardTranslator.hpp b/src/energyplus/ForwardTranslator.hpp index 24a8e5f209..b636a14335 100644 --- a/src/energyplus/ForwardTranslator.hpp +++ b/src/energyplus/ForwardTranslator.hpp @@ -264,6 +264,9 @@ namespace model { class HeatExchangerDesiccantBalancedFlow; class HeatExchangerDesiccantBalancedFlowPerformanceDataType1; class HeatExchangerFluidToFluid; + class HeatPumpAirToWater; + class HeatPumpAirToWaterCooling; + class HeatPumpAirToWaterHeating; class HeatPumpAirToWaterFuelFiredHeating; class HeatPumpAirToWaterFuelFiredCooling; class HeatPumpWaterToWaterEquationFitCooling; @@ -1121,6 +1124,10 @@ namespace energyplus { boost::optional translateHeatExchangerFluidToFluid(model::HeatExchangerFluidToFluid& modelObject); + boost::optional translateHeatPumpAirToWater(model::HeatPumpAirToWater& modelObject); + void translateHeatPumpAirToWaterCooling(model::HeatPumpAirToWaterCooling& modelObject, IdfObject& idfObject); + void translateHeatPumpAirToWaterHeating(model::HeatPumpAirToWaterHeating& modelObject, IdfObject& idfObject); + boost::optional translateHeatPumpAirToWaterFuelFiredHeating(model::HeatPumpAirToWaterFuelFiredHeating& modelObject); boost::optional translateHeatPumpAirToWaterFuelFiredCooling(model::HeatPumpAirToWaterFuelFiredCooling& modelObject); diff --git a/src/energyplus/ForwardTranslator/ForwardTranslateHeatPumpAirToWater.cpp b/src/energyplus/ForwardTranslator/ForwardTranslateHeatPumpAirToWater.cpp new file mode 100644 index 0000000000..901eddeb8c --- /dev/null +++ b/src/energyplus/ForwardTranslator/ForwardTranslateHeatPumpAirToWater.cpp @@ -0,0 +1,490 @@ +/*********************************************************************************************************************** +* OpenStudio(R), Copyright (c) Alliance for Sustainable Energy, LLC. +* See also https://openstudio.net/license +***********************************************************************************************************************/ + +#include "../ForwardTranslator.hpp" +#include "../../model/Model.hpp" + +#include "../../model/HeatPumpAirToWater.hpp" +#include "../../model/HeatPumpAirToWaterCooling.hpp" +#include "../../model/HeatPumpAirToWaterCoolingSpeedData.hpp" +#include "../../model/HeatPumpAirToWaterHeating.hpp" +#include "../../model/HeatPumpAirToWaterHeatingSpeedData.hpp" + +#include "../../model/Schedule.hpp" +#include "../../model/Curve.hpp" +#include "../../model/Node.hpp" +#include "../../model/Node_Impl.hpp" + +#include +#include + +using namespace openstudio::model; + +namespace openstudio { + +namespace energyplus { + + void ForwardTranslator::translateHeatPumpAirToWaterCooling(model::HeatPumpAirToWaterCooling& modelObject, IdfObject& idfObject) { + // Number of Speeds for Cooling: Optional Integer + const int numberofSpeedsforCooling = modelObject.plantLoop() ? modelObject.numberOfSpeeds() : 0; + idfObject.setInt(HeatPump_AirToWaterFields::NumberofSpeedsforCooling, numberofSpeedsforCooling); + if (numberofSpeedsforCooling == 0) { + return; + } + + // Availability Schedule Name Cooling: Optional Object + { + auto sch = modelObject.availabilitySchedule(); + if (boost::optional wo_ = translateAndMapModelObject(sch)) { + idfObject.setString(HeatPump_AirToWaterFields::AvailabilityScheduleNameCooling, wo_->nameString()); + } + } + + // Rated Inlet Air Temperature in Cooling Mode: Optional Double + const double ratedInletAirTemperature = modelObject.ratedInletAirTemperature(); + idfObject.setDouble(HeatPump_AirToWaterFields::RatedInletAirTemperatureinCoolingMode, ratedInletAirTemperature); + + // Rated Air Flow Rate + if (modelObject.isRatedAirFlowRateAutosized()) { + idfObject.setString(HeatPump_AirToWaterFields::RatedAirFlowRateinCoolingMode, "Autosize"); + } else { + // Rated Air Flow Rate in Cooling Mode: boost::optional + if (boost::optional ratedAirFlowRate_ = modelObject.ratedAirFlowRate()) { + idfObject.setDouble(HeatPump_AirToWaterFields::RatedAirFlowRateinCoolingMode, ratedAirFlowRate_.get()); + } + } + + // Rated Leaving Water Temperature in Cooling Mode: Optional Double + const double ratedLeavingWaterTemperature = modelObject.ratedLeavingWaterTemperature(); + idfObject.setDouble(HeatPump_AirToWaterFields::RatedLeavingWaterTemperatureinCoolingMode, ratedLeavingWaterTemperature); + + if (modelObject.isRatedWaterFlowRateAutosized()) { + idfObject.setString(HeatPump_AirToWaterFields::RatedWaterFlowRateinCoolingMode, "Autosize"); + } else { + // Rated Water Flow Rate in Cooling Mode: boost::optional + if (boost::optional ratedWaterFlowRate_ = modelObject.ratedWaterFlowRate()) { + idfObject.setDouble(HeatPump_AirToWaterFields::RatedWaterFlowRateinCoolingMode, ratedWaterFlowRate_.get()); + } + } + + // Minimum Outdoor Air Temperature in Cooling Mode: Optional Double + const double minimumOutdoorAirTemperature = modelObject.minimumOutdoorAirTemperature(); + idfObject.setDouble(HeatPump_AirToWaterFields::MinimumOutdoorAirTemperatureinCoolingMode, minimumOutdoorAirTemperature); + + // Maximum Outdoor Air Temperature in Cooling Mode: Optional Double + const double maximumOutdoorAirTemperature = modelObject.maximumOutdoorAirTemperature(); + idfObject.setDouble(HeatPump_AirToWaterFields::MaximumOutdoorAirTemperatureinCoolingMode, maximumOutdoorAirTemperature); + + // Minimum Leaving Water Temperature Curve Name in Cooling Mode: Optional Object + if (boost::optional minimumLeavingWaterTemperatureCurve_ = modelObject.minimumLeavingWaterTemperatureCurve()) { + if (boost::optional wo_ = translateAndMapModelObject(minimumLeavingWaterTemperatureCurve_.get())) { + idfObject.setString(HeatPump_AirToWaterFields::MinimumLeavingWaterTemperatureCurveNameinCoolingMode, wo_->nameString()); + } + } + + // Maximum Leaving Water Temperature Curve Name in Cooling Mode: Optional Object + if (boost::optional maximumLeavingWaterTemperatureCurve_ = modelObject.maximumLeavingWaterTemperatureCurve()) { + if (boost::optional wo_ = translateAndMapModelObject(maximumLeavingWaterTemperatureCurve_.get())) { + idfObject.setString(HeatPump_AirToWaterFields::MaximumLeavingWaterTemperatureCurveNameinCoolingMode, wo_->nameString()); + } + } + + // Sizing Factor for Cooling: Optional Double + const double sizingFactor = modelObject.sizingFactor(); + idfObject.setDouble(HeatPump_AirToWaterFields::SizingFactorforCooling, sizingFactor); + + // Chilled Water Inlet Node Name: Optional Node + if (boost::optional mo_ = modelObject.inletModelObject()) { + if (boost::optional node_ = mo_->optionalCast()) { + idfObject.setString(HeatPump_AirToWaterFields::ChilledWaterInletNodeName, node_->nameString()); + } + } + + // Chilled Water Outlet Node Name: Optional Node + if (boost::optional mo_ = modelObject.outletModelObject()) { + if (boost::optional node_ = mo_->optionalCast()) { + idfObject.setString(HeatPump_AirToWaterFields::ChilledWaterOutletNodeName, node_->nameString()); + } + } + + auto speeds = modelObject.speeds(); + unsigned startIndex = HeatPump_AirToWaterFields::RatedCoolingCapacityatSpeed1; + const unsigned number_fields = + static_cast(HeatPump_AirToWaterFields::CoolingEnergyInputRatioFunctionofPLRCurveNameatSpeed1) - startIndex + 1; + + auto getFieldIndex = [&startIndex](HeatPump_AirToWaterFields::domain field) -> unsigned { + return startIndex + (static_cast(field) - static_cast(HeatPump_AirToWaterFields::RatedCoolingCapacityatSpeed1)); + }; + + for (const auto& speed : speeds) { + + // Rated Cooling Capacity at Speed + if (speed.isRatedCoolingCapacityAutosized()) { + idfObject.setString(getFieldIndex(HeatPump_AirToWaterFields::RatedCoolingCapacityatSpeed1), "Autosize"); + } else { + idfObject.setDouble(getFieldIndex(HeatPump_AirToWaterFields::RatedCoolingCapacityatSpeed1), speed.ratedCoolingCapacity().get()); + } + + // Rated COP for Cooling at Speed + idfObject.setDouble(getFieldIndex(HeatPump_AirToWaterFields::RatedCOPforCoolingatSpeed1), speed.ratedCOPforCooling()); + + // Normalized Cooling Capacity Function of Temperature Curve Name at Speed + { + auto curve = speed.normalizedCoolingCapacityFunctionofTemperatureCurve(); + if (boost::optional wo_ = translateAndMapModelObject(curve)) { + idfObject.setString(getFieldIndex(HeatPump_AirToWaterFields::NormalizedCoolingCapacityFunctionofTemperatureCurveNameatSpeed1), + wo_->nameString()); + } + } + + // Cooling Energy Input Ratio Function of Temperature Curve Name at Speed + { + auto curve = speed.coolingEnergyInputRatioFunctionofTemperatureCurve(); + if (boost::optional wo_ = translateAndMapModelObject(curve)) { + idfObject.setString(getFieldIndex(HeatPump_AirToWaterFields::CoolingEnergyInputRatioFunctionofTemperatureCurveNameatSpeed1), + wo_->nameString()); + } + } + + // Cooling Energy Input Ratio Function of PLR Curve Name at Speed + { + auto curve = speed.coolingEnergyInputRatioFunctionofPLRCurve(); + if (boost::optional wo_ = translateAndMapModelObject(curve)) { + idfObject.setString(getFieldIndex(HeatPump_AirToWaterFields::CoolingEnergyInputRatioFunctionofPLRCurveNameatSpeed1), wo_->nameString()); + } + } + + startIndex += number_fields; + } + + // Booster Mode On Speed: Optional HeatPump:AirToWater:CoolingSpeedData + if (auto boosterModeOnSpeed_ = modelObject.boosterModeOnSpeed()) { + + idfObject.setString(HeatPump_AirToWaterFields::BoosterModeOnCooling, "Yes"); + + auto speed = std::move(*boosterModeOnSpeed_); + + // Rated Cooling Capacity at Speed + if (speed.isRatedCoolingCapacityAutosized()) { + idfObject.setString(HeatPump_AirToWaterFields::RatedCoolingCapacityinBoosterMode, "Autosize"); + } else { + idfObject.setDouble(HeatPump_AirToWaterFields::RatedCoolingCapacityinBoosterMode, speed.ratedCoolingCapacity().get()); + } + + // Rated COP for Cooling at Speed + idfObject.setDouble(HeatPump_AirToWaterFields::RatedCoolingCOPinBoosterMode, speed.ratedCOPforCooling()); + + // Normalized Cooling Capacity Function of Temperature Curve Name at Speed + { + auto curve = speed.normalizedCoolingCapacityFunctionofTemperatureCurve(); + if (boost::optional wo_ = translateAndMapModelObject(curve)) { + idfObject.setString(HeatPump_AirToWaterFields::NormalizedCoolingCapacityFunctionofTemperatureCurveNameinBoosterMode, wo_->nameString()); + } + } + + // Cooling Energy Input Ratio Function of Temperature Curve Name at Speed + { + auto curve = speed.coolingEnergyInputRatioFunctionofTemperatureCurve(); + if (boost::optional wo_ = translateAndMapModelObject(curve)) { + idfObject.setString(HeatPump_AirToWaterFields::CoolingEnergyInputRatioFunctionofTemperatureCurveNameinBoosterMode, wo_->nameString()); + } + } + + // Cooling Energy Input Ratio Function of PLR Curve Name at Speed + { + auto curve = speed.coolingEnergyInputRatioFunctionofPLRCurve(); + if (boost::optional wo_ = translateAndMapModelObject(curve)) { + idfObject.setString(HeatPump_AirToWaterFields::CoolingEnergyInputRatioFunctionofPLRCurveNameinBoosterMode, wo_->nameString()); + } + } + + } else { + // Reset the field to blank if no booster mode + idfObject.setString(HeatPump_AirToWaterFields::BoosterModeOnCooling, "No"); + } + } + + void ForwardTranslator::translateHeatPumpAirToWaterHeating(model::HeatPumpAirToWaterHeating& modelObject, IdfObject& idfObject) { + + const int numberofSpeedsforHeating = modelObject.plantLoop() ? modelObject.numberOfSpeeds() : 0; + idfObject.setInt(HeatPump_AirToWaterFields::NumberofSpeedsforHeating, numberofSpeedsforHeating); + if (numberofSpeedsforHeating == 0) { + return; + } + + // Availability Schedule Name Heating: Optional Object + { + auto sch = modelObject.availabilitySchedule(); + if (boost::optional wo_ = translateAndMapModelObject(sch)) { + idfObject.setString(HeatPump_AirToWaterFields::AvailabilityScheduleNameHeating, wo_->nameString()); + } + } + + // Rated Inlet Air Temperature in Heating Mode: Optional Double + const double ratedInletAirTemperature = modelObject.ratedInletAirTemperature(); + idfObject.setDouble(HeatPump_AirToWaterFields::RatedInletAirTemperatureinHeatingMode, ratedInletAirTemperature); + + // Rated Air Flow Rate + if (modelObject.isRatedAirFlowRateAutosized()) { + idfObject.setString(HeatPump_AirToWaterFields::RatedAirFlowRateinHeatingMode, "Autosize"); + } else { + // Rated Air Flow Rate in Heating Mode: boost::optional + if (boost::optional ratedAirFlowRate_ = modelObject.ratedAirFlowRate()) { + idfObject.setDouble(HeatPump_AirToWaterFields::RatedAirFlowRateinHeatingMode, ratedAirFlowRate_.get()); + } + } + + // Rated Leaving Water Temperature in Heating Mode: Optional Double + const double ratedLeavingWaterTemperature = modelObject.ratedLeavingWaterTemperature(); + idfObject.setDouble(HeatPump_AirToWaterFields::RatedLeavingWaterTemperatureinHeatingMode, ratedLeavingWaterTemperature); + + if (modelObject.isRatedWaterFlowRateAutosized()) { + idfObject.setString(HeatPump_AirToWaterFields::RatedWaterFlowRateinHeatingMode, "Autosize"); + } else { + // Rated Water Flow Rate in Heating Mode: boost::optional + if (boost::optional ratedWaterFlowRate_ = modelObject.ratedWaterFlowRate()) { + idfObject.setDouble(HeatPump_AirToWaterFields::RatedWaterFlowRateinHeatingMode, ratedWaterFlowRate_.get()); + } + } + + // Minimum Outdoor Air Temperature in Heating Mode: Optional Double + const double minimumOutdoorAirTemperature = modelObject.minimumOutdoorAirTemperature(); + idfObject.setDouble(HeatPump_AirToWaterFields::MinimumOutdoorAirTemperatureinHeatingMode, minimumOutdoorAirTemperature); + + // Maximum Outdoor Air Temperature in Heating Mode: Optional Double + const double maximumOutdoorAirTemperature = modelObject.maximumOutdoorAirTemperature(); + idfObject.setDouble(HeatPump_AirToWaterFields::MaximumOutdoorAirTemperatureinHeatingMode, maximumOutdoorAirTemperature); + + // Minimum Leaving Water Temperature Curve Name in Heating Mode: Optional Object + if (boost::optional minimumLeavingWaterTemperatureCurve_ = modelObject.minimumLeavingWaterTemperatureCurve()) { + if (boost::optional wo_ = translateAndMapModelObject(minimumLeavingWaterTemperatureCurve_.get())) { + idfObject.setString(HeatPump_AirToWaterFields::MinimumLeavingWaterTemperatureCurveNameinHeatingMode, wo_->nameString()); + } + } + + // Maximum Leaving Water Temperature Curve Name in Heating Mode: Optional Object + if (boost::optional maximumLeavingWaterTemperatureCurve_ = modelObject.maximumLeavingWaterTemperatureCurve()) { + if (boost::optional wo_ = translateAndMapModelObject(maximumLeavingWaterTemperatureCurve_.get())) { + idfObject.setString(HeatPump_AirToWaterFields::MaximumLeavingWaterTemperatureCurveNameinHeatingMode, wo_->nameString()); + } + } + + // Sizing Factor for Heating: Optional Double + const double sizingFactor = modelObject.sizingFactor(); + idfObject.setDouble(HeatPump_AirToWaterFields::SizingFactorforHeating, sizingFactor); + + // Hot Water Inlet Node Name: Optional Node + if (boost::optional mo_ = modelObject.inletModelObject()) { + if (boost::optional node_ = mo_->optionalCast()) { + idfObject.setString(HeatPump_AirToWaterFields::HotWaterInletNodeName, node_->nameString()); + } + } + + // Hot Water Outlet Node Name: Optional Node + if (boost::optional mo_ = modelObject.outletModelObject()) { + if (boost::optional node_ = mo_->optionalCast()) { + idfObject.setString(HeatPump_AirToWaterFields::HotWaterOutletNodeName, node_->nameString()); + } + } + + auto speeds = modelObject.speeds(); + unsigned startIndex = HeatPump_AirToWaterFields::RatedHeatingCapacityatSpeed1; + const unsigned number_fields = + static_cast(HeatPump_AirToWaterFields::HeatingEnergyInputRatioFunctionofPLRCurveNameatSpeed1) - startIndex + 1; + + auto getFieldIndex = [&startIndex](HeatPump_AirToWaterFields::domain field) -> unsigned { + return startIndex + (static_cast(field) - static_cast(HeatPump_AirToWaterFields::RatedHeatingCapacityatSpeed1)); + }; + + for (const auto& speed : speeds) { + + // Rated Heating Capacity at Speed + if (speed.isRatedHeatingCapacityAutosized()) { + idfObject.setString(getFieldIndex(HeatPump_AirToWaterFields::RatedHeatingCapacityatSpeed1), "Autosize"); + } else { + idfObject.setDouble(getFieldIndex(HeatPump_AirToWaterFields::RatedHeatingCapacityatSpeed1), speed.ratedHeatingCapacity().get()); + } + + // Rated COP for Heating at Speed + idfObject.setDouble(getFieldIndex(HeatPump_AirToWaterFields::RatedCOPforHeatingatSpeed1), speed.ratedCOPforHeating()); + + // Normalized Heating Capacity Function of Temperature Curve Name at Speed + { + auto curve = speed.normalizedHeatingCapacityFunctionofTemperatureCurve(); + if (boost::optional wo_ = translateAndMapModelObject(curve)) { + idfObject.setString(getFieldIndex(HeatPump_AirToWaterFields::NormalizedHeatingCapacityFunctionofTemperatureCurveNameatSpeed1), + wo_->nameString()); + } + } + + // Heating Energy Input Ratio Function of Temperature Curve Name at Speed + { + auto curve = speed.heatingEnergyInputRatioFunctionofTemperatureCurve(); + if (boost::optional wo_ = translateAndMapModelObject(curve)) { + idfObject.setString(getFieldIndex(HeatPump_AirToWaterFields::HeatingEnergyInputRatioFunctionofTemperatureCurveNameatSpeed1), + wo_->nameString()); + } + } + + // Heating Energy Input Ratio Function of PLR Curve Name at Speed + { + auto curve = speed.heatingEnergyInputRatioFunctionofPLRCurve(); + if (boost::optional wo_ = translateAndMapModelObject(curve)) { + idfObject.setString(getFieldIndex(HeatPump_AirToWaterFields::HeatingEnergyInputRatioFunctionofPLRCurveNameatSpeed1), wo_->nameString()); + } + } + + startIndex += number_fields; + } + + // Booster Mode On Speed: Optional HeatPump:AirToWater:HeatingSpeedData + if (auto boosterModeOnSpeed_ = modelObject.boosterModeOnSpeed()) { + + idfObject.setString(HeatPump_AirToWaterFields::BoosterModeOnHeating, "Yes"); + + auto speed = std::move(*boosterModeOnSpeed_); + + // Rated Heating Capacity at Speed + if (speed.isRatedHeatingCapacityAutosized()) { + idfObject.setString(HeatPump_AirToWaterFields::RatedHeatingCapacityinBoosterMode, "Autosize"); + } else { + idfObject.setDouble(HeatPump_AirToWaterFields::RatedHeatingCapacityinBoosterMode, speed.ratedHeatingCapacity().get()); + } + + // Rated COP for Heating at Speed + idfObject.setDouble(HeatPump_AirToWaterFields::RatedHeatingCOPinBoosterMode, speed.ratedCOPforHeating()); + + // Normalized Heating Capacity Function of Temperature Curve Name at Speed + { + auto curve = speed.normalizedHeatingCapacityFunctionofTemperatureCurve(); + if (boost::optional wo_ = translateAndMapModelObject(curve)) { + idfObject.setString(HeatPump_AirToWaterFields::NormalizedHeatingCapacityFunctionofTemperatureCurveNameinBoosterMode, wo_->nameString()); + } + } + + // Heating Energy Input Ratio Function of Temperature Curve Name at Speed + { + auto curve = speed.heatingEnergyInputRatioFunctionofTemperatureCurve(); + if (boost::optional wo_ = translateAndMapModelObject(curve)) { + idfObject.setString(HeatPump_AirToWaterFields::HeatingEnergyInputRatioFunctionofTemperatureCurveNameinBoosterMode, wo_->nameString()); + } + } + + // Heating Energy Input Ratio Function of PLR Curve Name at Speed + { + auto curve = speed.heatingEnergyInputRatioFunctionofPLRCurve(); + if (boost::optional wo_ = translateAndMapModelObject(curve)) { + idfObject.setString(HeatPump_AirToWaterFields::HeatingEnergyInputRatioFunctionofPLRCurveNameinBoosterMode, wo_->nameString()); + } + } + + } else { + // Reset the field to blank if no booster mode + idfObject.setString(HeatPump_AirToWaterFields::BoosterModeOnHeating, "No"); + } + } + + boost::optional ForwardTranslator::translateHeatPumpAirToWater(model::HeatPumpAirToWater& modelObject) { + + // Instantiate an IdfObject of the class to store the values + IdfObject idfObject = createRegisterAndNameIdfObject(openstudio::IddObjectType::HeatPump_AirToWater, modelObject); + + // Operating Mode Control Method: Optional String + const std::string operatingModeControlMethod = modelObject.operatingModeControlMethod(); + idfObject.setString(HeatPump_AirToWaterFields::OperatingModeControlMethod, operatingModeControlMethod); + + // Operating Mode Control Option for Multiple Unit: Optional String + const std::string operatingModeControlOptionforMultipleUnit = modelObject.operatingModeControlOptionforMultipleUnit(); + idfObject.setString(HeatPump_AirToWaterFields::OperatingModeControlOptionforMultipleUnit, operatingModeControlOptionforMultipleUnit); + + // Operating Mode Control Schedule Name: Optional Object + if (boost::optional operatingModeControlSchedule_ = modelObject.operatingModeControlSchedule()) { + if (boost::optional wo_ = translateAndMapModelObject(operatingModeControlSchedule_.get())) { + idfObject.setString(HeatPump_AirToWaterFields::OperatingModeControlScheduleName, wo_->nameString()); + } + } + + // Minimum Part Load Ratio: Optional Double + const double minimumPartLoadRatio = modelObject.minimumPartLoadRatio(); + idfObject.setDouble(HeatPump_AirToWaterFields::MinimumPartLoadRatio, minimumPartLoadRatio); + + // Air Inlet Node Name: Required Node + { + IdfObject oaNodeListIdf(openstudio::IddObjectType::OutdoorAir_NodeList); + { + const std::string airInletNodeName = modelObject.airInletNodeName().value_or(modelObject.nameString() + " Air Inlet Node"); + idfObject.setString(HeatPump_AirToWaterFields::AirInletNodeName, airInletNodeName); + oaNodeListIdf.setString(0, airInletNodeName); + } + + // Air Outlet Node Name: Required Node + { + const std::string airOutletNodeName = modelObject.airOutletNodeName().value_or(modelObject.nameString() + " Air Outlet Node"); + idfObject.setString(HeatPump_AirToWaterFields::AirOutletNodeName, airOutletNodeName); + oaNodeListIdf.setString(1, airOutletNodeName); + } + m_idfObjects.emplace_back(std::move(oaNodeListIdf)); + } + + // Maximum Outdoor Dry Bulb Temperature For Defrost Operation: Optional Double + const double maximumOutdoorDryBulbTemperatureForDefrostOperation = modelObject.maximumOutdoorDryBulbTemperatureForDefrostOperation(); + idfObject.setDouble(HeatPump_AirToWaterFields::MaximumOutdoorDryBulbTemperatureForDefrostOperation, + maximumOutdoorDryBulbTemperatureForDefrostOperation); + + // Heat Pump Defrost Control: Optional String + const std::string heatPumpDefrostControl = modelObject.heatPumpDefrostControl(); + idfObject.setString(HeatPump_AirToWaterFields::HeatPumpDefrostControl, heatPumpDefrostControl); + + // Heat Pump Defrost Time Period Fraction: Optional Double + const double heatPumpDefrostTimePeriodFraction = modelObject.heatPumpDefrostTimePeriodFraction(); + idfObject.setDouble(HeatPump_AirToWaterFields::HeatPumpDefrostTimePeriodFraction, heatPumpDefrostTimePeriodFraction); + + const double resistiveDefrostHeaterCapacity = modelObject.resistiveDefrostHeaterCapacity(); + idfObject.setDouble(HeatPump_AirToWaterFields::ResistiveDefrostHeaterCapacity, resistiveDefrostHeaterCapacity); + + // Defrost Energy Input Ratio Function of Temperature Curve Name: Optional Object + if (boost::optional defrostEnergyInputRatioFunctionofTemperatureCurve_ = modelObject.defrostEnergyInputRatioFunctionofTemperatureCurve()) { + if (boost::optional wo_ = translateAndMapModelObject(defrostEnergyInputRatioFunctionofTemperatureCurve_.get())) { + idfObject.setString(HeatPump_AirToWaterFields::DefrostEnergyInputRatioFunctionofTemperatureCurveName, wo_->nameString()); + } + } + + // Heat Pump Multiplier: Optional Integer + const int heatPumpMultiplier = modelObject.heatPumpMultiplier(); + idfObject.setInt(HeatPump_AirToWaterFields::HeatPumpMultiplier, heatPumpMultiplier); + + // Control Type: Optional String + const std::string controlType = modelObject.controlType(); + idfObject.setString(HeatPump_AirToWaterFields::ControlType, controlType); + + // Crankcase Heater Capacity: Optional Double + const double crankcaseHeaterCapacity = modelObject.crankcaseHeaterCapacity(); + idfObject.setDouble(HeatPump_AirToWaterFields::CrankcaseHeaterCapacity, crankcaseHeaterCapacity); + + // Crankcase Heater Capacity Function of Temperature Curve Name: Optional Object + if (boost::optional crankcaseHeaterCapacityFunctionofTemperatureCurve_ = modelObject.crankcaseHeaterCapacityFunctionofTemperatureCurve()) { + if (boost::optional wo_ = translateAndMapModelObject(crankcaseHeaterCapacityFunctionofTemperatureCurve_.get())) { + idfObject.setString(HeatPump_AirToWaterFields::CrankcaseHeaterCapacityFunctionofTemperatureCurveName, wo_->nameString()); + } + } + + // Maximum Ambient Temperature for Crankcase Heater Operation: Optional Double + const double maximumAmbientTemperatureforCrankcaseHeaterOperation = modelObject.maximumAmbientTemperatureforCrankcaseHeaterOperation(); + idfObject.setDouble(HeatPump_AirToWaterFields::MaximumAmbientTemperatureforCrankcaseHeaterOperation, + maximumAmbientTemperatureforCrankcaseHeaterOperation); + + if (auto awhp_cc_ = modelObject.coolingOperationMode()) { + translateHeatPumpAirToWaterCooling(*awhp_cc_, idfObject); + } + + if (auto awhp_cc_ = modelObject.heatingOperationMode()) { + translateHeatPumpAirToWaterHeating(*awhp_cc_, idfObject); + } + + return idfObject; + } // End of translate function + +} // end namespace energyplus +} // end namespace openstudio diff --git a/src/energyplus/ForwardTranslator/ForwardTranslatePlantEquipmentOperationSchemes.cpp b/src/energyplus/ForwardTranslator/ForwardTranslatePlantEquipmentOperationSchemes.cpp index 64e7c03496..b8c933a424 100644 --- a/src/energyplus/ForwardTranslator/ForwardTranslatePlantEquipmentOperationSchemes.cpp +++ b/src/energyplus/ForwardTranslator/ForwardTranslatePlantEquipmentOperationSchemes.cpp @@ -79,6 +79,10 @@ #include "../../model/PlantComponentTemperatureSource_Impl.hpp" #include "../../model/PlantComponentUserDefined.hpp" #include "../../model/PlantComponentUserDefined_Impl.hpp" +#include "../../model/HeatPumpAirToWaterCooling.hpp" +#include "../../model/HeatPumpAirToWaterCooling_Impl.hpp" +#include "../../model/HeatPumpAirToWaterHeating.hpp" +#include "../../model/HeatPumpAirToWaterHeating_Impl.hpp" #include "../../model/HeatPumpAirToWaterFuelFiredHeating.hpp" #include "../../model/HeatPumpAirToWaterFuelFiredHeating_Impl.hpp" #include "../../model/HeatPumpAirToWaterFuelFiredCooling.hpp" @@ -356,6 +360,16 @@ namespace energyplus { result = mo.loadSideReferenceFlowRate(); break; } + case openstudio::IddObjectType::OS_HeatPump_AirToWater_Cooling: { + auto mo = component.cast(); + result = mo.ratedWaterFlowRate(); + break; + } + case openstudio::IddObjectType::OS_HeatPump_AirToWater_Heating: { + auto mo = component.cast(); + result = mo.ratedWaterFlowRate(); + break; + } case openstudio::IddObjectType::OS_HeatPump_AirToWater_FuelFired_Heating: { auto mo = component.cast(); result = mo.designFlowRate(); @@ -395,6 +409,16 @@ namespace energyplus { } break; } + case openstudio::IddObjectType::OS_HeatPump_AirToWater_Cooling: { + auto awhp = component.cast(); + result = awhp.containingHVACComponent(); + break; + } + case openstudio::IddObjectType::OS_HeatPump_AirToWater_Heating: { + auto awhp = component.cast(); + result = awhp.containingHVACComponent(); + break; + } default: { break; } diff --git a/src/energyplus/ForwardTranslator/ForwardTranslatePlantLoop.cpp b/src/energyplus/ForwardTranslator/ForwardTranslatePlantLoop.cpp index a3dc363a53..8f1b02795e 100644 --- a/src/energyplus/ForwardTranslator/ForwardTranslatePlantLoop.cpp +++ b/src/energyplus/ForwardTranslator/ForwardTranslatePlantLoop.cpp @@ -62,6 +62,10 @@ #include "../../model/GroundHeatExchangerHorizontalTrench_Impl.hpp" #include "../../model/HeatExchangerFluidToFluid.hpp" #include "../../model/HeatExchangerFluidToFluid_Impl.hpp" +#include "../../model/HeatPumpAirToWaterCooling.hpp" +#include "../../model/HeatPumpAirToWaterCooling_Impl.hpp" +#include "../../model/HeatPumpAirToWaterHeating.hpp" +#include "../../model/HeatPumpAirToWaterHeating_Impl.hpp" #include "../../model/WaterToAirComponent.hpp" #include "../../model/WaterToAirComponent_Impl.hpp" #include "../../model/WaterToWaterComponent.hpp" @@ -296,6 +300,26 @@ namespace energyplus { } } } + + // special case for HeatPumpAirToWaterCooling and HeatPumpAirToWaterHeating + if (auto awhp_cc_ = modelObject.optionalCast()) { + if (auto awhp = awhp_cc_->containingHVACComponent()) { + if (auto idf_awhp = this->translateAndMapModelObject(*awhp)) { + objectName = idf_awhp->name().get(); + iddType = idf_awhp->iddObject().name(); + } + } + } + + if (auto awhp_hc_ = modelObject.optionalCast()) { + if (auto awhp = awhp_hc_->containingHVACComponent()) { + if (auto idf_awhp = this->translateAndMapModelObject(*awhp)) { + objectName = idf_awhp->name().get(); + iddType = idf_awhp->iddObject().name(); + } + } + } + } else if (auto waterToAirComponent = modelObject.optionalCast()) { if (loop.optionalCast()) { inletNode = waterToAirComponent->waterInletModelObject()->optionalCast(); diff --git a/src/energyplus/ReverseTranslator.cpp b/src/energyplus/ReverseTranslator.cpp index d066a659f6..215467453a 100644 --- a/src/energyplus/ReverseTranslator.cpp +++ b/src/energyplus/ReverseTranslator.cpp @@ -593,6 +593,10 @@ namespace energyplus { modelObject = translateHeatBalanceAlgorithm(workspaceObject); break; } + case openstudio::IddObjectType::HeatPump_AirToWater: { + modelObject = translateHeatPumpAirToWater(workspaceObject); + break; + } case openstudio::IddObjectType::HotWaterEquipment: { modelObject = translateHotWaterEquipment(workspaceObject); break; diff --git a/src/energyplus/ReverseTranslator.hpp b/src/energyplus/ReverseTranslator.hpp index 30900afd44..1daa56b5cf 100644 --- a/src/energyplus/ReverseTranslator.hpp +++ b/src/energyplus/ReverseTranslator.hpp @@ -203,6 +203,10 @@ namespace energyplus { boost::optional translateHeatBalanceAlgorithm(const WorkspaceObject& workspaceObject); + boost::optional translateHeatPumpAirToWater(const WorkspaceObject& workspaceObject); + boost::optional translateHeatPumpAirToWaterCooling(const WorkspaceObject& workspaceObject); + boost::optional translateHeatPumpAirToWaterHeating(const WorkspaceObject& workspaceObject); + boost::optional translateHotWaterEquipment(const WorkspaceObject& workspaceObject); boost::optional translateInternalMass(const WorkspaceObject& workspaceObject); diff --git a/src/energyplus/ReverseTranslator/ReverseTranslateHeatPumpAirToWater.cpp b/src/energyplus/ReverseTranslator/ReverseTranslateHeatPumpAirToWater.cpp new file mode 100644 index 0000000000..9cb97d53f6 --- /dev/null +++ b/src/energyplus/ReverseTranslator/ReverseTranslateHeatPumpAirToWater.cpp @@ -0,0 +1,601 @@ +/*********************************************************************************************************************** +* OpenStudio(R), Copyright (c) Alliance for Sustainable Energy, LLC. +* See also https://openstudio.net/license +***********************************************************************************************************************/ + +#include "../ReverseTranslator.hpp" + +#include "../../model/HeatPumpAirToWater.hpp" +#include "../../model/HeatPumpAirToWaterCooling.hpp" +#include "../../model/HeatPumpAirToWaterCooling_Impl.hpp" +#include "../../model/HeatPumpAirToWaterCoolingSpeedData.hpp" +#include "../../model/HeatPumpAirToWaterHeating.hpp" +#include "../../model/HeatPumpAirToWaterHeating_Impl.hpp" +#include "../../model/HeatPumpAirToWaterHeatingSpeedData.hpp" + +#include "../../model/Schedule.hpp" +#include "../../model/Schedule_Impl.hpp" + +#include "../../model/Curve.hpp" +#include "../../model/Curve_Impl.hpp" + +#include +#include + +using namespace openstudio::model; + +namespace openstudio { + +namespace energyplus { + + boost::optional ReverseTranslator::translateHeatPumpAirToWaterCooling(const WorkspaceObject& workspaceObject) { + // Number of Speeds for Cooling: Optional Integer + int numberofSpeedsforCooling = workspaceObject.getInt(HeatPump_AirToWaterFields::NumberofSpeedsforCooling).value_or(0); + if (numberofSpeedsforCooling == 0) { + return boost::none; + } + + HeatPumpAirToWaterCooling awhp_cc(m_model); + awhp_cc.setName(workspaceObject.nameString() + " Cooling"); + + // Availability Schedule Name Cooling: Optional Object + if (boost::optional wo_ = workspaceObject.getTarget(HeatPump_AirToWaterFields::AvailabilityScheduleNameCooling)) { + if (boost::optional mo_ = translateAndMapWorkspaceObject(wo_.get())) { + if (boost::optional availabilityScheduleCooling_ = mo_->optionalCast()) { + awhp_cc.setAvailabilitySchedule(availabilityScheduleCooling_.get()); + } else { + LOG(Warn, workspaceObject.briefDescription() << " has a wrong type for 'Availability Schedule Name Cooling'"); + } + } + } + + // Rated Inlet Air Temperature in Cooling Mode: Optional Double + if (boost::optional ratedInletAirTemperature_ = + workspaceObject.getDouble(HeatPump_AirToWaterFields::RatedInletAirTemperatureinCoolingMode)) { + awhp_cc.setRatedInletAirTemperature(ratedInletAirTemperature_.get()); + } + + // Rated Air Flow Rate in Cooling Mode: Optional Double + if (boost::optional ratedAirFlowRate_ = workspaceObject.getDouble(HeatPump_AirToWaterFields::RatedAirFlowRateinCoolingMode)) { + awhp_cc.setRatedAirFlowRate(ratedAirFlowRate_.get()); + } + + // Rated Leaving Water Temperature in Cooling Mode: Optional Double + if (boost::optional ratedLeavingWaterTemperature_ = + workspaceObject.getDouble(HeatPump_AirToWaterFields::RatedLeavingWaterTemperatureinCoolingMode)) { + awhp_cc.setRatedLeavingWaterTemperature(ratedLeavingWaterTemperature_.get()); + } + + // Rated Water Flow Rate in Cooling Mode: Optional Double + if (boost::optional ratedWaterFlowRate_ = workspaceObject.getDouble(HeatPump_AirToWaterFields::RatedWaterFlowRateinCoolingMode)) { + awhp_cc.setRatedWaterFlowRate(ratedWaterFlowRate_.get()); + } + + // Minimum Outdoor Air Temperature in Cooling Mode: Optional Double + if (boost::optional minimumOutdoorAirTemperature_ = + workspaceObject.getDouble(HeatPump_AirToWaterFields::MinimumOutdoorAirTemperatureinCoolingMode)) { + awhp_cc.setMinimumOutdoorAirTemperature(minimumOutdoorAirTemperature_.get()); + } + + // Maximum Outdoor Air Temperature in Cooling Mode: Optional Double + if (boost::optional maximumOutdoorAirTemperature_ = + workspaceObject.getDouble(HeatPump_AirToWaterFields::MaximumOutdoorAirTemperatureinCoolingMode)) { + awhp_cc.setMaximumOutdoorAirTemperature(maximumOutdoorAirTemperature_.get()); + } + + // Minimum Leaving Water Temperature Curve Name in Cooling Mode: Optional Object + if (boost::optional wo_ = + workspaceObject.getTarget(HeatPump_AirToWaterFields::MinimumLeavingWaterTemperatureCurveNameinCoolingMode)) { + if (boost::optional mo_ = translateAndMapWorkspaceObject(wo_.get())) { + if (boost::optional minimumLeavingWaterTemperatureCurveinCoolingMode_ = mo_->optionalCast()) { + awhp_cc.setMinimumLeavingWaterTemperatureCurve(minimumLeavingWaterTemperatureCurveinCoolingMode_.get()); + } else { + LOG(Warn, workspaceObject.briefDescription() << " has a wrong type for 'Minimum Leaving Water Temperature Curve Name in Cooling Mode'"); + } + } + } + // Maximum Leaving Water Temperature Curve Name in Cooling Mode: Optional Object + if (boost::optional wo_ = + workspaceObject.getTarget(HeatPump_AirToWaterFields::MaximumLeavingWaterTemperatureCurveNameinCoolingMode)) { + if (boost::optional mo_ = translateAndMapWorkspaceObject(wo_.get())) { + if (boost::optional maximumLeavingWaterTemperatureCurveinCoolingMode_ = mo_->optionalCast()) { + awhp_cc.setMaximumLeavingWaterTemperatureCurve(maximumLeavingWaterTemperatureCurveinCoolingMode_.get()); + } else { + LOG(Warn, workspaceObject.briefDescription() << " has a wrong type for 'Maximum Leaving Water Temperature Curve Name in Cooling Mode'"); + } + } + } + + // Sizing Factor for Cooling: Optional Double + if (boost::optional sizingFactor_ = workspaceObject.getDouble(HeatPump_AirToWaterFields::SizingFactorforCooling)) { + awhp_cc.setSizingFactor(sizingFactor_.get()); + } + + unsigned startIndex = HeatPump_AirToWaterFields::RatedCoolingCapacityatSpeed1; + const unsigned number_fields = + static_cast(HeatPump_AirToWaterFields::CoolingEnergyInputRatioFunctionofPLRCurveNameatSpeed1) - startIndex + 1; + + auto getFieldIndex = [&startIndex](HeatPump_AirToWaterFields::domain field) -> unsigned { + return startIndex + (static_cast(field) - static_cast(HeatPump_AirToWaterFields::RatedCoolingCapacityatSpeed1)); + }; + for (int i = 1; i <= numberofSpeedsforCooling; ++i) { + + HeatPumpAirToWaterCoolingSpeedData speed(m_model); + speed.setName(awhp_cc.nameString() + " Speed " + std::to_string(i)); + // Rated Cooling Capacity at Speed + if (boost::optional ratedCoolingCapacityatSpeed_ = + workspaceObject.getDouble(getFieldIndex(HeatPump_AirToWaterFields::RatedCoolingCapacityatSpeed1))) { + speed.setRatedCoolingCapacity(ratedCoolingCapacityatSpeed_.get()); + } + + // Rated COP for Cooling at Speed + if (boost::optional ratedCOPforCoolingatSpeed_ = + workspaceObject.getDouble(getFieldIndex(HeatPump_AirToWaterFields::RatedCOPforCoolingatSpeed1))) { + speed.setRatedCOPforCooling(ratedCOPforCoolingatSpeed_.get()); + } + + // Normalized Cooling Capacity Function of Temperature Curve Name at Speed + if (boost::optional wo_ = + workspaceObject.getTarget(getFieldIndex(HeatPump_AirToWaterFields::NormalizedCoolingCapacityFunctionofTemperatureCurveNameatSpeed1))) { + if (boost::optional mo_ = translateAndMapWorkspaceObject(wo_.get())) { + if (boost::optional normalizedCoolingCapacityFunctionofTemperatureCurveatSpeed_ = mo_->optionalCast()) { + speed.normalizedCoolingCapacityFunctionofTemperatureCurve().remove(); + speed.setNormalizedCoolingCapacityFunctionofTemperatureCurve(normalizedCoolingCapacityFunctionofTemperatureCurveatSpeed_.get()); + } else { + LOG(Warn, workspaceObject.briefDescription() + << " has a wrong type for 'Normalized Cooling Capacity Function of Temperature Curve Name at Speed " << i); + } + } + } + + // Cooling Energy Input Ratio Function of Temperature Curve Name at Speed + if (boost::optional wo_ = + workspaceObject.getTarget(getFieldIndex(HeatPump_AirToWaterFields::CoolingEnergyInputRatioFunctionofTemperatureCurveNameatSpeed1))) { + if (boost::optional mo_ = translateAndMapWorkspaceObject(wo_.get())) { + if (boost::optional coolingEnergyInputRatioFunctionofTemperatureCurveatSpeed_ = mo_->optionalCast()) { + speed.coolingEnergyInputRatioFunctionofTemperatureCurve().remove(); + speed.setCoolingEnergyInputRatioFunctionofTemperatureCurve(coolingEnergyInputRatioFunctionofTemperatureCurveatSpeed_.get()); + } else { + LOG(Warn, workspaceObject.briefDescription() + << " has a wrong type for 'Cooling Energy Input Ratio Function of Temperature Curve Name at Speed " << i); + } + } + } + + // Cooling Energy Input Ratio Function of PLR Curve Name at Speed + if (boost::optional wo_ = + workspaceObject.getTarget(getFieldIndex(HeatPump_AirToWaterFields::CoolingEnergyInputRatioFunctionofPLRCurveNameatSpeed1))) { + if (boost::optional mo_ = translateAndMapWorkspaceObject(wo_.get())) { + if (boost::optional coolingEnergyInputRatioFunctionofPLRCurveatSpeed_ = mo_->optionalCast()) { + speed.coolingEnergyInputRatioFunctionofPLRCurve().remove(); + speed.setCoolingEnergyInputRatioFunctionofPLRCurve(coolingEnergyInputRatioFunctionofPLRCurveatSpeed_.get()); + } else { + LOG(Warn, workspaceObject.briefDescription() + << " has a wrong type for 'Cooling Energy Input Ratio Function of PLR Curve Name at Speed " << i) + } + } + } + + if (!awhp_cc.addSpeed(speed)) { + LOG(Error, "For " << workspaceObject.briefDescription() << ", cannot add speed data " << i << " to HeatPumpAirToWaterCooling"); + } + + startIndex += number_fields; + } + + // Booster Mode On Cooling: Optional Boolean + const std::string boosterModeOnCooling = workspaceObject.getString(HeatPump_AirToWaterFields::BoosterModeOnCooling, true).get(); + if (istringEqual("Yes", boosterModeOnCooling)) { + HeatPumpAirToWaterCoolingSpeedData speed(m_model); + speed.setName(awhp_cc.nameString() + " Booster Mode On Speed"); + + // Rated Cooling Capacity in Booster Mode: Optional Double + if (boost::optional ratedCoolingCapacityinBoosterMode_ = + workspaceObject.getDouble(HeatPump_AirToWaterFields::RatedCoolingCapacityinBoosterMode)) { + speed.setRatedCoolingCapacity(ratedCoolingCapacityinBoosterMode_.get()); + } + + // Rated Cooling COP in Booster Mode: Optional Double + if (boost::optional ratedCoolingCOPinBoosterMode_ = + workspaceObject.getDouble(HeatPump_AirToWaterFields::RatedCoolingCOPinBoosterMode)) { + speed.setRatedCOPforCooling(ratedCoolingCOPinBoosterMode_.get()); + } + + // Normalized Cooling Capacity Function of Temperature Curve Name in Booster Mode: Optional Object + if (boost::optional wo_ = + workspaceObject.getTarget(HeatPump_AirToWaterFields::NormalizedCoolingCapacityFunctionofTemperatureCurveNameinBoosterMode)) { + if (boost::optional mo_ = translateAndMapWorkspaceObject(wo_.get())) { + if (boost::optional normalizedCoolingCapacityFunctionofTemperatureCurveinBoosterMode_ = mo_->optionalCast()) { + speed.normalizedCoolingCapacityFunctionofTemperatureCurve().remove(); + speed.setNormalizedCoolingCapacityFunctionofTemperatureCurve(normalizedCoolingCapacityFunctionofTemperatureCurveinBoosterMode_.get()); + } else { + LOG(Warn, workspaceObject.briefDescription() + << " has a wrong type for 'Normalized Cooling Capacity Function of Temperature Curve Name in Booster Mode'"); + } + } + } + // Cooling Energy Input Ratio Function of Temperature Curve Name in Booster Mode: Optional Object + if (boost::optional wo_ = + workspaceObject.getTarget(HeatPump_AirToWaterFields::CoolingEnergyInputRatioFunctionofTemperatureCurveNameinBoosterMode)) { + if (boost::optional mo_ = translateAndMapWorkspaceObject(wo_.get())) { + if (boost::optional coolingEnergyInputRatioFunctionofTemperatureCurveinBoosterMode_ = mo_->optionalCast()) { + speed.coolingEnergyInputRatioFunctionofTemperatureCurve().remove(); + speed.setCoolingEnergyInputRatioFunctionofTemperatureCurve(coolingEnergyInputRatioFunctionofTemperatureCurveinBoosterMode_.get()); + } else { + LOG(Warn, workspaceObject.briefDescription() + << " has a wrong type for 'Cooling Energy Input Ratio Function of Temperature Curve Name in Booster Mode'"); + } + } + } + // Cooling Energy Input Ratio Function of PLR Curve Name in Booster Mode: Optional Object + if (boost::optional wo_ = + workspaceObject.getTarget(HeatPump_AirToWaterFields::CoolingEnergyInputRatioFunctionofPLRCurveNameinBoosterMode)) { + if (boost::optional mo_ = translateAndMapWorkspaceObject(wo_.get())) { + if (boost::optional coolingEnergyInputRatioFunctionofPLRCurveinBoosterMode_ = mo_->optionalCast()) { + speed.coolingEnergyInputRatioFunctionofPLRCurve().remove(); + speed.setCoolingEnergyInputRatioFunctionofPLRCurve(coolingEnergyInputRatioFunctionofPLRCurveinBoosterMode_.get()); + } else { + LOG(Warn, workspaceObject.briefDescription() + << " has a wrong type for 'Cooling Energy Input Ratio Function of PLR Curve Name in Booster Mode'"); + } + } + } + if (!awhp_cc.setBoosterModeOnSpeed(speed)) { + LOG(Error, "For " << workspaceObject.briefDescription() << ", cannot set booster mode on speed to HeatPumpAirToWaterCooling"); + } + } + + return awhp_cc; + } + + boost::optional ReverseTranslator::translateHeatPumpAirToWaterHeating(const WorkspaceObject& workspaceObject) { + // Number of Speeds for Heating: Optional Integer + int numberofSpeedsforHeating = workspaceObject.getInt(HeatPump_AirToWaterFields::NumberofSpeedsforHeating).value_or(0); + if (numberofSpeedsforHeating == 0) { + return boost::none; + } + + HeatPumpAirToWaterHeating awhp_hc(m_model); + awhp_hc.setName(workspaceObject.nameString() + " Heating"); + + // Availability Schedule Name Heating: Optional Object + if (boost::optional wo_ = workspaceObject.getTarget(HeatPump_AirToWaterFields::AvailabilityScheduleNameHeating)) { + if (boost::optional mo_ = translateAndMapWorkspaceObject(wo_.get())) { + if (boost::optional availabilityScheduleHeating_ = mo_->optionalCast()) { + awhp_hc.setAvailabilitySchedule(availabilityScheduleHeating_.get()); + } else { + LOG(Warn, workspaceObject.briefDescription() << " has a wrong type for 'Availability Schedule Name Heating'"); + } + } + } + + // Rated Inlet Air Temperature in Heating Mode: Optional Double + if (boost::optional ratedInletAirTemperature_ = + workspaceObject.getDouble(HeatPump_AirToWaterFields::RatedInletAirTemperatureinHeatingMode)) { + awhp_hc.setRatedInletAirTemperature(ratedInletAirTemperature_.get()); + } + + // Rated Air Flow Rate in Heating Mode: Optional Double + if (boost::optional ratedAirFlowRate_ = workspaceObject.getDouble(HeatPump_AirToWaterFields::RatedAirFlowRateinHeatingMode)) { + awhp_hc.setRatedAirFlowRate(ratedAirFlowRate_.get()); + } + + // Rated Leaving Water Temperature in Heating Mode: Optional Double + if (boost::optional ratedLeavingWaterTemperature_ = + workspaceObject.getDouble(HeatPump_AirToWaterFields::RatedLeavingWaterTemperatureinHeatingMode)) { + awhp_hc.setRatedLeavingWaterTemperature(ratedLeavingWaterTemperature_.get()); + } + + // Rated Water Flow Rate in Heating Mode: Optional Double + if (boost::optional ratedWaterFlowRate_ = workspaceObject.getDouble(HeatPump_AirToWaterFields::RatedWaterFlowRateinHeatingMode)) { + awhp_hc.setRatedWaterFlowRate(ratedWaterFlowRate_.get()); + } + + // Minimum Outdoor Air Temperature in Heating Mode: Optional Double + if (boost::optional minimumOutdoorAirTemperature_ = + workspaceObject.getDouble(HeatPump_AirToWaterFields::MinimumOutdoorAirTemperatureinHeatingMode)) { + awhp_hc.setMinimumOutdoorAirTemperature(minimumOutdoorAirTemperature_.get()); + } + + // Maximum Outdoor Air Temperature in Heating Mode: Optional Double + if (boost::optional maximumOutdoorAirTemperature_ = + workspaceObject.getDouble(HeatPump_AirToWaterFields::MaximumOutdoorAirTemperatureinHeatingMode)) { + awhp_hc.setMaximumOutdoorAirTemperature(maximumOutdoorAirTemperature_.get()); + } + + // Minimum Leaving Water Temperature Curve Name in Heating Mode: Optional Object + if (boost::optional wo_ = + workspaceObject.getTarget(HeatPump_AirToWaterFields::MinimumLeavingWaterTemperatureCurveNameinHeatingMode)) { + if (boost::optional mo_ = translateAndMapWorkspaceObject(wo_.get())) { + if (boost::optional minimumLeavingWaterTemperatureCurveinHeatingMode_ = mo_->optionalCast()) { + awhp_hc.setMinimumLeavingWaterTemperatureCurve(minimumLeavingWaterTemperatureCurveinHeatingMode_.get()); + } else { + LOG(Warn, workspaceObject.briefDescription() << " has a wrong type for 'Minimum Leaving Water Temperature Curve Name in Heating Mode'"); + } + } + } + // Maximum Leaving Water Temperature Curve Name in Heating Mode: Optional Object + if (boost::optional wo_ = + workspaceObject.getTarget(HeatPump_AirToWaterFields::MaximumLeavingWaterTemperatureCurveNameinHeatingMode)) { + if (boost::optional mo_ = translateAndMapWorkspaceObject(wo_.get())) { + if (boost::optional maximumLeavingWaterTemperatureCurveinHeatingMode_ = mo_->optionalCast()) { + awhp_hc.setMaximumLeavingWaterTemperatureCurve(maximumLeavingWaterTemperatureCurveinHeatingMode_.get()); + } else { + LOG(Warn, workspaceObject.briefDescription() << " has a wrong type for 'Maximum Leaving Water Temperature Curve Name in Heating Mode'"); + } + } + } + + // Sizing Factor for Heating: Optional Double + if (boost::optional sizingFactor_ = workspaceObject.getDouble(HeatPump_AirToWaterFields::SizingFactorforHeating)) { + awhp_hc.setSizingFactor(sizingFactor_.get()); + } + + unsigned startIndex = HeatPump_AirToWaterFields::RatedHeatingCapacityatSpeed1; + const unsigned number_fields = + static_cast(HeatPump_AirToWaterFields::HeatingEnergyInputRatioFunctionofPLRCurveNameatSpeed1) - startIndex + 1; + + auto getFieldIndex = [&startIndex](HeatPump_AirToWaterFields::domain field) -> unsigned { + return startIndex + (static_cast(field) - static_cast(HeatPump_AirToWaterFields::RatedHeatingCapacityatSpeed1)); + }; + for (int i = 1; i <= numberofSpeedsforHeating; ++i) { + + HeatPumpAirToWaterHeatingSpeedData speed(m_model); + speed.setName(awhp_hc.nameString() + " Speed " + std::to_string(i)); + // Rated Heating Capacity at Speed + if (boost::optional ratedHeatingCapacityatSpeed_ = + workspaceObject.getDouble(getFieldIndex(HeatPump_AirToWaterFields::RatedHeatingCapacityatSpeed1))) { + speed.setRatedHeatingCapacity(ratedHeatingCapacityatSpeed_.get()); + } + + // Rated COP for Heating at Speed + if (boost::optional ratedCOPforHeatingatSpeed_ = + workspaceObject.getDouble(getFieldIndex(HeatPump_AirToWaterFields::RatedCOPforHeatingatSpeed1))) { + speed.setRatedCOPforHeating(ratedCOPforHeatingatSpeed_.get()); + } + + // Normalized Heating Capacity Function of Temperature Curve Name at Speed + if (boost::optional wo_ = + workspaceObject.getTarget(getFieldIndex(HeatPump_AirToWaterFields::NormalizedHeatingCapacityFunctionofTemperatureCurveNameatSpeed1))) { + if (boost::optional mo_ = translateAndMapWorkspaceObject(wo_.get())) { + if (boost::optional normalizedHeatingCapacityFunctionofTemperatureCurveatSpeed_ = mo_->optionalCast()) { + speed.normalizedHeatingCapacityFunctionofTemperatureCurve().remove(); + speed.setNormalizedHeatingCapacityFunctionofTemperatureCurve(normalizedHeatingCapacityFunctionofTemperatureCurveatSpeed_.get()); + } else { + LOG(Warn, workspaceObject.briefDescription() + << " has a wrong type for 'Normalized Heating Capacity Function of Temperature Curve Name at Speed " << i); + } + } + } + + // Heating Energy Input Ratio Function of Temperature Curve Name at Speed + if (boost::optional wo_ = + workspaceObject.getTarget(getFieldIndex(HeatPump_AirToWaterFields::HeatingEnergyInputRatioFunctionofTemperatureCurveNameatSpeed1))) { + if (boost::optional mo_ = translateAndMapWorkspaceObject(wo_.get())) { + if (boost::optional heatingEnergyInputRatioFunctionofTemperatureCurveatSpeed_ = mo_->optionalCast()) { + speed.heatingEnergyInputRatioFunctionofTemperatureCurve().remove(); + speed.setHeatingEnergyInputRatioFunctionofTemperatureCurve(heatingEnergyInputRatioFunctionofTemperatureCurveatSpeed_.get()); + } else { + LOG(Warn, workspaceObject.briefDescription() + << " has a wrong type for 'Heating Energy Input Ratio Function of Temperature Curve Name at Speed " << i); + } + } + } + + // Heating Energy Input Ratio Function of PLR Curve Name at Speed + if (boost::optional wo_ = + workspaceObject.getTarget(getFieldIndex(HeatPump_AirToWaterFields::HeatingEnergyInputRatioFunctionofPLRCurveNameatSpeed1))) { + if (boost::optional mo_ = translateAndMapWorkspaceObject(wo_.get())) { + if (boost::optional heatingEnergyInputRatioFunctionofPLRCurveatSpeed_ = mo_->optionalCast()) { + speed.heatingEnergyInputRatioFunctionofPLRCurve().remove(); + speed.setHeatingEnergyInputRatioFunctionofPLRCurve(heatingEnergyInputRatioFunctionofPLRCurveatSpeed_.get()); + } else { + LOG(Warn, workspaceObject.briefDescription() + << " has a wrong type for 'Heating Energy Input Ratio Function of PLR Curve Name at Speed " << i) + } + } + } + + if (!awhp_hc.addSpeed(speed)) { + LOG(Error, "For " << workspaceObject.briefDescription() << ", cannot add speed data " << i << " to HeatPumpAirToWaterHeating"); + } + + startIndex += number_fields; + } + + // Booster Mode On Heating: Optional Boolean + const std::string boosterModeOnHeating = workspaceObject.getString(HeatPump_AirToWaterFields::BoosterModeOnHeating, true).get(); + if (istringEqual("Yes", boosterModeOnHeating)) { + HeatPumpAirToWaterHeatingSpeedData speed(m_model); + speed.setName(awhp_hc.nameString() + " Booster Mode On Speed"); + + // Rated Heating Capacity in Booster Mode: Optional Double + if (boost::optional ratedHeatingCapacityinBoosterMode_ = + workspaceObject.getDouble(HeatPump_AirToWaterFields::RatedHeatingCapacityinBoosterMode)) { + speed.setRatedHeatingCapacity(ratedHeatingCapacityinBoosterMode_.get()); + } + + // Rated Heating COP in Booster Mode: Optional Double + if (boost::optional ratedHeatingCOPinBoosterMode_ = + workspaceObject.getDouble(HeatPump_AirToWaterFields::RatedHeatingCOPinBoosterMode)) { + speed.setRatedCOPforHeating(ratedHeatingCOPinBoosterMode_.get()); + } + + // Normalized Heating Capacity Function of Temperature Curve Name in Booster Mode: Optional Object + if (boost::optional wo_ = + workspaceObject.getTarget(HeatPump_AirToWaterFields::NormalizedHeatingCapacityFunctionofTemperatureCurveNameinBoosterMode)) { + if (boost::optional mo_ = translateAndMapWorkspaceObject(wo_.get())) { + if (boost::optional normalizedHeatingCapacityFunctionofTemperatureCurveinBoosterMode_ = mo_->optionalCast()) { + speed.normalizedHeatingCapacityFunctionofTemperatureCurve().remove(); + speed.setNormalizedHeatingCapacityFunctionofTemperatureCurve(normalizedHeatingCapacityFunctionofTemperatureCurveinBoosterMode_.get()); + } else { + LOG(Warn, workspaceObject.briefDescription() + << " has a wrong type for 'Normalized Heating Capacity Function of Temperature Curve Name in Booster Mode'"); + } + } + } + // Heating Energy Input Ratio Function of Temperature Curve Name in Booster Mode: Optional Object + if (boost::optional wo_ = + workspaceObject.getTarget(HeatPump_AirToWaterFields::HeatingEnergyInputRatioFunctionofTemperatureCurveNameinBoosterMode)) { + if (boost::optional mo_ = translateAndMapWorkspaceObject(wo_.get())) { + if (boost::optional heatingEnergyInputRatioFunctionofTemperatureCurveinBoosterMode_ = mo_->optionalCast()) { + speed.heatingEnergyInputRatioFunctionofTemperatureCurve().remove(); + speed.setHeatingEnergyInputRatioFunctionofTemperatureCurve(heatingEnergyInputRatioFunctionofTemperatureCurveinBoosterMode_.get()); + } else { + LOG(Warn, workspaceObject.briefDescription() + << " has a wrong type for 'Heating Energy Input Ratio Function of Temperature Curve Name in Booster Mode'"); + } + } + } + // Heating Energy Input Ratio Function of PLR Curve Name in Booster Mode: Optional Object + if (boost::optional wo_ = + workspaceObject.getTarget(HeatPump_AirToWaterFields::HeatingEnergyInputRatioFunctionofPLRCurveNameinBoosterMode)) { + if (boost::optional mo_ = translateAndMapWorkspaceObject(wo_.get())) { + if (boost::optional heatingEnergyInputRatioFunctionofPLRCurveinBoosterMode_ = mo_->optionalCast()) { + speed.heatingEnergyInputRatioFunctionofPLRCurve().remove(); + speed.setHeatingEnergyInputRatioFunctionofPLRCurve(heatingEnergyInputRatioFunctionofPLRCurveinBoosterMode_.get()); + } else { + LOG(Warn, workspaceObject.briefDescription() + << " has a wrong type for 'Heating Energy Input Ratio Function of PLR Curve Name in Booster Mode'"); + } + } + } + if (!awhp_hc.setBoosterModeOnSpeed(speed)) { + LOG(Error, "For " << workspaceObject.briefDescription() << ", cannot set booster mode on speed to HeatPumpAirToWaterHeating"); + } + } + + return awhp_hc; + } + + boost::optional ReverseTranslator::translateHeatPumpAirToWater(const WorkspaceObject& workspaceObject) { + + openstudio::model::HeatPumpAirToWater modelObject(m_model); + + // Name + if (boost::optional name_ = workspaceObject.name()) { + modelObject.setName(name_.get()); + } + + // Operating Mode Control Method: Optional String + if (boost::optional operatingModeControlMethod_ = workspaceObject.getString(HeatPump_AirToWaterFields::OperatingModeControlMethod)) { + modelObject.setOperatingModeControlMethod(operatingModeControlMethod_.get()); + } + + // Operating Mode Control Option for Multiple Unit: Optional String + if (boost::optional operatingModeControlOptionforMultipleUnit_ = + workspaceObject.getString(HeatPump_AirToWaterFields::OperatingModeControlOptionforMultipleUnit)) { + modelObject.setOperatingModeControlOptionforMultipleUnit(operatingModeControlOptionforMultipleUnit_.get()); + } + + // Operating Mode Control Schedule Name: Optional Object + if (boost::optional wo_ = workspaceObject.getTarget(HeatPump_AirToWaterFields::OperatingModeControlScheduleName)) { + if (boost::optional mo_ = translateAndMapWorkspaceObject(wo_.get())) { + if (boost::optional operatingModeControlSchedule_ = mo_->optionalCast()) { + modelObject.setOperatingModeControlSchedule(operatingModeControlSchedule_.get()); + } else { + LOG(Warn, workspaceObject.briefDescription() << " has a wrong type for 'Operating Mode Control Schedule Name'"); + } + } + } + + // Minimum Part Load Ratio: Optional Double + if (boost::optional minimumPartLoadRatio_ = workspaceObject.getDouble(HeatPump_AirToWaterFields::MinimumPartLoadRatio)) { + modelObject.setMinimumPartLoadRatio(minimumPartLoadRatio_.get()); + } + + // Air Inlet Node Name: Required Node + if (auto airInletNodeName_ = workspaceObject.getString(HeatPump_AirToWaterFields::AirInletNodeName)) { + modelObject.setAirInletNodeName(airInletNodeName_.get()); + } + + // Air Outlet Node Name: Required Node + if (auto airOutletNodeName_ = workspaceObject.getString(HeatPump_AirToWaterFields::AirOutletNodeName)) { + modelObject.setAirOutletNodeName(airOutletNodeName_.get()); + } + // Hot Water Inlet Node Name: Optional Node + + // Hot Water Outlet Node Name: Optional Node + + // Chilled Water Inlet Node Name: Optional Node + + // Chilled Water Outlet Node Name: Optional Node + + // Maximum Outdoor Dry Bulb Temperature For Defrost Operation: Optional Double + if (boost::optional maximumOutdoorDryBulbTemperatureForDefrostOperation_ = + workspaceObject.getDouble(HeatPump_AirToWaterFields::MaximumOutdoorDryBulbTemperatureForDefrostOperation)) { + modelObject.setMaximumOutdoorDryBulbTemperatureForDefrostOperation(maximumOutdoorDryBulbTemperatureForDefrostOperation_.get()); + } + + // Heat Pump Defrost Control: Optional String + if (boost::optional heatPumpDefrostControl_ = workspaceObject.getString(HeatPump_AirToWaterFields::HeatPumpDefrostControl)) { + modelObject.setHeatPumpDefrostControl(heatPumpDefrostControl_.get()); + } + + // Heat Pump Defrost Time Period Fraction: Optional Double + if (boost::optional heatPumpDefrostTimePeriodFraction_ = + workspaceObject.getDouble(HeatPump_AirToWaterFields::HeatPumpDefrostTimePeriodFraction)) { + modelObject.setHeatPumpDefrostTimePeriodFraction(heatPumpDefrostTimePeriodFraction_.get()); + } + + // Resistive Defrost Heater Capacity: Optional Double + if (boost::optional resistiveDefrostHeaterCapacity_ = + workspaceObject.getDouble(HeatPump_AirToWaterFields::ResistiveDefrostHeaterCapacity)) { + modelObject.setResistiveDefrostHeaterCapacity(resistiveDefrostHeaterCapacity_.get()); + } + + // Defrost Energy Input Ratio Function of Temperature Curve Name: Optional Object + if (boost::optional wo_ = + workspaceObject.getTarget(HeatPump_AirToWaterFields::DefrostEnergyInputRatioFunctionofTemperatureCurveName)) { + if (boost::optional mo_ = translateAndMapWorkspaceObject(wo_.get())) { + if (boost::optional defrostEnergyInputRatioFunctionofTemperatureCurve_ = mo_->optionalCast()) { + modelObject.setDefrostEnergyInputRatioFunctionofTemperatureCurve(defrostEnergyInputRatioFunctionofTemperatureCurve_.get()); + } else { + LOG(Warn, workspaceObject.briefDescription() << " has a wrong type for 'Defrost Energy Input Ratio Function of Temperature Curve Name'"); + } + } + } + // Heat Pump Multiplier: Optional Integer + if (boost::optional heatPumpMultiplier_ = workspaceObject.getInt(HeatPump_AirToWaterFields::HeatPumpMultiplier)) { + modelObject.setHeatPumpMultiplier(heatPumpMultiplier_.get()); + } + + // Control Type: Optional String + if (boost::optional controlType_ = workspaceObject.getString(HeatPump_AirToWaterFields::ControlType)) { + modelObject.setControlType(controlType_.get()); + } + + // Crankcase Heater Capacity: Optional Double + if (boost::optional crankcaseHeaterCapacity_ = workspaceObject.getDouble(HeatPump_AirToWaterFields::CrankcaseHeaterCapacity)) { + modelObject.setCrankcaseHeaterCapacity(crankcaseHeaterCapacity_.get()); + } + + // Crankcase Heater Capacity Function of Temperature Curve Name: Optional Object + if (boost::optional wo_ = + workspaceObject.getTarget(HeatPump_AirToWaterFields::CrankcaseHeaterCapacityFunctionofTemperatureCurveName)) { + if (boost::optional mo_ = translateAndMapWorkspaceObject(wo_.get())) { + if (boost::optional crankcaseHeaterCapacityFunctionofTemperatureCurve_ = mo_->optionalCast()) { + modelObject.setCrankcaseHeaterCapacityFunctionofTemperatureCurve(crankcaseHeaterCapacityFunctionofTemperatureCurve_.get()); + } else { + LOG(Warn, workspaceObject.briefDescription() << " has a wrong type for 'Crankcase Heater Capacity Function of Temperature Curve Name'"); + } + } + } + // Maximum Ambient Temperature for Crankcase Heater Operation: Optional Double + if (boost::optional maximumAmbientTemperatureforCrankcaseHeaterOperation_ = + workspaceObject.getDouble(HeatPump_AirToWaterFields::MaximumAmbientTemperatureforCrankcaseHeaterOperation)) { + modelObject.setMaximumAmbientTemperatureforCrankcaseHeaterOperation(maximumAmbientTemperatureforCrankcaseHeaterOperation_.get()); + } + + if (auto awhp_hc_ = translateHeatPumpAirToWaterHeating(workspaceObject)) { + modelObject.setHeatingOperationMode(awhp_hc_->cast()); + } + if (auto awhp_cc_ = translateHeatPumpAirToWaterCooling(workspaceObject)) { + modelObject.setCoolingOperationMode(awhp_cc_->cast()); + } + + return modelObject; + } // End of translate function + +} // end namespace energyplus +} // end namespace openstudio diff --git a/src/energyplus/Test/HeatPumpAirToWater_GTest.cpp b/src/energyplus/Test/HeatPumpAirToWater_GTest.cpp new file mode 100644 index 0000000000..da689ccb96 --- /dev/null +++ b/src/energyplus/Test/HeatPumpAirToWater_GTest.cpp @@ -0,0 +1,809 @@ +/*********************************************************************************************************************** +* OpenStudio(R), Copyright (c) Alliance for Sustainable Energy, LLC. +* See also https://openstudio.net/license +***********************************************************************************************************************/ + +#include +#include "EnergyPlusFixture.hpp" + +#include "../ForwardTranslator.hpp" +#include "../ReverseTranslator.hpp" + +#include "../../model/HeatPumpAirToWater.hpp" +#include "../../model/HeatPumpAirToWater_Impl.hpp" +#include "../../model/HeatPumpAirToWaterCooling.hpp" +#include "../../model/HeatPumpAirToWaterCooling_Impl.hpp" +#include "../../model/HeatPumpAirToWaterCoolingSpeedData.hpp" +#include "../../model/HeatPumpAirToWaterCoolingSpeedData_Impl.hpp" +#include "../../model/HeatPumpAirToWaterHeating.hpp" +#include "../../model/HeatPumpAirToWaterHeating_Impl.hpp" +#include "../../model/HeatPumpAirToWaterHeatingSpeedData.hpp" +#include "../../model/HeatPumpAirToWaterHeatingSpeedData_Impl.hpp" + +#include "../../model/Model.hpp" +#include "../../model/Curve.hpp" +#include "../../model/Curve_Impl.hpp" +#include "../../model/CurveBiquadratic.hpp" +#include "../../model/CurveQuadratic.hpp" +#include "../../model/PlantLoop.hpp" +#include "../../model/Node.hpp" +#include "../../model/Schedule.hpp" +#include "../../model/ScheduleConstant.hpp" + +#include "../../utilities/idf/Workspace.hpp" +#include "../../utilities/idf/IdfObject.hpp" +#include "../../utilities/idf/WorkspaceObject.hpp" +#include "../../utilities/idf/WorkspaceObject_Impl.hpp" +#include "../../utilities/idf/IdfExtensibleGroup.hpp" +#include "../../utilities/idf/WorkspaceExtensibleGroup.hpp" + +// E+ FieldEnums +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace openstudio::energyplus; +using namespace openstudio::model; +using namespace openstudio; + +HeatPumpAirToWater makeAWHP(const Model& m) { + + HeatPumpAirToWater awhp(m); + awhp.setName("AWHP"); + + // Operating Mode Control Option for Multiple Unit: Required String + EXPECT_TRUE(awhp.setOperatingModeControlOptionforMultipleUnit("CoolingPriority")); + + // Operating Mode Control Schedule Name: Optional Object + ScheduleConstant operatingModeControlSchedule(m); + operatingModeControlSchedule.setName(awhp.nameString() + " Operating Mode Control Schedule"); + EXPECT_TRUE(awhp.setOperatingModeControlSchedule(operatingModeControlSchedule)); + // Operating Mode Control Method: Required String + EXPECT_EQ("ScheduledModes", awhp.operatingModeControlMethod()); + + // Minimum Part Load Ratio: Required Double + EXPECT_TRUE(awhp.setMinimumPartLoadRatio(0.6)); + + // Air Inlet Node Name: Optional String + EXPECT_TRUE(awhp.setAirInletNodeName(awhp.nameString() + " Air Inlet Node")); + + // Air Outlet Node Name: Optional String + EXPECT_TRUE(awhp.setAirOutletNodeName(awhp.nameString() + " Air Outlet Node")); + + // Maximum Outdoor Dry Bulb Temperature For Defrost Operation: Required Double + EXPECT_TRUE(awhp.setMaximumOutdoorDryBulbTemperatureForDefrostOperation(0.9)); + + // Heat Pump Defrost Control: Required String + EXPECT_TRUE(awhp.setHeatPumpDefrostControl("TimedEmpirical")); + + // Heat Pump Defrost Time Period Fraction: Required Double + EXPECT_TRUE(awhp.setHeatPumpDefrostTimePeriodFraction(1.1)); + + // Resistive Defrost Heater Capacity: Required Double + EXPECT_TRUE(awhp.setResistiveDefrostHeaterCapacity(100.0)); + + // Defrost Energy Input Ratio Function of Temperature Curve Name: Optional Object, BivariateFunctions + CurveBiquadratic defrostEnergyInputRatioFunctionofTemperatureCurve(m); + defrostEnergyInputRatioFunctionofTemperatureCurve.setName(awhp.nameString() + " Defrost EIR FT Curve"); + EXPECT_TRUE(awhp.setDefrostEnergyInputRatioFunctionofTemperatureCurve(defrostEnergyInputRatioFunctionofTemperatureCurve)); + + // Heat Pump Multiplier: Required Integer + EXPECT_TRUE(awhp.setHeatPumpMultiplier(14)); + + // Control Type: Required String + EXPECT_TRUE(awhp.setControlType("FixedSpeed")); + + // Crankcase Heater Capacity: Required Double + EXPECT_TRUE(awhp.setCrankcaseHeaterCapacity(1.6)); + + // Crankcase Heater Capacity Function of Temperature Curve Name: Optional Object, UnivariateFunctions + CurveQuadratic crankcaseHeaterCapacityFunctionofTemperatureCurve(m); + crankcaseHeaterCapacityFunctionofTemperatureCurve.setName("AWHP Crankcase Heater Cap FT Curve"); + EXPECT_TRUE(awhp.setCrankcaseHeaterCapacityFunctionofTemperatureCurve(crankcaseHeaterCapacityFunctionofTemperatureCurve)); + + // Maximum Ambient Temperature for Crankcase Heater Operation: Required Double + EXPECT_TRUE(awhp.setMaximumAmbientTemperatureforCrankcaseHeaterOperation(1.8)); + + return awhp; +} + +HeatPumpAirToWaterHeating makeAWHP_Heating(const Model& m) { + + HeatPumpAirToWaterHeating awhp_hc(m); + + awhp_hc.setName("AWHP Heating"); + + ScheduleConstant availabilitySchedule(m); + availabilitySchedule.setName(awhp_hc.nameString() + " Avail Schedule"); + EXPECT_TRUE(awhp_hc.setAvailabilitySchedule(availabilitySchedule)); + + // Rated Inlet Air Temperature: Required Double + EXPECT_TRUE(awhp_hc.setRatedInletAirTemperature(0.4)); + + // Rated Air Flow Rate: Required Double + EXPECT_TRUE(awhp_hc.setRatedAirFlowRate(0.5)); + + // Rated Leaving Water Temperature: Required Double + EXPECT_TRUE(awhp_hc.setRatedLeavingWaterTemperature(0.6)); + + // Rated Water Flow Rate: Required Double + EXPECT_TRUE(awhp_hc.setRatedWaterFlowRate(0.7)); + + // Minimum Outdoor Air Temperature: Required Double + EXPECT_TRUE(awhp_hc.setMinimumOutdoorAirTemperature(0.8)); + + // Maximum Outdoor Air Temperature: Required Double + EXPECT_TRUE(awhp_hc.setMaximumOutdoorAirTemperature(0.9)); + + // Minimum Leaving Water Temperature Curve Name: Optional Object + CurveQuadratic minimumLeavingWaterTemperatureCurve(m); + minimumLeavingWaterTemperatureCurve.setName(awhp_hc.nameString() + " MinLWT Curve"); + EXPECT_TRUE(awhp_hc.setMinimumLeavingWaterTemperatureCurve(minimumLeavingWaterTemperatureCurve)); + + // Maximum Leaving Water Temperature Curve Name: Optional Object + CurveQuadratic maximumLeavingWaterTemperatureCurve(m); + maximumLeavingWaterTemperatureCurve.setName(awhp_hc.nameString() + " MaxLWT Curve"); + EXPECT_TRUE(awhp_hc.setMaximumLeavingWaterTemperatureCurve(maximumLeavingWaterTemperatureCurve)); + + // Sizing Factor: Required Double + EXPECT_TRUE(awhp_hc.setSizingFactor(1.2)); + + // Booster Mode On Speed: Optional Object + HeatPumpAirToWaterHeatingSpeedData boosterModeOnSpeed(m); + boosterModeOnSpeed.setName(awhp_hc.nameString() + " Booster Mode On Speed"); + boosterModeOnSpeed.setRatedHeatingCapacity(6000.0); + boosterModeOnSpeed.setRatedCOPforHeating(3.0); + boosterModeOnSpeed.normalizedHeatingCapacityFunctionofTemperatureCurve().setName(boosterModeOnSpeed.nameString() + " CapFT Curve"); + boosterModeOnSpeed.heatingEnergyInputRatioFunctionofTemperatureCurve().setName(boosterModeOnSpeed.nameString() + " EIRfT Curve"); + boosterModeOnSpeed.heatingEnergyInputRatioFunctionofPLRCurve().setName(boosterModeOnSpeed.nameString() + " EIRfPLR Curve"); + EXPECT_TRUE(awhp_hc.setBoosterModeOnSpeed(boosterModeOnSpeed)); + + for (unsigned i = 1; i <= HeatPumpAirToWaterHeating::maximum_number_of_speeds; ++i) { + HeatPumpAirToWaterHeatingSpeedData speed(m); + speed.setName(awhp_hc.nameString() + " Speed " + std::to_string(i)); + if (i == HeatPumpAirToWaterCooling::maximum_number_of_speeds) { + speed.autosizeRatedHeatingCapacity(); + } else { + speed.setRatedHeatingCapacity(1000.0 * i); + } + speed.setRatedCOPforHeating(4.0 - 0.1 * (i - 1)); + speed.normalizedHeatingCapacityFunctionofTemperatureCurve().setName(speed.nameString() + " CapFT Curve"); + speed.heatingEnergyInputRatioFunctionofTemperatureCurve().setName(speed.nameString() + " EIRfT Curve"); + speed.heatingEnergyInputRatioFunctionofPLRCurve().setName(speed.nameString() + " EIRfPLR Curve"); + EXPECT_TRUE(awhp_hc.addSpeed(speed)); + } + + return awhp_hc; +} + +HeatPumpAirToWaterCooling makeAWHP_Cooling(const Model& m) { + + HeatPumpAirToWaterCooling awhp_cc(m); + + awhp_cc.setName("AWHP Cooling"); + + ScheduleConstant availabilitySchedule(m); + availabilitySchedule.setName(awhp_cc.nameString() + " Avail Schedule"); + EXPECT_TRUE(awhp_cc.setAvailabilitySchedule(availabilitySchedule)); + + // Rated Inlet Air Temperature: Required Double + EXPECT_TRUE(awhp_cc.setRatedInletAirTemperature(0.6)); + + // Rated Air Flow Rate: Required Double + EXPECT_TRUE(awhp_cc.setRatedAirFlowRate(0.7)); + + // Rated Leaving Water Temperature: Required Double + EXPECT_TRUE(awhp_cc.setRatedLeavingWaterTemperature(0.8)); + + // Rated Water Flow Rate: Required Double + EXPECT_TRUE(awhp_cc.setRatedWaterFlowRate(0.9)); + + // Minimum Outdoor Air Temperature: Required Double + EXPECT_TRUE(awhp_cc.setMinimumOutdoorAirTemperature(-5.0)); + + // Maximum Outdoor Air Temperature: Required Double + EXPECT_TRUE(awhp_cc.setMaximumOutdoorAirTemperature(50.0)); + + // Minimum Leaving Water Temperature Curve Name: Optional Object + CurveQuadratic minimumLeavingWaterTemperatureCurve(m); + minimumLeavingWaterTemperatureCurve.setName(awhp_cc.nameString() + " MinLWT Curve"); + EXPECT_TRUE(awhp_cc.setMinimumLeavingWaterTemperatureCurve(minimumLeavingWaterTemperatureCurve)); + + // Maximum Leaving Water Temperature Curve Name: Optional Object + CurveQuadratic maximumLeavingWaterTemperatureCurve(m); + maximumLeavingWaterTemperatureCurve.setName(awhp_cc.nameString() + " MaxLWT Curve"); + EXPECT_TRUE(awhp_cc.setMaximumLeavingWaterTemperatureCurve(maximumLeavingWaterTemperatureCurve)); + + // Sizing Factor: Required Double + EXPECT_TRUE(awhp_cc.setSizingFactor(1.5)); + + // Booster Mode On Speed: Optional Object + HeatPumpAirToWaterCoolingSpeedData boosterModeOnSpeed(m); + boosterModeOnSpeed.setName(awhp_cc.nameString() + " Booster Mode On Speed"); + boosterModeOnSpeed.setRatedCoolingCapacity(12000.0); + boosterModeOnSpeed.setRatedCOPforCooling(3.5); + boosterModeOnSpeed.normalizedCoolingCapacityFunctionofTemperatureCurve().setName(boosterModeOnSpeed.nameString() + " CapFT Curve"); + boosterModeOnSpeed.coolingEnergyInputRatioFunctionofTemperatureCurve().setName(boosterModeOnSpeed.nameString() + " EIRfT Curve"); + boosterModeOnSpeed.coolingEnergyInputRatioFunctionofPLRCurve().setName(boosterModeOnSpeed.nameString() + " EIRfPLR Curve"); + EXPECT_TRUE(awhp_cc.setBoosterModeOnSpeed(boosterModeOnSpeed)); + + for (unsigned i = 1; i <= HeatPumpAirToWaterCooling::maximum_number_of_speeds; ++i) { + HeatPumpAirToWaterCoolingSpeedData speed(m); + speed.setName(awhp_cc.nameString() + " Speed " + std::to_string(i)); + if (i == HeatPumpAirToWaterCooling::maximum_number_of_speeds) { + speed.autosizeRatedCoolingCapacity(); + } else { + speed.setRatedCoolingCapacity(2000.0 * i); + } + + speed.setRatedCOPforCooling(4.5 - 0.1 * (i - 1)); + speed.normalizedCoolingCapacityFunctionofTemperatureCurve().setName(speed.nameString() + " CapFT Curve"); + speed.coolingEnergyInputRatioFunctionofTemperatureCurve().setName(speed.nameString() + " EIRfT Curve"); + speed.coolingEnergyInputRatioFunctionofPLRCurve().setName(speed.nameString() + " EIRfPLR Curve"); + EXPECT_TRUE(awhp_cc.addSpeed(speed)); + } + + return awhp_cc; +} + +PlantLoop createLoop(Model& m, const std::string& prefix) { + PlantLoop p(m); + static constexpr std::array compNames = { + "Supply Inlet", "Supply Splitter", "Supply Connection Node", "Supply Mixer", "Supply Outlet", + "Demand Inlet", "Demand Splitter", "Demand Connection Node", "Demand Mixer", "Demand Outlet", + }; + p.setName(prefix); + for (size_t i = 0; auto& comp : p.components()) { + comp.setName(prefix + " " + std::string{compNames[i++]}); + } + return p; +} + +TEST_F(EnergyPlusFixture, ForwardTranslator_HeatPumpAirToWater) { + + ForwardTranslator ft; + ft.setExcludeLCCObjects(true); + + Model m; + + HeatPumpAirToWater awhp = makeAWHP(m); + HeatPumpAirToWaterHeating awhp_hc = makeAWHP_Heating(m); + EXPECT_TRUE(awhp.setHeatingOperationMode(awhp_hc)); + + HeatPumpAirToWaterCooling awhp_cc = makeAWHP_Cooling(m); + EXPECT_TRUE(awhp.setCoolingOperationMode(awhp_cc)); + + // Not on a PlantLoop: not translated + { + const Workspace w = ft.translateModel(m); + const auto idfObjs = w.getObjectsByType(IddObjectType::HeatPump_AirToWater); + ASSERT_EQ(0u, idfObjs.size()); + } + + PlantLoop hwLoop = createLoop(m, "HW Loop"); + EXPECT_TRUE(hwLoop.addSupplyBranchForComponent(awhp_hc)); + + // Check node names on supply/demand branches + // Checks that the special case implemented in ForwardTranslatePlantLoop::populateBranch does the right job + + struct Expected + { + bool isHeating = true; + std::string plantName; + std::string inletNodeName; + std::string outletNodeName; + }; + + // Only on a Heating Loop, so even though I have a cooling operation mode and speeds for it, only the heating side is translated + { + Workspace w = ft.translateModel(m); + + const auto idfObjs = w.getObjectsByType(IddObjectType::HeatPump_AirToWater); + ASSERT_EQ(1u, idfObjs.size()); + + const auto& idfObject = idfObjs.front(); + EXPECT_EQ("ScheduledModes", idfObject.getString(HeatPump_AirToWaterFields::OperatingModeControlMethod).get()); + EXPECT_EQ("CoolingPriority", idfObject.getString(HeatPump_AirToWaterFields::OperatingModeControlOptionforMultipleUnit).get()); + EXPECT_EQ("AWHP Operating Mode Control Schedule", idfObject.getString(HeatPump_AirToWaterFields::OperatingModeControlScheduleName).get()); + EXPECT_EQ(0.6, idfObject.getDouble(HeatPump_AirToWaterFields::MinimumPartLoadRatio).get()); + + EXPECT_EQ("AWHP Air Inlet Node", idfObject.getString(HeatPump_AirToWaterFields::AirInletNodeName).get()); + EXPECT_EQ("AWHP Air Outlet Node", idfObject.getString(HeatPump_AirToWaterFields::AirOutletNodeName).get()); + + EXPECT_EQ(0.9, idfObject.getDouble(HeatPump_AirToWaterFields::MaximumOutdoorDryBulbTemperatureForDefrostOperation).get()); + EXPECT_EQ("TimedEmpirical", idfObject.getString(HeatPump_AirToWaterFields::HeatPumpDefrostControl).get()); + EXPECT_EQ(1.1, idfObject.getDouble(HeatPump_AirToWaterFields::HeatPumpDefrostTimePeriodFraction).get()); + EXPECT_EQ(100.0, idfObject.getDouble(HeatPump_AirToWaterFields::ResistiveDefrostHeaterCapacity).get()); + EXPECT_EQ("AWHP Defrost EIR FT Curve", + idfObject.getString(HeatPump_AirToWaterFields::DefrostEnergyInputRatioFunctionofTemperatureCurveName).get()); + EXPECT_EQ(14, idfObject.getInt(HeatPump_AirToWaterFields::HeatPumpMultiplier).get()); + EXPECT_EQ("FixedSpeed", idfObject.getString(HeatPump_AirToWaterFields::ControlType).get()); + EXPECT_EQ(1.6, idfObject.getDouble(HeatPump_AirToWaterFields::CrankcaseHeaterCapacity).get()); + EXPECT_EQ("AWHP Crankcase Heater Cap FT Curve", + idfObject.getString(HeatPump_AirToWaterFields::CrankcaseHeaterCapacityFunctionofTemperatureCurveName).get()); + EXPECT_EQ(1.8, idfObject.getDouble(HeatPump_AirToWaterFields::MaximumAmbientTemperatureforCrankcaseHeaterOperation).get()); + + // Heating + EXPECT_EQ("AWHP Heating Avail Schedule", idfObject.getString(HeatPump_AirToWaterFields::AvailabilityScheduleNameHeating).get()); + EXPECT_EQ(0.4, idfObject.getDouble(HeatPump_AirToWaterFields::RatedInletAirTemperatureinHeatingMode).get()); + // EXPECT_EQ("Autosize", idfObject.getString(HeatPump_AirToWaterFields::RatedAirFlowRateinHeatingMode).get()); + EXPECT_EQ(0.5, idfObject.getDouble(HeatPump_AirToWaterFields::RatedAirFlowRateinHeatingMode).get()); + EXPECT_EQ(0.6, idfObject.getDouble(HeatPump_AirToWaterFields::RatedLeavingWaterTemperatureinHeatingMode).get()); + // EXPECT_EQ("Autosize", idfObject.getString(HeatPump_AirToWaterFields::RatedWaterFlowRateinHeatingMode).get()); + EXPECT_EQ(0.7, idfObject.getDouble(HeatPump_AirToWaterFields::RatedWaterFlowRateinHeatingMode).get()); + EXPECT_EQ(0.8, idfObject.getDouble(HeatPump_AirToWaterFields::MinimumOutdoorAirTemperatureinHeatingMode).get()); + EXPECT_EQ(0.9, idfObject.getDouble(HeatPump_AirToWaterFields::MaximumOutdoorAirTemperatureinHeatingMode).get()); + EXPECT_EQ("AWHP Heating MinLWT Curve", + idfObject.getString(HeatPump_AirToWaterFields::MinimumLeavingWaterTemperatureCurveNameinHeatingMode).get()); + EXPECT_EQ("AWHP Heating MaxLWT Curve", + idfObject.getString(HeatPump_AirToWaterFields::MaximumLeavingWaterTemperatureCurveNameinHeatingMode).get()); + + EXPECT_EQ(1.2, idfObject.getDouble(HeatPump_AirToWaterFields::SizingFactorforHeating).get()); + + EXPECT_EQ(awhp_hc.inletModelObject()->nameString(), idfObject.getString(HeatPump_AirToWaterFields::HotWaterInletNodeName).get()); + EXPECT_EQ(awhp_hc.outletModelObject()->nameString(), idfObject.getString(HeatPump_AirToWaterFields::HotWaterOutletNodeName).get()); + + EXPECT_EQ(5, idfObject.getInt(HeatPump_AirToWaterFields::NumberofSpeedsforHeating).get()); + + // EXPECT_EQ("Autosize", idfObject.getString(HeatPump_AirToWaterFields::RatedHeatingCapacityatSpeed1).get()); + EXPECT_EQ(1000.0, idfObject.getDouble(HeatPump_AirToWaterFields::RatedHeatingCapacityatSpeed1).get()); + EXPECT_EQ(4.0, idfObject.getDouble(HeatPump_AirToWaterFields::RatedCOPforHeatingatSpeed1).get()); + EXPECT_EQ("AWHP Heating Speed 1 CapFT Curve", + idfObject.getString(HeatPump_AirToWaterFields::NormalizedHeatingCapacityFunctionofTemperatureCurveNameatSpeed1).get()); + EXPECT_EQ("AWHP Heating Speed 1 EIRfT Curve", + idfObject.getString(HeatPump_AirToWaterFields::HeatingEnergyInputRatioFunctionofTemperatureCurveNameatSpeed1).get()); + EXPECT_EQ("AWHP Heating Speed 1 EIRfPLR Curve", + idfObject.getString(HeatPump_AirToWaterFields::HeatingEnergyInputRatioFunctionofPLRCurveNameatSpeed1).get()); + EXPECT_EQ(2000.0, idfObject.getDouble(HeatPump_AirToWaterFields::RatedHeatingCapacityatSpeed2).get()); + EXPECT_EQ(3.9, idfObject.getDouble(HeatPump_AirToWaterFields::RatedCOPforHeatingatSpeed2).get()); + EXPECT_EQ("AWHP Heating Speed 2 CapFT Curve", + idfObject.getString(HeatPump_AirToWaterFields::NormalizedHeatingCapacityFunctionofTemperatureCurveNameatSpeed2).get()); + EXPECT_EQ("AWHP Heating Speed 2 EIRfT Curve", + idfObject.getString(HeatPump_AirToWaterFields::HeatingEnergyInputRatioFunctionofTemperatureCurveNameatSpeed2).get()); + EXPECT_EQ("AWHP Heating Speed 2 EIRfPLR Curve", + idfObject.getString(HeatPump_AirToWaterFields::HeatingEnergyInputRatioFunctionofPLRCurveNameatSpeed2).get()); + EXPECT_EQ(3000.0, idfObject.getDouble(HeatPump_AirToWaterFields::RatedHeatingCapacityatSpeed3).get()); + EXPECT_EQ(3.8, idfObject.getDouble(HeatPump_AirToWaterFields::RatedCOPforHeatingatSpeed3).get()); + EXPECT_EQ("AWHP Heating Speed 3 CapFT Curve", + idfObject.getString(HeatPump_AirToWaterFields::NormalizedHeatingCapacityFunctionofTemperatureCurveNameatSpeed3).get()); + EXPECT_EQ("AWHP Heating Speed 3 EIRfT Curve", + idfObject.getString(HeatPump_AirToWaterFields::HeatingEnergyInputRatioFunctionofTemperatureCurveNameatSpeed3).get()); + EXPECT_EQ("AWHP Heating Speed 3 EIRfPLR Curve", + idfObject.getString(HeatPump_AirToWaterFields::HeatingEnergyInputRatioFunctionofPLRCurveNameatSpeed3).get()); + EXPECT_EQ(4000.0, idfObject.getDouble(HeatPump_AirToWaterFields::RatedHeatingCapacityatSpeed4).get()); + EXPECT_EQ(3.7, idfObject.getDouble(HeatPump_AirToWaterFields::RatedCOPforHeatingatSpeed4).get()); + EXPECT_EQ("AWHP Heating Speed 4 CapFT Curve", + idfObject.getString(HeatPump_AirToWaterFields::NormalizedHeatingCapacityFunctionofTemperatureCurveNameatSpeed4).get()); + EXPECT_EQ("AWHP Heating Speed 4 EIRfT Curve", + idfObject.getString(HeatPump_AirToWaterFields::HeatingEnergyInputRatioFunctionofTemperatureCurveNameatSpeed4).get()); + EXPECT_EQ("AWHP Heating Speed 4 EIRfPLR Curve", + idfObject.getString(HeatPump_AirToWaterFields::HeatingEnergyInputRatioFunctionofPLRCurveNameatSpeed4).get()); + EXPECT_EQ("Autosize", idfObject.getString(HeatPump_AirToWaterFields::RatedHeatingCapacityatSpeed5).get()); + EXPECT_EQ(3.6, idfObject.getDouble(HeatPump_AirToWaterFields::RatedCOPforHeatingatSpeed5).get()); + EXPECT_EQ("AWHP Heating Speed 5 CapFT Curve", + idfObject.getString(HeatPump_AirToWaterFields::NormalizedHeatingCapacityFunctionofTemperatureCurveNameatSpeed5).get()); + EXPECT_EQ("AWHP Heating Speed 5 EIRfT Curve", + idfObject.getString(HeatPump_AirToWaterFields::HeatingEnergyInputRatioFunctionofTemperatureCurveNameatSpeed5).get()); + EXPECT_EQ("AWHP Heating Speed 5 EIRfPLR Curve", + idfObject.getString(HeatPump_AirToWaterFields::HeatingEnergyInputRatioFunctionofPLRCurveNameatSpeed5).get()); + EXPECT_EQ("Yes", idfObject.getString(HeatPump_AirToWaterFields::BoosterModeOnHeating).get()); + EXPECT_EQ(6000.0, idfObject.getDouble(HeatPump_AirToWaterFields::RatedHeatingCapacityinBoosterMode).get()); + EXPECT_EQ(3.0, idfObject.getDouble(HeatPump_AirToWaterFields::RatedHeatingCOPinBoosterMode).get()); + EXPECT_EQ("AWHP Heating Booster Mode On Speed CapFT Curve", + idfObject.getString(HeatPump_AirToWaterFields::NormalizedHeatingCapacityFunctionofTemperatureCurveNameinBoosterMode).get()); + EXPECT_EQ("AWHP Heating Booster Mode On Speed EIRfT Curve", + idfObject.getString(HeatPump_AirToWaterFields::HeatingEnergyInputRatioFunctionofTemperatureCurveNameinBoosterMode).get()); + EXPECT_EQ("AWHP Heating Booster Mode On Speed EIRfPLR Curve", + idfObject.getString(HeatPump_AirToWaterFields::HeatingEnergyInputRatioFunctionofPLRCurveNameinBoosterMode).get()); + + // COOLING + // Everything cooling is empty + EXPECT_TRUE(idfObject.isEmpty(HeatPump_AirToWaterFields::AvailabilityScheduleNameCooling)); + EXPECT_TRUE(idfObject.isEmpty(HeatPump_AirToWaterFields::RatedInletAirTemperatureinCoolingMode)); + EXPECT_TRUE(idfObject.isEmpty(HeatPump_AirToWaterFields::RatedAirFlowRateinCoolingMode)); + EXPECT_TRUE(idfObject.isEmpty(HeatPump_AirToWaterFields::RatedLeavingWaterTemperatureinCoolingMode)); + EXPECT_TRUE(idfObject.isEmpty(HeatPump_AirToWaterFields::RatedWaterFlowRateinCoolingMode)); + EXPECT_TRUE(idfObject.isEmpty(HeatPump_AirToWaterFields::MinimumOutdoorAirTemperatureinCoolingMode)); + EXPECT_TRUE(idfObject.isEmpty(HeatPump_AirToWaterFields::MaximumOutdoorAirTemperatureinCoolingMode)); + EXPECT_TRUE(idfObject.isEmpty(HeatPump_AirToWaterFields::MinimumLeavingWaterTemperatureCurveNameinCoolingMode)); + EXPECT_TRUE(idfObject.isEmpty(HeatPump_AirToWaterFields::MaximumLeavingWaterTemperatureCurveNameinCoolingMode)); + EXPECT_TRUE(idfObject.isEmpty(HeatPump_AirToWaterFields::SizingFactorforCooling)); + EXPECT_TRUE(idfObject.isEmpty(HeatPump_AirToWaterFields::ChilledWaterInletNodeName)); + EXPECT_TRUE(idfObject.isEmpty(HeatPump_AirToWaterFields::ChilledWaterOutletNodeName)); + + EXPECT_EQ(0, idfObject.getInt(HeatPump_AirToWaterFields::NumberofSpeedsforCooling).get()); + + for (unsigned index = HeatPump_AirToWaterFields::RatedCoolingCapacityatSpeed1; + index <= HeatPump_AirToWaterFields::CoolingEnergyInputRatioFunctionofPLRCurveNameatSpeed5; ++index) { + EXPECT_TRUE(idfObject.isEmpty(index)); + } + + EXPECT_TRUE(idfObject.isEmpty(HeatPump_AirToWaterFields::ChilledWaterInletNodeName)); + EXPECT_TRUE(idfObject.isEmpty(HeatPump_AirToWaterFields::ChilledWaterOutletNodeName)); + + EXPECT_TRUE(idfObject.isEmpty(HeatPump_AirToWaterFields::BoosterModeOnCooling)); + EXPECT_TRUE(idfObject.isEmpty(HeatPump_AirToWaterFields::RatedCoolingCapacityinBoosterMode)); + EXPECT_TRUE(idfObject.isEmpty(HeatPump_AirToWaterFields::RatedCoolingCOPinBoosterMode)); + EXPECT_TRUE(idfObject.isEmpty(HeatPump_AirToWaterFields::NormalizedCoolingCapacityFunctionofTemperatureCurveNameinBoosterMode)); + EXPECT_TRUE(idfObject.isEmpty(HeatPump_AirToWaterFields::CoolingEnergyInputRatioFunctionofTemperatureCurveNameinBoosterMode)); + EXPECT_TRUE(idfObject.isEmpty(HeatPump_AirToWaterFields::CoolingEnergyInputRatioFunctionofPLRCurveNameinBoosterMode)); + + const size_t expectedNumCurves = 1 // defrostEnergyInputRatioFunctionofTemperatureCurve + + 1 // crankcaseHeaterCapacityFunctionofTemperatureCurve + + 2 * 2 // awhp_hc/cc min/max LWT curves + + 2 * (5 + 1) * 3; // awhp_hc/cc: 5 speeds + one booster mode, each with 3 curves + EXPECT_EQ(expectedNumCurves, + w.getObjectsByType(IddObjectType::Curve_Biquadratic).size() + w.getObjectsByType(IddObjectType::Curve_Quadratic).size()); + auto oaNodeList = w.getObjectsByType(IddObjectType::OutdoorAir_NodeList); + ASSERT_EQ(1, oaNodeList.size()); + EXPECT_EQ("AWHP Air Inlet Node", oaNodeList.front().getString(0).get()); + EXPECT_EQ("AWHP Air Outlet Node", oaNodeList.front().getString(1).get()); + + EXPECT_EQ(1, w.getObjectsByType(IddObjectType::PlantLoop).size()); + + std::vector expecteds = { + Expected{.isHeating = true, + .plantName = hwLoop.nameString(), + .inletNodeName = awhp_hc.inletModelObject()->nameString(), + .outletNodeName = awhp_hc.outletModelObject()->nameString()}, + }; + for (const auto& e : expecteds) { + auto p_ = w.getObjectByTypeAndName(IddObjectType::PlantLoop, e.plantName); + ASSERT_TRUE(p_.is_initialized()) << "Cannot find PlantLoop named " << e.plantName; + WorkspaceObject idf_plant = p_.get(); + WorkspaceObject idf_brlist = idf_plant.getTarget(PlantLoopFields::PlantSideBranchListName).get(); + + // Should have at three branches: supply inlet, the one with the AWHP, supply outlet. + ASSERT_EQ(3, idf_brlist.extensibleGroups().size()) << "Failed for " << e.plantName; + // Get the AWHP one + auto w_eg = idf_brlist.extensibleGroups()[1].cast(); + WorkspaceObject idf_branch = w_eg.getTarget(BranchListExtensibleFields::BranchName).get(); + + // There should be only one equipment on the branch + ASSERT_EQ(1, idf_branch.extensibleGroups().size()); + auto w_eg2 = idf_branch.extensibleGroups()[0].cast(); + + ASSERT_EQ(w_eg2.getString(BranchExtensibleFields::ComponentName).get(), awhp.nameString()); + ASSERT_EQ(w_eg2.getString(BranchExtensibleFields::ComponentInletNodeName).get(), e.inletNodeName); + ASSERT_EQ(w_eg2.getString(BranchExtensibleFields::ComponentOutletNodeName).get(), e.outletNodeName); + + WorkspaceObject idf_plant_op = p_->getTarget(PlantLoopFields::PlantEquipmentOperationSchemeName).get(); + // Should have created a Cooling Load one only + ASSERT_EQ(1, idf_plant_op.extensibleGroups().size()); + auto w_eg_schemes = idf_plant_op.extensibleGroups()[0].cast(); + ASSERT_EQ(e.isHeating ? "PlantEquipmentOperation:HeatingLoad" : "PlantEquipmentOperation:CoolingLoad", + w_eg_schemes.getString(PlantEquipmentOperationSchemesExtensibleFields::ControlSchemeObjectType).get()); + + // Get the Operation Scheme + auto op_scheme_ = w_eg_schemes.getTarget(PlantEquipmentOperationSchemesExtensibleFields::ControlSchemeName); + ASSERT_TRUE(op_scheme_); + + // Get the Plant Equipment List of this CoolingLoad/HeatingLoad scheme + // There should only be one Load Range + ASSERT_EQ(1u, op_scheme_->extensibleGroups().size()); + + // Load range 1 + w_eg = op_scheme_->extensibleGroups()[0].cast(); + boost::optional peq_list_; + if (e.isHeating) { + peq_list_ = w_eg.getTarget(PlantEquipmentOperation_HeatingLoadExtensibleFields::RangeEquipmentListName); + } else { + peq_list_ = w_eg.getTarget(PlantEquipmentOperation_CoolingLoadExtensibleFields::RangeEquipmentListName); + } + ASSERT_TRUE(peq_list_); + + // Should have one equipment on it: HeatPump:AirToWater + auto peqs = peq_list_->extensibleGroups(); + ASSERT_EQ(1, peqs.size()); + ASSERT_EQ("HeatPump:AirToWater", peqs.front().getString(PlantEquipmentListExtensibleFields::EquipmentObjectType).get()); + ASSERT_EQ(awhp.nameString(), peqs.front().getString(PlantEquipmentListExtensibleFields::EquipmentName).get()); + } + } + + PlantLoop chwLoop = createLoop(m, "ChW Loop"); + EXPECT_TRUE(chwLoop.addSupplyBranchForComponent(awhp_cc)); + + // On both a Heating and a Cooling Loop + { + Workspace w = ft.translateModel(m); + + const auto idfObjs = w.getObjectsByType(IddObjectType::HeatPump_AirToWater); + ASSERT_EQ(1u, idfObjs.size()); + + const auto& idfObject = idfObjs.front(); + EXPECT_EQ("ScheduledModes", idfObject.getString(HeatPump_AirToWaterFields::OperatingModeControlMethod).get()); + EXPECT_EQ("CoolingPriority", idfObject.getString(HeatPump_AirToWaterFields::OperatingModeControlOptionforMultipleUnit).get()); + EXPECT_EQ("AWHP Operating Mode Control Schedule", idfObject.getString(HeatPump_AirToWaterFields::OperatingModeControlScheduleName).get()); + EXPECT_EQ(0.6, idfObject.getDouble(HeatPump_AirToWaterFields::MinimumPartLoadRatio).get()); + + EXPECT_EQ("AWHP Air Inlet Node", idfObject.getString(HeatPump_AirToWaterFields::AirInletNodeName).get()); + EXPECT_EQ("AWHP Air Outlet Node", idfObject.getString(HeatPump_AirToWaterFields::AirOutletNodeName).get()); + + EXPECT_EQ(0.9, idfObject.getDouble(HeatPump_AirToWaterFields::MaximumOutdoorDryBulbTemperatureForDefrostOperation).get()); + EXPECT_EQ("TimedEmpirical", idfObject.getString(HeatPump_AirToWaterFields::HeatPumpDefrostControl).get()); + EXPECT_EQ(1.1, idfObject.getDouble(HeatPump_AirToWaterFields::HeatPumpDefrostTimePeriodFraction).get()); + EXPECT_EQ(100.0, idfObject.getDouble(HeatPump_AirToWaterFields::ResistiveDefrostHeaterCapacity).get()); + EXPECT_EQ("AWHP Defrost EIR FT Curve", + idfObject.getString(HeatPump_AirToWaterFields::DefrostEnergyInputRatioFunctionofTemperatureCurveName).get()); + EXPECT_EQ(14, idfObject.getInt(HeatPump_AirToWaterFields::HeatPumpMultiplier).get()); + EXPECT_EQ("FixedSpeed", idfObject.getString(HeatPump_AirToWaterFields::ControlType).get()); + EXPECT_EQ(1.6, idfObject.getDouble(HeatPump_AirToWaterFields::CrankcaseHeaterCapacity).get()); + EXPECT_EQ("AWHP Crankcase Heater Cap FT Curve", + idfObject.getString(HeatPump_AirToWaterFields::CrankcaseHeaterCapacityFunctionofTemperatureCurveName).get()); + EXPECT_EQ(1.8, idfObject.getDouble(HeatPump_AirToWaterFields::MaximumAmbientTemperatureforCrankcaseHeaterOperation).get()); + + // Heating + EXPECT_EQ("AWHP Heating Avail Schedule", idfObject.getString(HeatPump_AirToWaterFields::AvailabilityScheduleNameHeating).get()); + EXPECT_EQ(0.4, idfObject.getDouble(HeatPump_AirToWaterFields::RatedInletAirTemperatureinHeatingMode).get()); + // EXPECT_EQ("Autosize", idfObject.getString(HeatPump_AirToWaterFields::RatedAirFlowRateinHeatingMode).get()); + EXPECT_EQ(0.5, idfObject.getDouble(HeatPump_AirToWaterFields::RatedAirFlowRateinHeatingMode).get()); + EXPECT_EQ(0.6, idfObject.getDouble(HeatPump_AirToWaterFields::RatedLeavingWaterTemperatureinHeatingMode).get()); + // EXPECT_EQ("Autosize", idfObject.getString(HeatPump_AirToWaterFields::RatedWaterFlowRateinHeatingMode).get()); + EXPECT_EQ(0.7, idfObject.getDouble(HeatPump_AirToWaterFields::RatedWaterFlowRateinHeatingMode).get()); + EXPECT_EQ(0.8, idfObject.getDouble(HeatPump_AirToWaterFields::MinimumOutdoorAirTemperatureinHeatingMode).get()); + EXPECT_EQ(0.9, idfObject.getDouble(HeatPump_AirToWaterFields::MaximumOutdoorAirTemperatureinHeatingMode).get()); + EXPECT_EQ("AWHP Heating MinLWT Curve", + idfObject.getString(HeatPump_AirToWaterFields::MinimumLeavingWaterTemperatureCurveNameinHeatingMode).get()); + EXPECT_EQ("AWHP Heating MaxLWT Curve", + idfObject.getString(HeatPump_AirToWaterFields::MaximumLeavingWaterTemperatureCurveNameinHeatingMode).get()); + + EXPECT_EQ(1.2, idfObject.getDouble(HeatPump_AirToWaterFields::SizingFactorforHeating).get()); + + EXPECT_EQ(awhp_hc.inletModelObject()->nameString(), idfObject.getString(HeatPump_AirToWaterFields::HotWaterInletNodeName).get()); + EXPECT_EQ(awhp_hc.outletModelObject()->nameString(), idfObject.getString(HeatPump_AirToWaterFields::HotWaterOutletNodeName).get()); + + EXPECT_EQ(5, idfObject.getInt(HeatPump_AirToWaterFields::NumberofSpeedsforHeating).get()); + + // EXPECT_EQ("Autosize", idfObject.getString(HeatPump_AirToWaterFields::RatedHeatingCapacityatSpeed1).get()); + EXPECT_EQ(1000.0, idfObject.getDouble(HeatPump_AirToWaterFields::RatedHeatingCapacityatSpeed1).get()); + EXPECT_EQ(4.0, idfObject.getDouble(HeatPump_AirToWaterFields::RatedCOPforHeatingatSpeed1).get()); + EXPECT_EQ("AWHP Heating Speed 1 CapFT Curve", + idfObject.getString(HeatPump_AirToWaterFields::NormalizedHeatingCapacityFunctionofTemperatureCurveNameatSpeed1).get()); + EXPECT_EQ("AWHP Heating Speed 1 EIRfT Curve", + idfObject.getString(HeatPump_AirToWaterFields::HeatingEnergyInputRatioFunctionofTemperatureCurveNameatSpeed1).get()); + EXPECT_EQ("AWHP Heating Speed 1 EIRfPLR Curve", + idfObject.getString(HeatPump_AirToWaterFields::HeatingEnergyInputRatioFunctionofPLRCurveNameatSpeed1).get()); + EXPECT_EQ(2000.0, idfObject.getDouble(HeatPump_AirToWaterFields::RatedHeatingCapacityatSpeed2).get()); + EXPECT_EQ(3.9, idfObject.getDouble(HeatPump_AirToWaterFields::RatedCOPforHeatingatSpeed2).get()); + EXPECT_EQ("AWHP Heating Speed 2 CapFT Curve", + idfObject.getString(HeatPump_AirToWaterFields::NormalizedHeatingCapacityFunctionofTemperatureCurveNameatSpeed2).get()); + EXPECT_EQ("AWHP Heating Speed 2 EIRfT Curve", + idfObject.getString(HeatPump_AirToWaterFields::HeatingEnergyInputRatioFunctionofTemperatureCurveNameatSpeed2).get()); + EXPECT_EQ("AWHP Heating Speed 2 EIRfPLR Curve", + idfObject.getString(HeatPump_AirToWaterFields::HeatingEnergyInputRatioFunctionofPLRCurveNameatSpeed2).get()); + EXPECT_EQ(3000.0, idfObject.getDouble(HeatPump_AirToWaterFields::RatedHeatingCapacityatSpeed3).get()); + EXPECT_EQ(3.8, idfObject.getDouble(HeatPump_AirToWaterFields::RatedCOPforHeatingatSpeed3).get()); + EXPECT_EQ("AWHP Heating Speed 3 CapFT Curve", + idfObject.getString(HeatPump_AirToWaterFields::NormalizedHeatingCapacityFunctionofTemperatureCurveNameatSpeed3).get()); + EXPECT_EQ("AWHP Heating Speed 3 EIRfT Curve", + idfObject.getString(HeatPump_AirToWaterFields::HeatingEnergyInputRatioFunctionofTemperatureCurveNameatSpeed3).get()); + EXPECT_EQ("AWHP Heating Speed 3 EIRfPLR Curve", + idfObject.getString(HeatPump_AirToWaterFields::HeatingEnergyInputRatioFunctionofPLRCurveNameatSpeed3).get()); + EXPECT_EQ(4000.0, idfObject.getDouble(HeatPump_AirToWaterFields::RatedHeatingCapacityatSpeed4).get()); + EXPECT_EQ(3.7, idfObject.getDouble(HeatPump_AirToWaterFields::RatedCOPforHeatingatSpeed4).get()); + EXPECT_EQ("AWHP Heating Speed 4 CapFT Curve", + idfObject.getString(HeatPump_AirToWaterFields::NormalizedHeatingCapacityFunctionofTemperatureCurveNameatSpeed4).get()); + EXPECT_EQ("AWHP Heating Speed 4 EIRfT Curve", + idfObject.getString(HeatPump_AirToWaterFields::HeatingEnergyInputRatioFunctionofTemperatureCurveNameatSpeed4).get()); + EXPECT_EQ("AWHP Heating Speed 4 EIRfPLR Curve", + idfObject.getString(HeatPump_AirToWaterFields::HeatingEnergyInputRatioFunctionofPLRCurveNameatSpeed4).get()); + EXPECT_EQ("Autosize", idfObject.getString(HeatPump_AirToWaterFields::RatedHeatingCapacityatSpeed5).get()); + EXPECT_EQ(3.6, idfObject.getDouble(HeatPump_AirToWaterFields::RatedCOPforHeatingatSpeed5).get()); + EXPECT_EQ("AWHP Heating Speed 5 CapFT Curve", + idfObject.getString(HeatPump_AirToWaterFields::NormalizedHeatingCapacityFunctionofTemperatureCurveNameatSpeed5).get()); + EXPECT_EQ("AWHP Heating Speed 5 EIRfT Curve", + idfObject.getString(HeatPump_AirToWaterFields::HeatingEnergyInputRatioFunctionofTemperatureCurveNameatSpeed5).get()); + EXPECT_EQ("AWHP Heating Speed 5 EIRfPLR Curve", + idfObject.getString(HeatPump_AirToWaterFields::HeatingEnergyInputRatioFunctionofPLRCurveNameatSpeed5).get()); + EXPECT_EQ("Yes", idfObject.getString(HeatPump_AirToWaterFields::BoosterModeOnHeating).get()); + EXPECT_EQ(6000.0, idfObject.getDouble(HeatPump_AirToWaterFields::RatedHeatingCapacityinBoosterMode).get()); + EXPECT_EQ(3.0, idfObject.getDouble(HeatPump_AirToWaterFields::RatedHeatingCOPinBoosterMode).get()); + EXPECT_EQ("AWHP Heating Booster Mode On Speed CapFT Curve", + idfObject.getString(HeatPump_AirToWaterFields::NormalizedHeatingCapacityFunctionofTemperatureCurveNameinBoosterMode).get()); + EXPECT_EQ("AWHP Heating Booster Mode On Speed EIRfT Curve", + idfObject.getString(HeatPump_AirToWaterFields::HeatingEnergyInputRatioFunctionofTemperatureCurveNameinBoosterMode).get()); + EXPECT_EQ("AWHP Heating Booster Mode On Speed EIRfPLR Curve", + idfObject.getString(HeatPump_AirToWaterFields::HeatingEnergyInputRatioFunctionofPLRCurveNameinBoosterMode).get()); + + // COOLING + EXPECT_EQ("AWHP Cooling Avail Schedule", idfObject.getString(HeatPump_AirToWaterFields::AvailabilityScheduleNameCooling).get()); + EXPECT_EQ(0.6, idfObject.getDouble(HeatPump_AirToWaterFields::RatedInletAirTemperatureinCoolingMode).get()); + // EXPECT_EQ("Autosize", idfObject.getString(HeatPump_AirToWaterFields::RatedAirFlowRateinCoolingMode).get()); + EXPECT_EQ(0.7, idfObject.getDouble(HeatPump_AirToWaterFields::RatedAirFlowRateinCoolingMode).get()); + EXPECT_EQ(0.8, idfObject.getDouble(HeatPump_AirToWaterFields::RatedLeavingWaterTemperatureinCoolingMode).get()); + // EXPECT_EQ("Autosize", idfObject.getString(HeatPump_AirToWaterFields::RatedWaterFlowRateinCoolingMode).get()); + EXPECT_EQ(0.9, idfObject.getDouble(HeatPump_AirToWaterFields::RatedWaterFlowRateinCoolingMode).get()); + EXPECT_EQ(-5.0, idfObject.getDouble(HeatPump_AirToWaterFields::MinimumOutdoorAirTemperatureinCoolingMode).get()); + EXPECT_EQ(50.0, idfObject.getDouble(HeatPump_AirToWaterFields::MaximumOutdoorAirTemperatureinCoolingMode).get()); + EXPECT_EQ("AWHP Cooling MinLWT Curve", + idfObject.getString(HeatPump_AirToWaterFields::MinimumLeavingWaterTemperatureCurveNameinCoolingMode).get()); + EXPECT_EQ("AWHP Cooling MaxLWT Curve", + idfObject.getString(HeatPump_AirToWaterFields::MaximumLeavingWaterTemperatureCurveNameinCoolingMode).get()); + + EXPECT_EQ(1.5, idfObject.getDouble(HeatPump_AirToWaterFields::SizingFactorforCooling).get()); + + EXPECT_EQ(awhp_cc.inletModelObject()->nameString(), idfObject.getString(HeatPump_AirToWaterFields::ChilledWaterInletNodeName).get()); + EXPECT_EQ(awhp_cc.outletModelObject()->nameString(), idfObject.getString(HeatPump_AirToWaterFields::ChilledWaterOutletNodeName).get()); + + EXPECT_EQ(5, idfObject.getInt(HeatPump_AirToWaterFields::NumberofSpeedsforCooling).get()); + + // EXPECT_EQ("Autosize", idfObject.getString(HeatPump_AirToWaterFields::RatedCoolingCapacityatSpeed1).get()); + EXPECT_EQ(2000.0, idfObject.getDouble(HeatPump_AirToWaterFields::RatedCoolingCapacityatSpeed1).get()); + EXPECT_EQ(4.5, idfObject.getDouble(HeatPump_AirToWaterFields::RatedCOPforCoolingatSpeed1).get()); + EXPECT_EQ("AWHP Cooling Speed 1 CapFT Curve", + idfObject.getString(HeatPump_AirToWaterFields::NormalizedCoolingCapacityFunctionofTemperatureCurveNameatSpeed1).get()); + EXPECT_EQ("AWHP Cooling Speed 1 EIRfT Curve", + idfObject.getString(HeatPump_AirToWaterFields::CoolingEnergyInputRatioFunctionofTemperatureCurveNameatSpeed1).get()); + EXPECT_EQ("AWHP Cooling Speed 1 EIRfPLR Curve", + idfObject.getString(HeatPump_AirToWaterFields::CoolingEnergyInputRatioFunctionofPLRCurveNameatSpeed1).get()); + EXPECT_EQ(4000.0, idfObject.getDouble(HeatPump_AirToWaterFields::RatedCoolingCapacityatSpeed2).get()); + EXPECT_EQ(4.4, idfObject.getDouble(HeatPump_AirToWaterFields::RatedCOPforCoolingatSpeed2).get()); + EXPECT_EQ("AWHP Cooling Speed 2 CapFT Curve", + idfObject.getString(HeatPump_AirToWaterFields::NormalizedCoolingCapacityFunctionofTemperatureCurveNameatSpeed2).get()); + EXPECT_EQ("AWHP Cooling Speed 2 EIRfT Curve", + idfObject.getString(HeatPump_AirToWaterFields::CoolingEnergyInputRatioFunctionofTemperatureCurveNameatSpeed2).get()); + EXPECT_EQ("AWHP Cooling Speed 2 EIRfPLR Curve", + idfObject.getString(HeatPump_AirToWaterFields::CoolingEnergyInputRatioFunctionofPLRCurveNameatSpeed2).get()); + EXPECT_EQ(6000.0, idfObject.getDouble(HeatPump_AirToWaterFields::RatedCoolingCapacityatSpeed3).get()); + EXPECT_EQ(4.3, idfObject.getDouble(HeatPump_AirToWaterFields::RatedCOPforCoolingatSpeed3).get()); + EXPECT_EQ("AWHP Cooling Speed 3 CapFT Curve", + idfObject.getString(HeatPump_AirToWaterFields::NormalizedCoolingCapacityFunctionofTemperatureCurveNameatSpeed3).get()); + EXPECT_EQ("AWHP Cooling Speed 3 EIRfT Curve", + idfObject.getString(HeatPump_AirToWaterFields::CoolingEnergyInputRatioFunctionofTemperatureCurveNameatSpeed3).get()); + EXPECT_EQ("AWHP Cooling Speed 3 EIRfPLR Curve", + idfObject.getString(HeatPump_AirToWaterFields::CoolingEnergyInputRatioFunctionofPLRCurveNameatSpeed3).get()); + EXPECT_EQ(8000.0, idfObject.getDouble(HeatPump_AirToWaterFields::RatedCoolingCapacityatSpeed4).get()); + EXPECT_EQ(4.2, idfObject.getDouble(HeatPump_AirToWaterFields::RatedCOPforCoolingatSpeed4).get()); + EXPECT_EQ("AWHP Cooling Speed 4 CapFT Curve", + idfObject.getString(HeatPump_AirToWaterFields::NormalizedCoolingCapacityFunctionofTemperatureCurveNameatSpeed4).get()); + EXPECT_EQ("AWHP Cooling Speed 4 EIRfT Curve", + idfObject.getString(HeatPump_AirToWaterFields::CoolingEnergyInputRatioFunctionofTemperatureCurveNameatSpeed4).get()); + EXPECT_EQ("AWHP Cooling Speed 4 EIRfPLR Curve", + idfObject.getString(HeatPump_AirToWaterFields::CoolingEnergyInputRatioFunctionofPLRCurveNameatSpeed4).get()); + EXPECT_EQ("Autosize", idfObject.getString(HeatPump_AirToWaterFields::RatedCoolingCapacityatSpeed5).get()); + EXPECT_EQ(4.1, idfObject.getDouble(HeatPump_AirToWaterFields::RatedCOPforCoolingatSpeed5).get()); + EXPECT_EQ("AWHP Cooling Speed 5 CapFT Curve", + idfObject.getString(HeatPump_AirToWaterFields::NormalizedCoolingCapacityFunctionofTemperatureCurveNameatSpeed5).get()); + EXPECT_EQ("AWHP Cooling Speed 5 EIRfT Curve", + idfObject.getString(HeatPump_AirToWaterFields::CoolingEnergyInputRatioFunctionofTemperatureCurveNameatSpeed5).get()); + EXPECT_EQ("AWHP Cooling Speed 5 EIRfPLR Curve", + idfObject.getString(HeatPump_AirToWaterFields::CoolingEnergyInputRatioFunctionofPLRCurveNameatSpeed5).get()); + EXPECT_EQ("Yes", idfObject.getString(HeatPump_AirToWaterFields::BoosterModeOnCooling).get()); + EXPECT_EQ(12000.0, idfObject.getDouble(HeatPump_AirToWaterFields::RatedCoolingCapacityinBoosterMode).get()); + EXPECT_EQ(3.5, idfObject.getDouble(HeatPump_AirToWaterFields::RatedCoolingCOPinBoosterMode).get()); + EXPECT_EQ("AWHP Cooling Booster Mode On Speed CapFT Curve", + idfObject.getString(HeatPump_AirToWaterFields::NormalizedCoolingCapacityFunctionofTemperatureCurveNameinBoosterMode).get()); + EXPECT_EQ("AWHP Cooling Booster Mode On Speed EIRfT Curve", + idfObject.getString(HeatPump_AirToWaterFields::CoolingEnergyInputRatioFunctionofTemperatureCurveNameinBoosterMode).get()); + EXPECT_EQ("AWHP Cooling Booster Mode On Speed EIRfPLR Curve", + idfObject.getString(HeatPump_AirToWaterFields::CoolingEnergyInputRatioFunctionofPLRCurveNameinBoosterMode).get()); + + const size_t expectedNumCurves = 1 // defrostEnergyInputRatioFunctionofTemperatureCurve + + 1 // crankcaseHeaterCapacityFunctionofTemperatureCurve + + 2 * 2 // awhp_hc/cc min/max LWT curves + + 2 * (5 + 1) * 3; // awhp_hc/cc: 5 speeds + one booster mode, each with 3 curves + EXPECT_EQ(expectedNumCurves, + w.getObjectsByType(IddObjectType::Curve_Biquadratic).size() + w.getObjectsByType(IddObjectType::Curve_Quadratic).size()); + auto oaNodeList = w.getObjectsByType(IddObjectType::OutdoorAir_NodeList); + ASSERT_EQ(1, oaNodeList.size()); + EXPECT_EQ("AWHP Air Inlet Node", oaNodeList.front().getString(0).get()); + EXPECT_EQ("AWHP Air Outlet Node", oaNodeList.front().getString(1).get()); + + EXPECT_EQ(2, w.getObjectsByType(IddObjectType::PlantLoop).size()); + + std::vector expecteds = { + Expected{.isHeating = true, + .plantName = hwLoop.nameString(), + .inletNodeName = awhp_hc.inletModelObject()->nameString(), + .outletNodeName = awhp_hc.outletModelObject()->nameString()}, + Expected{.isHeating = false, + .plantName = chwLoop.nameString(), + .inletNodeName = awhp_cc.inletModelObject()->nameString(), + .outletNodeName = awhp_cc.outletModelObject()->nameString()}, + }; + for (const auto& e : expecteds) { + auto p_ = w.getObjectByTypeAndName(IddObjectType::PlantLoop, e.plantName); + ASSERT_TRUE(p_.is_initialized()) << "Cannot find PlantLoop named " << e.plantName; + WorkspaceObject idf_plant = p_.get(); + WorkspaceObject idf_brlist = idf_plant.getTarget(PlantLoopFields::PlantSideBranchListName).get(); + + // Should have at three branches: supply inlet, the one with the AWHP, supply outlet. + ASSERT_EQ(3, idf_brlist.extensibleGroups().size()) << "Failed for " << e.plantName; + // Get the AWHP one + auto w_eg = idf_brlist.extensibleGroups()[1].cast(); + WorkspaceObject idf_branch = w_eg.getTarget(BranchListExtensibleFields::BranchName).get(); + + // There should be only one equipment on the branch + ASSERT_EQ(1, idf_branch.extensibleGroups().size()); + auto w_eg2 = idf_branch.extensibleGroups()[0].cast(); + + ASSERT_EQ(w_eg2.getString(BranchExtensibleFields::ComponentName).get(), awhp.nameString()); + ASSERT_EQ(w_eg2.getString(BranchExtensibleFields::ComponentInletNodeName).get(), e.inletNodeName); + ASSERT_EQ(w_eg2.getString(BranchExtensibleFields::ComponentOutletNodeName).get(), e.outletNodeName); + + WorkspaceObject idf_plant_op = p_->getTarget(PlantLoopFields::PlantEquipmentOperationSchemeName).get(); + // Should have created a Cooling Load one only + ASSERT_EQ(1, idf_plant_op.extensibleGroups().size()); + auto w_eg_schemes = idf_plant_op.extensibleGroups()[0].cast(); + ASSERT_EQ(e.isHeating ? "PlantEquipmentOperation:HeatingLoad" : "PlantEquipmentOperation:CoolingLoad", + w_eg_schemes.getString(PlantEquipmentOperationSchemesExtensibleFields::ControlSchemeObjectType).get()); + + // Get the Operation Scheme + auto op_scheme_ = w_eg_schemes.getTarget(PlantEquipmentOperationSchemesExtensibleFields::ControlSchemeName); + ASSERT_TRUE(op_scheme_); + + // Get the Plant Equipment List of this CoolingLoad/HeatingLoad scheme + // There should only be one Load Range + ASSERT_EQ(1u, op_scheme_->extensibleGroups().size()); + + // Load range 1 + w_eg = op_scheme_->extensibleGroups()[0].cast(); + boost::optional peq_list_; + if (e.isHeating) { + peq_list_ = w_eg.getTarget(PlantEquipmentOperation_HeatingLoadExtensibleFields::RangeEquipmentListName); + } else { + peq_list_ = w_eg.getTarget(PlantEquipmentOperation_CoolingLoadExtensibleFields::RangeEquipmentListName); + } + ASSERT_TRUE(peq_list_); + + // Should have one equipment on it: HeatPump:AirToWater + auto peqs = peq_list_->extensibleGroups(); + ASSERT_EQ(1, peqs.size()); + ASSERT_EQ("HeatPump:AirToWater", peqs.front().getString(PlantEquipmentListExtensibleFields::EquipmentObjectType).get()); + ASSERT_EQ(awhp.nameString(), peqs.front().getString(PlantEquipmentListExtensibleFields::EquipmentName).get()); + } + } +} + +TEST_F(EnergyPlusFixture, ReverseTranslator_HeatPumpAirToWater) { + + ForwardTranslator ft; + ft.setExcludeLCCObjects(true); + + Model m; + + HeatPumpAirToWater awhp = makeAWHP(m); + HeatPumpAirToWaterHeating awhp_hc = makeAWHP_Heating(m); + EXPECT_TRUE(awhp.setHeatingOperationMode(awhp_hc)); + + HeatPumpAirToWaterCooling awhp_cc = makeAWHP_Cooling(m); + EXPECT_TRUE(awhp.setCoolingOperationMode(awhp_cc)); + + PlantLoop hwLoop = createLoop(m, "HW Loop"); + EXPECT_TRUE(hwLoop.addSupplyBranchForComponent(awhp_hc)); + + PlantLoop chwLoop = createLoop(m, "ChW Loop"); + EXPECT_TRUE(chwLoop.addSupplyBranchForComponent(awhp_cc)); + + { + Workspace w = ft.translateModel(m); + ReverseTranslator rt; + Model m2 = rt.translateWorkspace(w); + EXPECT_EQ(1, m.getConcreteModelObjects().size()); + EXPECT_EQ(1, m.getConcreteModelObjects().size()); + EXPECT_EQ(5, m.getConcreteModelObjects().front().numberOfSpeeds()); + EXPECT_TRUE(m.getConcreteModelObjects().front().boosterModeOnSpeed()); + EXPECT_EQ(6, m.getConcreteModelObjects().size()); + EXPECT_EQ(1, m.getConcreteModelObjects().size()); + EXPECT_EQ(5, m.getConcreteModelObjects().front().numberOfSpeeds()); + EXPECT_TRUE(m.getConcreteModelObjects().front().boosterModeOnSpeed()); + EXPECT_EQ(6, m.getConcreteModelObjects().size()); + const size_t expectedNumCurves = 1 // defrostEnergyInputRatioFunctionofTemperatureCurve + + 1 // crankcaseHeaterCapacityFunctionofTemperatureCurve + + 2 * 2 // awhp_hc/cc min/max LWT curves + + 2 * (5 + 1) * 3; // awhp_hc/cc: 5 speeds + one booster mode, each with 3 curves + EXPECT_EQ(expectedNumCurves, m.getModelObjects().size()); + // m.save("HeatPumpAirToWater_original.osm"); + // m2.save("HeatPumpAirToWater_roundtrip.osm"); + } +} diff --git a/src/model/CMakeLists.txt b/src/model/CMakeLists.txt index aa91ad5add..ed16f521b0 100644 --- a/src/model/CMakeLists.txt +++ b/src/model/CMakeLists.txt @@ -920,6 +920,21 @@ set(${target_name}_src HeatPumpAirToWaterFuelFiredCooling.hpp HeatPumpAirToWaterFuelFiredCooling_Impl.hpp HeatPumpAirToWaterFuelFiredCooling.cpp + HeatPumpAirToWater.hpp + HeatPumpAirToWater_Impl.hpp + HeatPumpAirToWater.cpp + HeatPumpAirToWaterCooling.hpp + HeatPumpAirToWaterCooling_Impl.hpp + HeatPumpAirToWaterCooling.cpp + HeatPumpAirToWaterCoolingSpeedData.hpp + HeatPumpAirToWaterCoolingSpeedData_Impl.hpp + HeatPumpAirToWaterCoolingSpeedData.cpp + HeatPumpAirToWaterHeating.hpp + HeatPumpAirToWaterHeating_Impl.hpp + HeatPumpAirToWaterHeating.cpp + HeatPumpAirToWaterHeatingSpeedData.hpp + HeatPumpAirToWaterHeatingSpeedData_Impl.hpp + HeatPumpAirToWaterHeatingSpeedData.cpp HeatPumpWaterToWaterEquationFitCooling.hpp HeatPumpWaterToWaterEquationFitCooling_Impl.hpp HeatPumpWaterToWaterEquationFitCooling.cpp @@ -2163,6 +2178,11 @@ set(${target_name}_test_src test/HeatExchangerFluidToFluid_GTest.cpp test/HeatPumpAirToWaterFuelFiredHeating_GTest.cpp test/HeatPumpAirToWaterFuelFiredCooling_GTest.cpp + test/HeatPumpAirToWater_GTest.cpp + test/HeatPumpAirToWaterCooling_GTest.cpp + test/HeatPumpAirToWaterCoolingSpeedData_GTest.cpp + test/HeatPumpAirToWaterHeating_GTest.cpp + test/HeatPumpAirToWaterHeatingSpeedData_GTest.cpp test/HeatPumpWaterToWaterEquationFitHeating_GTest.cpp test/HeatPumpWaterToWaterEquationFitCooling_GTest.cpp test/HeatPumpPlantLoopEIRHeating_GTest.cpp diff --git a/src/model/ConcreteModelObjects.hpp b/src/model/ConcreteModelObjects.hpp index 57e043e49b..97b16b6232 100644 --- a/src/model/ConcreteModelObjects.hpp +++ b/src/model/ConcreteModelObjects.hpp @@ -290,6 +290,11 @@ #include "HeatExchangerFluidToFluid.hpp" #include "HeatPumpAirToWaterFuelFiredHeating.hpp" #include "HeatPumpAirToWaterFuelFiredCooling.hpp" +#include "HeatPumpAirToWater.hpp" +#include "HeatPumpAirToWaterCooling.hpp" +#include "HeatPumpAirToWaterCoolingSpeedData.hpp" +#include "HeatPumpAirToWaterHeating.hpp" +#include "HeatPumpAirToWaterHeatingSpeedData.hpp" #include "HeatPumpWaterToWaterEquationFitCooling.hpp" #include "HeatPumpWaterToWaterEquationFitHeating.hpp" #include "HeatPumpPlantLoopEIRCooling.hpp" @@ -849,6 +854,11 @@ #include "HeatExchangerFluidToFluid_Impl.hpp" #include "HeatPumpAirToWaterFuelFiredHeating_Impl.hpp" #include "HeatPumpAirToWaterFuelFiredCooling_Impl.hpp" +#include "HeatPumpAirToWater_Impl.hpp" +#include "HeatPumpAirToWaterCooling_Impl.hpp" +#include "HeatPumpAirToWaterCoolingSpeedData_Impl.hpp" +#include "HeatPumpAirToWaterHeating_Impl.hpp" +#include "HeatPumpAirToWaterHeatingSpeedData_Impl.hpp" #include "HeatPumpWaterToWaterEquationFitCooling_Impl.hpp" #include "HeatPumpWaterToWaterEquationFitHeating_Impl.hpp" #include "HeatPumpPlantLoopEIRCooling_Impl.hpp" diff --git a/src/model/HeatPumpAirToWater.cpp b/src/model/HeatPumpAirToWater.cpp new file mode 100644 index 0000000000..989e9fa214 --- /dev/null +++ b/src/model/HeatPumpAirToWater.cpp @@ -0,0 +1,784 @@ +/*********************************************************************************************************************** +* OpenStudio(R), Copyright (c) Alliance for Sustainable Energy, LLC. +* See also https://openstudio.net/license +***********************************************************************************************************************/ + +#include "HeatPumpAirToWater.hpp" +#include "HeatPumpAirToWater_Impl.hpp" + +#include "Model.hpp" +#include "Curve.hpp" +#include "Curve_Impl.hpp" +#include "HeatPumpAirToWaterHeating.hpp" +#include "HeatPumpAirToWaterHeating_Impl.hpp" +#include "HeatPumpAirToWaterCooling.hpp" +#include "HeatPumpAirToWaterCooling_Impl.hpp" +#include "PlantLoop.hpp" +#include "Schedule.hpp" +#include "Schedule_Impl.hpp" +#include "ScheduleTypeLimits.hpp" +#include "ScheduleTypeRegistry.hpp" + +#include "../utilities/core/Assert.hpp" +#include "../utilities/core/ContainersMove.hpp" +#include "../utilities/data/DataEnums.hpp" + +#include +#include +#include + +namespace openstudio { +namespace model { + + namespace detail { + + HeatPumpAirToWater_Impl::HeatPumpAirToWater_Impl(const IdfObject& idfObject, Model_Impl* model, bool keepHandle) + : StraightComponent_Impl(idfObject, model, keepHandle) { + OS_ASSERT(idfObject.iddObject().type() == HeatPumpAirToWater::iddObjectType()); + } + + HeatPumpAirToWater_Impl::HeatPumpAirToWater_Impl(const openstudio::detail::WorkspaceObject_Impl& other, Model_Impl* model, bool keepHandle) + : StraightComponent_Impl(other, model, keepHandle) { + OS_ASSERT(other.iddObject().type() == HeatPumpAirToWater::iddObjectType()); + } + + HeatPumpAirToWater_Impl::HeatPumpAirToWater_Impl(const HeatPumpAirToWater_Impl& other, Model_Impl* model, bool keepHandle) + : StraightComponent_Impl(other, model, keepHandle) {} + + const std::vector& HeatPumpAirToWater_Impl::outputVariableNames() const { + static const std::vector result{ + "Heat Pump Part Load Ratio in Heating Mode", + "Heat Pump Cycling Ratio in Heating Mode", + "Heat Pump Load Side Heat Transfer Rate in Heating Mode", + "Heat Pump Load Side Heat Transfer Energy in Heating Mode", + "Heat Pump Source Side Heat Transfer Rate in Heating Mode", + "Heat Pump Source Side Heat Transfer Energy in Heating Mode", + "Heat Pump Load Side Inlet Temperature in Heating Mode", + "Heat Pump Load Side Outlet Temperature in Heating Mode", + "Heat Pump Source Side Inlet Temperature in Heating Mode", + "Heat Pump Source Side Outlet Temperature in Heating Mode", + "Heat Pump Electricity Rate in Heating Mode", + "Heat Pump Electricity Energy in Heating Mode", + "Heat Pump Load Due To Defrost", + "Heat Pump Fractional Defrost Time", + "Heat Pump Defrost Electricity Rate", + "Heat Pump Defrost Electricity Energy", + "Heat Pump Load Side Mass Flow Rate in Heating Mode", + "Heat Pump Source Side Mass Flow Rate in Heating Mode", + "Heat Pump Total Heating Rate", + "Heat Pump Number Of Heating Unit On", + "Heat Pump Speed Level in Heating Mode", + "Heat Pump Speed Ratio in Heating Mode", + "Heat Pump Air Flow Rate in Heating Mode", + "Heat Pump Inlet Air Temperature in Heating Mode", + "Heat Pump Outlet Air Temperature in Heating Mode", + "Heat Pump Capacity Temperature Modifier in Heating Mode", + "Heat Pump EIR Temperature Modifier in Heating Mode", + "Heat Pump EIR PLR Modifier in Heating Mode", + "Heat Pump Crankcase Heater Electricity Rate", + "Heat Pump Crankcase Heater Electricity Energy", + "Heat Pump Heating COP", + "Heat Pump Part Load Ratio in Cooling Mode", + "Heat Pump Cycling Ratio in Cooling Mode", + "Heat Pump Load Side Heat Transfer Rate in Cooling Mode", + "Heat Pump Load Side Heat Transfer Energy in Cooling Mode", + "Heat Pump Source Side Heat Transfer Rate in Cooling Mode", + "Heat Pump Source Side Heat Transfer Energy in Cooling Mode", + "Heat Pump Load Side Inlet Temperature in Cooling Mode", + "Heat Pump Load Side Outlet Temperature in Cooling Mode", + "Heat Pump Source Side Inlet Temperature in Cooling Mode", + "Heat Pump Source Side Outlet Temperature in Cooling Mode", + "Heat Pump Electricity Rate in Cooling Mode", + "Heat Pump Electricity Energy in Cooling Mode", + "Heat Pump Load Side Mass Flow Rate in Cooling Mode", + "Heat Pump Source Side Mass Flow Rate in Cooling Mode", + "Heat Pump Total Cooling Rate", + "Heat Pump Number Of Cooling Unit On", + "Heat Pump Speed Level in Cooling Mode", + "Heat Pump Speed Ratio in Cooling Mode", + "Heat Pump Air Flow Rate in Cooling Mode", + "Heat Pump Inlet Air Temperature in Cooling Mode", + "Heat Pump Outlet Air Temperature in Cooling Mode", + "Heat Pump Capacity Temperature Modifier in Cooling Mode", + "Heat Pump EIR Temperature Modifier in Cooling Mode", + "Heat Pump EIR PLR Modifier in Cooling Mode", + "Heat Pump Cooling COP", + }; + return result; + } + + IddObjectType HeatPumpAirToWater_Impl::iddObjectType() const { + return HeatPumpAirToWater::iddObjectType(); + } + + std::vector HeatPumpAirToWater_Impl::getScheduleTypeKeys(const Schedule& schedule) const { + std::vector result; + const UnsignedVector fieldIndices = getSourceIndices(schedule.handle()); + if (std::find(fieldIndices.cbegin(), fieldIndices.cend(), OS_HeatPump_AirToWaterFields::OperatingModeControlScheduleName) + != fieldIndices.cend()) { + result.emplace_back("HeatPumpAirToWater", "Operating Mode Control"); + } + return result; + } + + unsigned HeatPumpAirToWater_Impl::inletPort() const { + return OS_HeatPump_AirToWaterFields::AirInletNodeName; + } + + unsigned HeatPumpAirToWater_Impl::outletPort() const { + return OS_HeatPump_AirToWaterFields::AirOutletNodeName; + } + + bool HeatPumpAirToWater_Impl::addToNode(Node& /*node*/) { + LOG(Info, "Use the underlying HeatPumpAirToWaterCooling / HeatPumpAirToWaterHeating objects to add to a plant loop."); + return false; + } + + std::vector HeatPumpAirToWater_Impl::children() const { + std::vector children; + if (auto c_ = defrostEnergyInputRatioFunctionofTemperatureCurve()) { + children.emplace_back(std::move(*c_)); + } + if (auto c_ = crankcaseHeaterCapacityFunctionofTemperatureCurve()) { + children.emplace_back(std::move(*c_)); + } + if (auto c_ = coolingOperationMode()) { + children.emplace_back(std::move(*c_)); + } + if (auto c_ = heatingOperationMode()) { + children.emplace_back(std::move(*c_)); + } + return children; + } + + ModelObject HeatPumpAirToWater_Impl::clone(Model model) const { + auto t_clone = StraightComponent_Impl::clone(model).cast(); + // We clone the operation modes, because the autosizing is reported at the wrapper level + // AND more importantly in OS, the modes are the the PlantLoops and that's what triggers the translation of the first wrapper (this object) it + // finds attached to it, so we must ensure they are unique + if (auto coolingOpMode_ = coolingOperationMode()) { + auto coolingOpModeClone = coolingOpMode_->clone(model).cast(); + bool ok = t_clone.setCoolingOperationMode(coolingOpModeClone); + OS_ASSERT(ok); + } + if (auto heatingOpMode_ = heatingOperationMode()) { + auto heatingOpModeClone = heatingOpMode_->clone(model).cast(); + bool ok = t_clone.setHeatingOperationMode(heatingOpModeClone); + OS_ASSERT(ok); + } + + return std::move(t_clone); + } + + std::vector HeatPumpAirToWater_Impl::remove() { + std::vector result; + + // We are the only one using these cooling/heating operation modes but design, remove it (it will remove the children too) + // But first reset so it's marked as removable + if (auto coolingOpMode_ = coolingOperationMode()) { + resetCoolingOperationMode(); + result = coolingOpMode_->remove(); + } + if (auto heatingOpMode_ = heatingOperationMode()) { + resetHeatingOperationMode(); + openstudio::detail::concat_helper(result, heatingOpMode_->remove()); + } + openstudio::detail::concat_helper(result, StraightComponent_Impl::remove()); + return result; + } + + ComponentType HeatPumpAirToWater_Impl::componentType() const { + bool has_cooling = coolingOperationMode().is_initialized(); + bool has_heating = heatingOperationMode().is_initialized(); + if (has_cooling && has_heating) { + return ComponentType::Both; + } else if (has_cooling) { + return ComponentType::Cooling; + } else if (has_heating) { + return ComponentType::Heating; + } + return ComponentType::None; + } + + std::vector HeatPumpAirToWater_Impl::coolingFuelTypes() const { + if (auto cc_ = coolingOperationMode()) { + return cc_->coolingFuelTypes(); + } + return {}; + } + + std::vector HeatPumpAirToWater_Impl::heatingFuelTypes() const { + if (auto hc_ = heatingOperationMode()) { + return hc_->heatingFuelTypes(); + } + return {}; + } + + std::vector HeatPumpAirToWater_Impl::appGHeatingFuelTypes() const { + if (auto hc_ = heatingOperationMode()) { + return hc_->appGHeatingFuelTypes(); + } + return {}; + } + + std::string HeatPumpAirToWater_Impl::operatingModeControlMethod() const { + boost::optional value = getString(OS_HeatPump_AirToWaterFields::OperatingModeControlMethod, true); + OS_ASSERT(value); + return value.get(); + } + + std::string HeatPumpAirToWater_Impl::operatingModeControlOptionforMultipleUnit() const { + boost::optional value = getString(OS_HeatPump_AirToWaterFields::OperatingModeControlOptionforMultipleUnit, true); + OS_ASSERT(value); + return value.get(); + } + + boost::optional HeatPumpAirToWater_Impl::operatingModeControlSchedule() const { + return getObject().getModelObjectTarget(OS_HeatPump_AirToWaterFields::OperatingModeControlScheduleName); + } + + double HeatPumpAirToWater_Impl::minimumPartLoadRatio() const { + boost::optional value = getDouble(OS_HeatPump_AirToWaterFields::MinimumPartLoadRatio, true); + OS_ASSERT(value); + return value.get(); + } + + boost::optional HeatPumpAirToWater_Impl::airInletNodeName() const { + return getString(OS_HeatPump_AirToWaterFields::AirInletNodeName, false, true); // Return empty if not initialized + } + + boost::optional HeatPumpAirToWater_Impl::airOutletNodeName() const { + return getString(OS_HeatPump_AirToWaterFields::AirOutletNodeName, false, true); // Return empty if not initialized + } + + double HeatPumpAirToWater_Impl::maximumOutdoorDryBulbTemperatureForDefrostOperation() const { + boost::optional value = getDouble(OS_HeatPump_AirToWaterFields::MaximumOutdoorDryBulbTemperatureForDefrostOperation, true); + OS_ASSERT(value); + return value.get(); + } + + std::string HeatPumpAirToWater_Impl::heatPumpDefrostControl() const { + boost::optional value = getString(OS_HeatPump_AirToWaterFields::HeatPumpDefrostControl, true); + OS_ASSERT(value); + return value.get(); + } + + double HeatPumpAirToWater_Impl::heatPumpDefrostTimePeriodFraction() const { + boost::optional value = getDouble(OS_HeatPump_AirToWaterFields::HeatPumpDefrostTimePeriodFraction, true); + OS_ASSERT(value); + return value.get(); + } + + double HeatPumpAirToWater_Impl::resistiveDefrostHeaterCapacity() const { + boost::optional value = getDouble(OS_HeatPump_AirToWaterFields::ResistiveDefrostHeaterCapacity, true); + OS_ASSERT(value); + return value.get(); + } + + boost::optional HeatPumpAirToWater_Impl::defrostEnergyInputRatioFunctionofTemperatureCurve() const { + return getObject().getModelObjectTarget( + OS_HeatPump_AirToWaterFields::DefrostEnergyInputRatioFunctionofTemperatureCurveName); + } + + int HeatPumpAirToWater_Impl::heatPumpMultiplier() const { + boost::optional value = getInt(OS_HeatPump_AirToWaterFields::HeatPumpMultiplier, true); + OS_ASSERT(value); + return value.get(); + } + + std::string HeatPumpAirToWater_Impl::controlType() const { + boost::optional value = getString(OS_HeatPump_AirToWaterFields::ControlType, true); + OS_ASSERT(value); + return value.get(); + } + + double HeatPumpAirToWater_Impl::crankcaseHeaterCapacity() const { + boost::optional value = getDouble(OS_HeatPump_AirToWaterFields::CrankcaseHeaterCapacity, true); + OS_ASSERT(value); + return value.get(); + } + + boost::optional HeatPumpAirToWater_Impl::crankcaseHeaterCapacityFunctionofTemperatureCurve() const { + return getObject().getModelObjectTarget( + OS_HeatPump_AirToWaterFields::CrankcaseHeaterCapacityFunctionofTemperatureCurveName); + } + + double HeatPumpAirToWater_Impl::maximumAmbientTemperatureforCrankcaseHeaterOperation() const { + boost::optional value = getDouble(OS_HeatPump_AirToWaterFields::MaximumAmbientTemperatureforCrankcaseHeaterOperation, true); + OS_ASSERT(value); + return value.get(); + } + + boost::optional HeatPumpAirToWater_Impl::heatingOperationMode() const { + return getObject().getModelObjectTarget(OS_HeatPump_AirToWaterFields::HeatingOperationMode); + } + + boost::optional HeatPumpAirToWater_Impl::coolingOperationMode() const { + return getObject().getModelObjectTarget(OS_HeatPump_AirToWaterFields::CoolingOperationMode); + } + + bool HeatPumpAirToWater_Impl::setOperatingModeControlMethod(const std::string& operatingModeControlMethod) { + if (operatingModeControlMethod == "ScheduledModes" && !operatingModeControlSchedule()) { + LOG(Warn, "For " << briefDescription() << ", to use 'ScheduledModes' use setOperatingModeControlSchedule"); + return false; + } + const bool result = setString(OS_HeatPump_AirToWaterFields::OperatingModeControlMethod, operatingModeControlMethod); + return result; + } + + bool HeatPumpAirToWater_Impl::setOperatingModeControlOptionforMultipleUnit(const std::string& operatingModeControlOptionforMultipleUnit) { + const bool result = + setString(OS_HeatPump_AirToWaterFields::OperatingModeControlOptionforMultipleUnit, operatingModeControlOptionforMultipleUnit); + return result; + } + + bool HeatPumpAirToWater_Impl::setOperatingModeControlSchedule(Schedule& operatingModeControlSchedule) { + bool result = setSchedule(OS_HeatPump_AirToWaterFields::OperatingModeControlScheduleName, "HeatPumpAirToWater", "Operating Mode Control", + operatingModeControlSchedule); + result &= setString(OS_HeatPump_AirToWaterFields::OperatingModeControlMethod, "ScheduledModes"); + return result; + } + + void HeatPumpAirToWater_Impl::resetOperatingModeControlSchedule() { + const bool result = setString(OS_HeatPump_AirToWaterFields::OperatingModeControlScheduleName, ""); + OS_ASSERT(result); + setString(OS_HeatPump_AirToWaterFields::OperatingModeControlMethod, "Load"); // Reset to default + } + + bool HeatPumpAirToWater_Impl::setMinimumPartLoadRatio(double minimumPartLoadRatio) { + const bool result = setDouble(OS_HeatPump_AirToWaterFields::MinimumPartLoadRatio, minimumPartLoadRatio); + return result; + } + + bool HeatPumpAirToWater_Impl::setAirInletNodeName(const std::string& airInletNodeName) { + const bool result = setString(OS_HeatPump_AirToWaterFields::AirInletNodeName, airInletNodeName); + OS_ASSERT(result); + return result; + } + + void HeatPumpAirToWater_Impl::resetAirInletNodeName() { + const bool result = setString(OS_HeatPump_AirToWaterFields::AirInletNodeName, ""); + OS_ASSERT(result); + } + + bool HeatPumpAirToWater_Impl::setAirOutletNodeName(const std::string& airOutletNodeName) { + const bool result = setString(OS_HeatPump_AirToWaterFields::AirOutletNodeName, airOutletNodeName); + OS_ASSERT(result); + return result; + } + + void HeatPumpAirToWater_Impl::resetAirOutletNodeName() { + const bool result = setString(OS_HeatPump_AirToWaterFields::AirOutletNodeName, ""); + OS_ASSERT(result); + } + + bool HeatPumpAirToWater_Impl::setMaximumOutdoorDryBulbTemperatureForDefrostOperation(double maximumOutdoorDryBulbTemperatureForDefrostOperation) { + const bool result = setDouble(OS_HeatPump_AirToWaterFields::MaximumOutdoorDryBulbTemperatureForDefrostOperation, + maximumOutdoorDryBulbTemperatureForDefrostOperation); + OS_ASSERT(result); + return result; + } + + bool HeatPumpAirToWater_Impl::setHeatPumpDefrostControl(const std::string& heatPumpDefrostControl) { + const bool result = setString(OS_HeatPump_AirToWaterFields::HeatPumpDefrostControl, heatPumpDefrostControl); + return result; + } + + bool HeatPumpAirToWater_Impl::setHeatPumpDefrostTimePeriodFraction(double heatPumpDefrostTimePeriodFraction) { + const bool result = setDouble(OS_HeatPump_AirToWaterFields::HeatPumpDefrostTimePeriodFraction, heatPumpDefrostTimePeriodFraction); + return result; + } + + bool HeatPumpAirToWater_Impl::setResistiveDefrostHeaterCapacity(double resistiveDefrostHeaterCapacity) { + const bool result = setDouble(OS_HeatPump_AirToWaterFields::ResistiveDefrostHeaterCapacity, resistiveDefrostHeaterCapacity); + return result; + } + + bool + HeatPumpAirToWater_Impl::setDefrostEnergyInputRatioFunctionofTemperatureCurve(const Curve& defrostEnergyInputRatioFunctionofTemperatureCurve) { + const bool result = setPointer(OS_HeatPump_AirToWaterFields::DefrostEnergyInputRatioFunctionofTemperatureCurveName, + defrostEnergyInputRatioFunctionofTemperatureCurve.handle()); + return result; + } + + void HeatPumpAirToWater_Impl::resetDefrostEnergyInputRatioFunctionofTemperatureCurve() { + const bool result = setString(OS_HeatPump_AirToWaterFields::DefrostEnergyInputRatioFunctionofTemperatureCurveName, ""); + OS_ASSERT(result); + } + + bool HeatPumpAirToWater_Impl::setHeatPumpMultiplier(int heatPumpMultiplier) { + const bool result = setInt(OS_HeatPump_AirToWaterFields::HeatPumpMultiplier, heatPumpMultiplier); + OS_ASSERT(result); + return result; + } + + bool HeatPumpAirToWater_Impl::setControlType(const std::string& controlType) { + const bool result = setString(OS_HeatPump_AirToWaterFields::ControlType, controlType); + return result; + } + + bool HeatPumpAirToWater_Impl::setCrankcaseHeaterCapacity(double crankcaseHeaterCapacity) { + const bool result = setDouble(OS_HeatPump_AirToWaterFields::CrankcaseHeaterCapacity, crankcaseHeaterCapacity); + return result; + } + + bool + HeatPumpAirToWater_Impl::setCrankcaseHeaterCapacityFunctionofTemperatureCurve(const Curve& crankcaseHeaterCapacityFunctionofTemperatureCurve) { + const bool result = setPointer(OS_HeatPump_AirToWaterFields::CrankcaseHeaterCapacityFunctionofTemperatureCurveName, + crankcaseHeaterCapacityFunctionofTemperatureCurve.handle()); + return result; + } + + void HeatPumpAirToWater_Impl::resetCrankcaseHeaterCapacityFunctionofTemperatureCurve() { + const bool result = setString(OS_HeatPump_AirToWaterFields::CrankcaseHeaterCapacityFunctionofTemperatureCurveName, ""); + OS_ASSERT(result); + } + + bool + HeatPumpAirToWater_Impl::setMaximumAmbientTemperatureforCrankcaseHeaterOperation(double maximumAmbientTemperatureforCrankcaseHeaterOperation) { + const bool result = setDouble(OS_HeatPump_AirToWaterFields::MaximumAmbientTemperatureforCrankcaseHeaterOperation, + maximumAmbientTemperatureforCrankcaseHeaterOperation); + return result; + } + + bool HeatPumpAirToWater_Impl::setHeatingOperationMode(const HeatPumpAirToWaterHeating& heatingOperationMode) { + if (auto awhp_ = heatingOperationMode.heatPumpAirToWater(); awhp_ && awhp_->handle() != this->handle()) { + LOG(Warn, "This HeatPumpAirToWaterCooling object is already assigned to a HeatPumpAirToWater object. Cannot assign it again."); + return false; + } + const bool result = setPointer(OS_HeatPump_AirToWaterFields::HeatingOperationMode, heatingOperationMode.handle()); + return result; + } + + void HeatPumpAirToWater_Impl::resetHeatingOperationMode() { + const bool result = setString(OS_HeatPump_AirToWaterFields::HeatingOperationMode, ""); + OS_ASSERT(result); + } + + bool HeatPumpAirToWater_Impl::setCoolingOperationMode(const HeatPumpAirToWaterCooling& coolingOperationMode) { + if (auto awhp_ = coolingOperationMode.heatPumpAirToWater(); awhp_ && awhp_->handle() != this->handle()) { + LOG(Warn, "This HeatPumpAirToWaterCooling object is already assigned to a HeatPumpAirToWater object. Cannot assign it again."); + return false; + } + const bool result = setPointer(OS_HeatPump_AirToWaterFields::CoolingOperationMode, coolingOperationMode.handle()); + return result; + } + + void HeatPumpAirToWater_Impl::resetCoolingOperationMode() { + const bool result = setString(OS_HeatPump_AirToWaterFields::CoolingOperationMode, ""); + OS_ASSERT(result); + } + + boost::optional HeatPumpAirToWater_Impl::autosizedRatedAirFlowRateinHeatingMode() const { + return getAutosizedValue("Design Size Rated Air Volume Flow Rate in Heating Mode", "m3/s"); + } + + boost::optional HeatPumpAirToWater_Impl::autosizedRatedWaterFlowRateinHeatingMode() const { + return getAutosizedValue("Design Size Rated Water Volume Flow Rate in Heating Mode", "m3/s"); + } + + boost::optional HeatPumpAirToWater_Impl::autosizedRatedAirFlowRateinCoolingMode() const { + return getAutosizedValue("Design Size Rated Air Volume Flow Rate in Cooling Mode", "m3/s"); + } + + boost::optional HeatPumpAirToWater_Impl::autosizedRatedWaterFlowRateinCoolingMode() const { + return getAutosizedValue("Design Size Rated Water Volume Flow Rate in Cooling Mode", "m3/s"); + } + + boost::optional HeatPumpAirToWater_Impl::autosizedRatedHeatingCapacity() const { + return getAutosizedValue("Design Size Rated Heating Capacity", "W"); + } + + boost::optional HeatPumpAirToWater_Impl::autosizedRatedCoolingCapacity() const { + return getAutosizedValue("Design Size Rated Cooling Capacity", "W"); + } + + void HeatPumpAirToWater_Impl::autosize() {} + + void HeatPumpAirToWater_Impl::applySizingValues() {} + + boost::optional HeatPumpAirToWater_Impl::coolingLoop() const { + if (auto mode_ = coolingOperationMode()) { + return mode_->plantLoop(); + } + return boost::none; + } + + boost::optional HeatPumpAirToWater_Impl::heatingLoop() const { + if (auto mode_ = heatingOperationMode()) { + return mode_->plantLoop(); + } + return boost::none; + } + + std::vector HeatPumpAirToWater_Impl::emsActuatorNames() const { + std::vector actuators{{"HeatPump:AirToWater", "Operating Mode"}}; + if (auto mode_ = heatingOperationMode()) { + actuators.emplace_back("HeatPump:AirToWater", "Defrost Flag"); + actuators.emplace_back("HeatPump:AirToWater", "Entering Water Temperature"); + actuators.emplace_back("HeatPump:AirToWater", "Leaving Water Temperature"); + } + return actuators; + } + + std::vector HeatPumpAirToWater_Impl::emsInternalVariableNames() const { + std::vector types; + return types; + } + + } // namespace detail + + HeatPumpAirToWater::HeatPumpAirToWater(const Model& model) : StraightComponent(HeatPumpAirToWater::iddObjectType(), model) { + OS_ASSERT(getImpl()); + + // IDD defaults + bool ok = true; + ok &= setOperatingModeControlMethod("Load"); + ok &= setOperatingModeControlOptionforMultipleUnit("SingleMode"); + ok &= setMinimumPartLoadRatio(0.0); + ok &= setMaximumOutdoorDryBulbTemperatureForDefrostOperation(10.0); + ok &= setHeatPumpDefrostControl("None"); + ok &= setHeatPumpDefrostTimePeriodFraction(0.058333); + ok &= setResistiveDefrostHeaterCapacity(0.0); + ok &= setHeatPumpMultiplier(1); + ok &= setControlType("VariableSpeed"); + ok &= setCrankcaseHeaterCapacity(0.0); + ok &= setMaximumAmbientTemperatureforCrankcaseHeaterOperation(10.0); + OS_ASSERT(ok); + } + + IddObjectType HeatPumpAirToWater::iddObjectType() { + return {IddObjectType::OS_HeatPump_AirToWater}; + } + + std::vector HeatPumpAirToWater::operatingModeControlMethodValues() { + return getIddKeyNames(IddFactory::instance().getObject(iddObjectType()).get(), OS_HeatPump_AirToWaterFields::OperatingModeControlMethod); + } + + std::vector HeatPumpAirToWater::operatingModeControlOptionforMultipleUnitValues() { + return getIddKeyNames(IddFactory::instance().getObject(iddObjectType()).get(), + OS_HeatPump_AirToWaterFields::OperatingModeControlOptionforMultipleUnit); + } + + std::vector HeatPumpAirToWater::heatPumpDefrostControlValues() { + return getIddKeyNames(IddFactory::instance().getObject(iddObjectType()).get(), OS_HeatPump_AirToWaterFields::HeatPumpDefrostControl); + } + + std::vector HeatPumpAirToWater::controlTypeValues() { + return getIddKeyNames(IddFactory::instance().getObject(iddObjectType()).get(), OS_HeatPump_AirToWaterFields::ControlType); + } + + std::string HeatPumpAirToWater::operatingModeControlMethod() const { + return getImpl()->operatingModeControlMethod(); + } + + std::string HeatPumpAirToWater::operatingModeControlOptionforMultipleUnit() const { + return getImpl()->operatingModeControlOptionforMultipleUnit(); + } + + boost::optional HeatPumpAirToWater::operatingModeControlSchedule() const { + return getImpl()->operatingModeControlSchedule(); + } + + double HeatPumpAirToWater::minimumPartLoadRatio() const { + return getImpl()->minimumPartLoadRatio(); + } + + boost::optional HeatPumpAirToWater::airInletNodeName() const { + return getImpl()->airInletNodeName(); + } + + boost::optional HeatPumpAirToWater::airOutletNodeName() const { + return getImpl()->airOutletNodeName(); + } + + double HeatPumpAirToWater::maximumOutdoorDryBulbTemperatureForDefrostOperation() const { + return getImpl()->maximumOutdoorDryBulbTemperatureForDefrostOperation(); + } + + std::string HeatPumpAirToWater::heatPumpDefrostControl() const { + return getImpl()->heatPumpDefrostControl(); + } + + double HeatPumpAirToWater::heatPumpDefrostTimePeriodFraction() const { + return getImpl()->heatPumpDefrostTimePeriodFraction(); + } + + double HeatPumpAirToWater::resistiveDefrostHeaterCapacity() const { + return getImpl()->resistiveDefrostHeaterCapacity(); + } + + boost::optional HeatPumpAirToWater::defrostEnergyInputRatioFunctionofTemperatureCurve() const { + return getImpl()->defrostEnergyInputRatioFunctionofTemperatureCurve(); + } + + int HeatPumpAirToWater::heatPumpMultiplier() const { + return getImpl()->heatPumpMultiplier(); + } + + std::string HeatPumpAirToWater::controlType() const { + return getImpl()->controlType(); + } + + double HeatPumpAirToWater::crankcaseHeaterCapacity() const { + return getImpl()->crankcaseHeaterCapacity(); + } + + boost::optional HeatPumpAirToWater::crankcaseHeaterCapacityFunctionofTemperatureCurve() const { + return getImpl()->crankcaseHeaterCapacityFunctionofTemperatureCurve(); + } + + double HeatPumpAirToWater::maximumAmbientTemperatureforCrankcaseHeaterOperation() const { + return getImpl()->maximumAmbientTemperatureforCrankcaseHeaterOperation(); + } + + boost::optional HeatPumpAirToWater::heatingOperationMode() const { + return getImpl()->heatingOperationMode(); + } + + boost::optional HeatPumpAirToWater::coolingOperationMode() const { + return getImpl()->coolingOperationMode(); + } + + bool HeatPumpAirToWater::setOperatingModeControlMethod(const std::string& operatingModeControlMethod) { + return getImpl()->setOperatingModeControlMethod(operatingModeControlMethod); + } + + bool HeatPumpAirToWater::setOperatingModeControlOptionforMultipleUnit(const std::string& operatingModeControlOptionforMultipleUnit) { + return getImpl()->setOperatingModeControlOptionforMultipleUnit(operatingModeControlOptionforMultipleUnit); + } + + bool HeatPumpAirToWater::setOperatingModeControlSchedule(Schedule& operatingModeControlSchedule) { + return getImpl()->setOperatingModeControlSchedule(operatingModeControlSchedule); + } + + void HeatPumpAirToWater::resetOperatingModeControlSchedule() { + getImpl()->resetOperatingModeControlSchedule(); + } + + bool HeatPumpAirToWater::setMinimumPartLoadRatio(double minimumPartLoadRatio) { + return getImpl()->setMinimumPartLoadRatio(minimumPartLoadRatio); + } + + bool HeatPumpAirToWater::setAirInletNodeName(const std::string& airInletNodeName) { + return getImpl()->setAirInletNodeName(airInletNodeName); + } + + void HeatPumpAirToWater::resetAirInletNodeName() { + getImpl()->resetAirInletNodeName(); + } + + bool HeatPumpAirToWater::setAirOutletNodeName(const std::string& airOutletNodeName) { + return getImpl()->setAirOutletNodeName(airOutletNodeName); + } + + void HeatPumpAirToWater::resetAirOutletNodeName() { + getImpl()->resetAirOutletNodeName(); + } + + bool HeatPumpAirToWater::setMaximumOutdoorDryBulbTemperatureForDefrostOperation(double maximumOutdoorDryBulbTemperatureForDefrostOperation) { + return getImpl()->setMaximumOutdoorDryBulbTemperatureForDefrostOperation( + maximumOutdoorDryBulbTemperatureForDefrostOperation); + } + + bool HeatPumpAirToWater::setHeatPumpDefrostControl(const std::string& heatPumpDefrostControl) { + return getImpl()->setHeatPumpDefrostControl(heatPumpDefrostControl); + } + + bool HeatPumpAirToWater::setHeatPumpDefrostTimePeriodFraction(double heatPumpDefrostTimePeriodFraction) { + return getImpl()->setHeatPumpDefrostTimePeriodFraction(heatPumpDefrostTimePeriodFraction); + } + + bool HeatPumpAirToWater::setResistiveDefrostHeaterCapacity(double resistiveDefrostHeaterCapacity) { + return getImpl()->setResistiveDefrostHeaterCapacity(resistiveDefrostHeaterCapacity); + } + + bool HeatPumpAirToWater::setDefrostEnergyInputRatioFunctionofTemperatureCurve(const Curve& defrostEnergyInputRatioFunctionofTemperatureCurve) { + return getImpl()->setDefrostEnergyInputRatioFunctionofTemperatureCurve( + defrostEnergyInputRatioFunctionofTemperatureCurve); + } + + void HeatPumpAirToWater::resetDefrostEnergyInputRatioFunctionofTemperatureCurve() { + getImpl()->resetDefrostEnergyInputRatioFunctionofTemperatureCurve(); + } + + bool HeatPumpAirToWater::setHeatPumpMultiplier(int heatPumpMultiplier) { + return getImpl()->setHeatPumpMultiplier(heatPumpMultiplier); + } + + bool HeatPumpAirToWater::setControlType(const std::string& controlType) { + return getImpl()->setControlType(controlType); + } + + bool HeatPumpAirToWater::setCrankcaseHeaterCapacity(double crankcaseHeaterCapacity) { + return getImpl()->setCrankcaseHeaterCapacity(crankcaseHeaterCapacity); + } + + bool HeatPumpAirToWater::setCrankcaseHeaterCapacityFunctionofTemperatureCurve(const Curve& crankcaseHeaterCapacityFunctionofTemperatureCurve) { + return getImpl()->setCrankcaseHeaterCapacityFunctionofTemperatureCurve( + crankcaseHeaterCapacityFunctionofTemperatureCurve); + } + + void HeatPumpAirToWater::resetCrankcaseHeaterCapacityFunctionofTemperatureCurve() { + getImpl()->resetCrankcaseHeaterCapacityFunctionofTemperatureCurve(); + } + + bool HeatPumpAirToWater::setMaximumAmbientTemperatureforCrankcaseHeaterOperation(double maximumAmbientTemperatureforCrankcaseHeaterOperation) { + return getImpl()->setMaximumAmbientTemperatureforCrankcaseHeaterOperation( + maximumAmbientTemperatureforCrankcaseHeaterOperation); + } + + bool HeatPumpAirToWater::setHeatingOperationMode(const HeatPumpAirToWaterHeating& heatingOperationMode) { + return getImpl()->setHeatingOperationMode(heatingOperationMode); + } + + void HeatPumpAirToWater::resetHeatingOperationMode() { + getImpl()->resetHeatingOperationMode(); + } + + bool HeatPumpAirToWater::setCoolingOperationMode(const HeatPumpAirToWaterCooling& coolingOperationMode) { + return getImpl()->setCoolingOperationMode(coolingOperationMode); + } + + void HeatPumpAirToWater::resetCoolingOperationMode() { + getImpl()->resetCoolingOperationMode(); + } + + boost::optional HeatPumpAirToWater::coolingLoop() const { + return getImpl()->coolingLoop(); + } + + boost::optional HeatPumpAirToWater::heatingLoop() const { + return getImpl()->heatingLoop(); + } + + boost::optional HeatPumpAirToWater::autosizedRatedAirFlowRateinCoolingMode() const { + return getImpl()->autosizedRatedAirFlowRateinCoolingMode(); + } + + boost::optional HeatPumpAirToWater::autosizedRatedWaterFlowRateinCoolingMode() const { + return getImpl()->autosizedRatedWaterFlowRateinCoolingMode(); + } + + boost::optional HeatPumpAirToWater::autosizedRatedAirFlowRateinHeatingMode() const { + return getImpl()->autosizedRatedAirFlowRateinHeatingMode(); + } + + boost::optional HeatPumpAirToWater::autosizedRatedWaterFlowRateinHeatingMode() const { + return getImpl()->autosizedRatedWaterFlowRateinHeatingMode(); + } + + boost::optional HeatPumpAirToWater::autosizedRatedHeatingCapacity() const { + return getImpl()->autosizedRatedHeatingCapacity(); + } + + boost::optional HeatPumpAirToWater::autosizedRatedCoolingCapacity() const { + return getImpl()->autosizedRatedCoolingCapacity(); + } + + /// @cond + HeatPumpAirToWater::HeatPumpAirToWater(std::shared_ptr impl) : StraightComponent(std::move(impl)) {} + /// @endcond + +} // namespace model +} // namespace openstudio diff --git a/src/model/HeatPumpAirToWater.hpp b/src/model/HeatPumpAirToWater.hpp new file mode 100644 index 0000000000..2e7fe7df8d --- /dev/null +++ b/src/model/HeatPumpAirToWater.hpp @@ -0,0 +1,192 @@ +/*********************************************************************************************************************** +* OpenStudio(R), Copyright (c) Alliance for Sustainable Energy, LLC. +* See also https://openstudio.net/license +***********************************************************************************************************************/ + +#ifndef MODEL_HEATPUMPAIRTOWATER_HPP +#define MODEL_HEATPUMPAIRTOWATER_HPP + +#include "ModelAPI.hpp" +#include "StraightComponent.hpp" + +namespace openstudio { + +namespace model { + + class Schedule; + class Curve; + class HeatPumpAirToWaterHeating; + class HeatPumpAirToWaterCooling; + class PlantLoop; + + namespace detail { + + class HeatPumpAirToWater_Impl; + + } // namespace detail + + /** HeatPumpAirToWater is a StraightComponent that wraps the OpenStudio IDD object 'OS:HeatPump:AirToWater'. */ + class MODEL_API HeatPumpAirToWater : public StraightComponent + { + public: + /** @name Constructors and Destructors */ + //@{ + + explicit HeatPumpAirToWater(const Model& model); + + virtual ~HeatPumpAirToWater() = default; + // Default the copy and move operators because the virtual dtor is explicit + HeatPumpAirToWater(const HeatPumpAirToWater& other) = default; + HeatPumpAirToWater(HeatPumpAirToWater&& other) = default; + HeatPumpAirToWater& operator=(const HeatPumpAirToWater&) = default; + HeatPumpAirToWater& operator=(HeatPumpAirToWater&&) = default; + + //@} + + static IddObjectType iddObjectType(); + + static std::vector operatingModeControlMethodValues(); + + static std::vector operatingModeControlOptionforMultipleUnitValues(); + + static std::vector heatPumpDefrostControlValues(); + + static std::vector controlTypeValues(); + + /** @name Getters */ + //@{ + + std::string operatingModeControlMethod() const; + + std::string operatingModeControlOptionforMultipleUnit() const; + + boost::optional operatingModeControlSchedule() const; + + double minimumPartLoadRatio() const; + + boost::optional airInletNodeName() const; + + boost::optional airOutletNodeName() const; + + double maximumOutdoorDryBulbTemperatureForDefrostOperation() const; + + std::string heatPumpDefrostControl() const; + + double heatPumpDefrostTimePeriodFraction() const; + + double resistiveDefrostHeaterCapacity() const; + + boost::optional defrostEnergyInputRatioFunctionofTemperatureCurve() const; + + int heatPumpMultiplier() const; + + std::string controlType() const; + + double crankcaseHeaterCapacity() const; + + boost::optional crankcaseHeaterCapacityFunctionofTemperatureCurve() const; + + double maximumAmbientTemperatureforCrankcaseHeaterOperation() const; + + boost::optional heatingOperationMode() const; + + boost::optional coolingOperationMode() const; + + //@} + /** @name Setters */ + //@{ + + bool setOperatingModeControlMethod(const std::string& operatingModeControlMethod); + + bool setOperatingModeControlOptionforMultipleUnit(const std::string& operatingModeControlOptionforMultipleUnit); + + bool setOperatingModeControlSchedule(Schedule& operatingModeControlSchedule); + + void resetOperatingModeControlSchedule(); + + bool setMinimumPartLoadRatio(double minimumPartLoadRatio); + + bool setAirInletNodeName(const std::string& airInletNodeName); + + void resetAirInletNodeName(); + + bool setAirOutletNodeName(const std::string& airOutletNodeName); + + void resetAirOutletNodeName(); + + bool setMaximumOutdoorDryBulbTemperatureForDefrostOperation(double maximumOutdoorDryBulbTemperatureForDefrostOperation); + + bool setHeatPumpDefrostControl(const std::string& heatPumpDefrostControl); + + bool setHeatPumpDefrostTimePeriodFraction(double heatPumpDefrostTimePeriodFraction); + + bool setResistiveDefrostHeaterCapacity(double resistiveDefrostHeaterCapacity); + + bool setDefrostEnergyInputRatioFunctionofTemperatureCurve(const Curve& defrostEnergyInputRatioFunctionofTemperatureCurve); + + void resetDefrostEnergyInputRatioFunctionofTemperatureCurve(); + + bool setHeatPumpMultiplier(int heatPumpMultiplier); + + bool setControlType(const std::string& controlType); + + bool setCrankcaseHeaterCapacity(double crankcaseHeaterCapacity); + + bool setCrankcaseHeaterCapacityFunctionofTemperatureCurve(const Curve& crankcaseHeaterCapacityFunctionofTemperatureCurve); + + void resetCrankcaseHeaterCapacityFunctionofTemperatureCurve(); + + bool setMaximumAmbientTemperatureforCrankcaseHeaterOperation(double maximumAmbientTemperatureforCrankcaseHeaterOperation); + + bool setHeatingOperationMode(const HeatPumpAirToWaterHeating& heatingOperationMode); + + void resetHeatingOperationMode(); + + bool setCoolingOperationMode(const HeatPumpAirToWaterCooling& coolingOperationMode); + + void resetCoolingOperationMode(); + + //@} + /** @name Other */ + //@{ + + // If there is a Cooling Operation Mode attached, it will lookup the PlantLoop it is connected to + boost::optional coolingLoop() const; + + // If there is a Heating Operation Mode attached, it will lookup the PlantLoop it is connected to + boost::optional heatingLoop() const; + + // Autosize methods + boost::optional autosizedRatedAirFlowRateinHeatingMode() const; + boost::optional autosizedRatedWaterFlowRateinHeatingMode() const; + boost::optional autosizedRatedAirFlowRateinCoolingMode() const; + boost::optional autosizedRatedWaterFlowRateinCoolingMode() const; + boost::optional autosizedRatedHeatingCapacity() const; + boost::optional autosizedRatedCoolingCapacity() const; + + //@} + protected: + /// @cond + using ImplType = detail::HeatPumpAirToWater_Impl; + + explicit HeatPumpAirToWater(std::shared_ptr impl); + + friend class detail::HeatPumpAirToWater_Impl; + friend class Model; + friend class IdfObject; + friend class openstudio::detail::IdfObject_Impl; + /// @endcond + private: + REGISTER_LOGGER("openstudio.model.HeatPumpAirToWater"); + }; + + /** \relates HeatPumpAirToWater*/ + using OptionalHeatPumpAirToWater = boost::optional; + + /** \relates HeatPumpAirToWater*/ + using HeatPumpAirToWaterVector = std::vector; + +} // namespace model +} // namespace openstudio + +#endif // MODEL_HEATPUMPAIRTOWATER_HPP diff --git a/src/model/HeatPumpAirToWaterCooling.cpp b/src/model/HeatPumpAirToWaterCooling.cpp new file mode 100644 index 0000000000..0c8a6ef111 --- /dev/null +++ b/src/model/HeatPumpAirToWaterCooling.cpp @@ -0,0 +1,746 @@ +/*********************************************************************************************************************** +* OpenStudio(R), Copyright (c) Alliance for Sustainable Energy, LLC. +* See also https://openstudio.net/license +***********************************************************************************************************************/ + +#include "HeatPumpAirToWaterCooling.hpp" +#include "HeatPumpAirToWaterCooling_Impl.hpp" + +#include "HeatPumpAirToWater.hpp" +#include "HeatPumpAirToWater_Impl.hpp" +#include "HeatPumpAirToWaterCoolingSpeedData.hpp" +#include "HeatPumpAirToWaterCoolingSpeedData_Impl.hpp" + +#include "Schedule.hpp" +#include "Schedule_Impl.hpp" +#include "Curve.hpp" +#include "Curve_Impl.hpp" +#include "ModelObjectList.hpp" +#include "ModelObjectList_Impl.hpp" + +#include "ScheduleTypeLimits.hpp" +#include "ScheduleTypeRegistry.hpp" + +#include "PlantLoop.hpp" +#include "PlantLoop_Impl.hpp" +#include "Node.hpp" + +// Need for clone override +#include "Model.hpp" +#include "Model_Impl.hpp" + +#include "../utilities/core/Assert.hpp" +#include "../utilities/core/ContainersMove.hpp" +#include "../utilities/data/DataEnums.hpp" + +#include +#include + +namespace openstudio { +namespace model { + + namespace detail { + + HeatPumpAirToWaterCooling_Impl::HeatPumpAirToWaterCooling_Impl(const IdfObject& idfObject, Model_Impl* model, bool keepHandle) + : StraightComponent_Impl(idfObject, model, keepHandle) { + OS_ASSERT(idfObject.iddObject().type() == HeatPumpAirToWaterCooling::iddObjectType()); + } + + HeatPumpAirToWaterCooling_Impl::HeatPumpAirToWaterCooling_Impl(const openstudio::detail::WorkspaceObject_Impl& other, Model_Impl* model, + bool keepHandle) + : StraightComponent_Impl(other, model, keepHandle) { + OS_ASSERT(other.iddObject().type() == HeatPumpAirToWaterCooling::iddObjectType()); + } + + HeatPumpAirToWaterCooling_Impl::HeatPumpAirToWaterCooling_Impl(const HeatPumpAirToWaterCooling_Impl& other, Model_Impl* model, bool keepHandle) + : StraightComponent_Impl(other, model, keepHandle) {} + + const std::vector& HeatPumpAirToWaterCooling_Impl::outputVariableNames() const { + static std::vector result; + if (result.empty()) { + } + return result; + } + + std::vector HeatPumpAirToWaterCooling_Impl::getScheduleTypeKeys(const Schedule& schedule) const { + std::vector result; + const UnsignedVector fieldIndices = getSourceIndices(schedule.handle()); + if (std::find(fieldIndices.cbegin(), fieldIndices.cend(), OS_HeatPump_AirToWater_CoolingFields::AvailabilityScheduleName) + != fieldIndices.cend()) { + result.emplace_back("HeatPumpAirToWaterCooling", "Availability"); + } + return result; + } + + IddObjectType HeatPumpAirToWaterCooling_Impl::iddObjectType() const { + return HeatPumpAirToWaterCooling::iddObjectType(); + } + + unsigned HeatPumpAirToWaterCooling_Impl::inletPort() const { + return OS_HeatPump_AirToWater_CoolingFields::ChilledWaterInletNodeName; + } + + unsigned HeatPumpAirToWaterCooling_Impl::outletPort() const { + return OS_HeatPump_AirToWater_CoolingFields::ChilledWaterOutletNodeName; + } + + bool HeatPumpAirToWaterCooling_Impl::addToNode(Node& node) { + if (boost::optional plant = node.plantLoop()) { + if (plant->supplyComponent(node.handle())) { + return StraightComponent_Impl::addToNode(node); + } + } + + return false; + } + + boost::optional HeatPumpAirToWaterCooling_Impl::containingHVACComponent() const { + auto awhp_ = heatPumpAirToWater(); + if (awhp_) { + return std::move(*awhp_); + } + return boost::none; + } + + boost::optional HeatPumpAirToWaterCooling_Impl::heatPumpAirToWater() const { + auto awhps = getObject().getModelObjectSources(HeatPumpAirToWater::iddObjectType()); + if (awhps.empty()) { + return boost::none; + } + if (awhps.size() > 1) { + LOG(Error, briefDescription() << " is referenced by more than one HeatPumpAirToWater, returning the first"); + } + return awhps[0]; + } + + ModelObject HeatPumpAirToWaterCooling_Impl::clone(Model model) const { + const bool same_model = this->model() == model; + // This handles resetting the ports, and bypassing ParentObject::clone so it doesn't clone children + auto t_clone = StraightComponent_Impl::clone(model).cast(); + + // Make a clean list and repopulate it, without cloning the SpeedData objects + auto newSpeedList = ModelObjectList(model); + newSpeedList.setName(t_clone.nameString() + " Speed Data List"); + bool ok = t_clone.getImpl()->setSpeedDataList(newSpeedList); + OS_ASSERT(ok); + + for (const auto& speed : speeds()) { + if (same_model) { + ok = t_clone.addSpeed(speed); + } else { + auto speedClone = speed.clone(model).cast(); + ok = t_clone.addSpeed(speedClone); + } + OS_ASSERT(ok); + } + + return std::move(t_clone); + } + + std::vector HeatPumpAirToWaterCooling_Impl::children() const { + std::vector children; + if (auto c_ = minimumLeavingWaterTemperatureCurve()) { + children.emplace_back(std::move(*c_)); + } + if (auto c_ = maximumLeavingWaterTemperatureCurve()) { + children.emplace_back(std::move(*c_)); + } + if (auto const speedDataList_ = optionalSpeedDataList()) { + for (const auto& mo : speedDataList_->modelObjects()) { + children.push_back(mo); + } + } + return children; + } + + // std::vector allowableChildTypes() const override; + + std::vector HeatPumpAirToWaterCooling_Impl::remove() { + // ModelObjectList is kinda dumb, it removes all the modelObjects it has, which we don't want + // We listed the SpeedData (ResourceObjects) in children, so it should be handled by ParentObject::remove + auto speedList_ = optionalSpeedDataList(); + std::vector result = StraightComponent_Impl::remove(); + if (!result.empty() && speedList_) { + speedList_->clearExtensibleGroups(); // Clearer than removeAllModelObjects (which does the same thing, but could mean delete all objects) + openstudio::detail::concat_helper(result, speedList_->remove()); + } + + return result; + } + + Schedule HeatPumpAirToWaterCooling_Impl::availabilitySchedule() const { + boost::optional value = optionalAvailabilitySchedule(); + if (!value) { + LOG_AND_THROW(briefDescription() << " does not have an Availability Schedule attached."); + } + return value.get(); + } + + double HeatPumpAirToWaterCooling_Impl::ratedInletAirTemperature() const { + boost::optional value = getDouble(OS_HeatPump_AirToWater_CoolingFields::RatedInletAirTemperature, true); + OS_ASSERT(value); + return value.get(); + } + + boost::optional HeatPumpAirToWaterCooling_Impl::ratedAirFlowRate() const { + return getDouble(OS_HeatPump_AirToWater_CoolingFields::RatedAirFlowRate, true); + } + + bool HeatPumpAirToWaterCooling_Impl::isRatedAirFlowRateAutosized() const { + bool result = false; + boost::optional value = getString(OS_HeatPump_AirToWater_CoolingFields::RatedAirFlowRate, true); + if (value) { + result = openstudio::istringEqual(value.get(), "autosize"); + } + return result; + } + + double HeatPumpAirToWaterCooling_Impl::ratedLeavingWaterTemperature() const { + boost::optional value = getDouble(OS_HeatPump_AirToWater_CoolingFields::RatedLeavingWaterTemperature, true); + OS_ASSERT(value); + return value.get(); + } + + boost::optional HeatPumpAirToWaterCooling_Impl::ratedWaterFlowRate() const { + return getDouble(OS_HeatPump_AirToWater_CoolingFields::RatedWaterFlowRate, true); + } + + bool HeatPumpAirToWaterCooling_Impl::isRatedWaterFlowRateAutosized() const { + bool result = false; + boost::optional value = getString(OS_HeatPump_AirToWater_CoolingFields::RatedWaterFlowRate, true); + if (value) { + result = openstudio::istringEqual(value.get(), "autosize"); + } + return result; + } + + double HeatPumpAirToWaterCooling_Impl::minimumOutdoorAirTemperature() const { + boost::optional value = getDouble(OS_HeatPump_AirToWater_CoolingFields::MinimumOutdoorAirTemperature, true); + OS_ASSERT(value); + return value.get(); + } + + double HeatPumpAirToWaterCooling_Impl::maximumOutdoorAirTemperature() const { + boost::optional value = getDouble(OS_HeatPump_AirToWater_CoolingFields::MaximumOutdoorAirTemperature, true); + OS_ASSERT(value); + return value.get(); + } + + boost::optional HeatPumpAirToWaterCooling_Impl::minimumLeavingWaterTemperatureCurve() const { + return getObject().getModelObjectTarget(OS_HeatPump_AirToWater_CoolingFields::MinimumLeavingWaterTemperatureCurveName); + } + + boost::optional HeatPumpAirToWaterCooling_Impl::maximumLeavingWaterTemperatureCurve() const { + return getObject().getModelObjectTarget(OS_HeatPump_AirToWater_CoolingFields::MaximumLeavingWaterTemperatureCurveName); + } + + double HeatPumpAirToWaterCooling_Impl::sizingFactor() const { + boost::optional value = getDouble(OS_HeatPump_AirToWater_CoolingFields::SizingFactor, true); + OS_ASSERT(value); + return value.get(); + } + + boost::optional HeatPumpAirToWaterCooling_Impl::boosterModeOnSpeed() const { + return getObject().getModelObjectTarget( + OS_HeatPump_AirToWater_CoolingFields::BoosterModeOnSpeed); + } + + bool HeatPumpAirToWaterCooling_Impl::setAvailabilitySchedule(Schedule& availabilitySchedule) { + const bool result = setSchedule(OS_HeatPump_AirToWater_CoolingFields::AvailabilityScheduleName, "HeatPumpAirToWaterCooling", "Availability", + availabilitySchedule); + return result; + } + + bool HeatPumpAirToWaterCooling_Impl::setRatedInletAirTemperature(double ratedInletAirTemperature) { + const bool result = setDouble(OS_HeatPump_AirToWater_CoolingFields::RatedInletAirTemperature, ratedInletAirTemperature); + OS_ASSERT(result); + return result; + } + + bool HeatPumpAirToWaterCooling_Impl::setRatedAirFlowRate(double ratedAirFlowRate) { + const bool result = setDouble(OS_HeatPump_AirToWater_CoolingFields::RatedAirFlowRate, ratedAirFlowRate); + return result; + } + + void HeatPumpAirToWaterCooling_Impl::autosizeRatedAirFlowRate() { + const bool result = setString(OS_HeatPump_AirToWater_CoolingFields::RatedAirFlowRate, "autosize"); + OS_ASSERT(result); + } + + bool HeatPumpAirToWaterCooling_Impl::setRatedLeavingWaterTemperature(double ratedLeavingWaterTemperature) { + const bool result = setDouble(OS_HeatPump_AirToWater_CoolingFields::RatedLeavingWaterTemperature, ratedLeavingWaterTemperature); + OS_ASSERT(result); + return result; + } + + bool HeatPumpAirToWaterCooling_Impl::setRatedWaterFlowRate(double ratedWaterFlowRate) { + const bool result = setDouble(OS_HeatPump_AirToWater_CoolingFields::RatedWaterFlowRate, ratedWaterFlowRate); + return result; + } + + void HeatPumpAirToWaterCooling_Impl::autosizeRatedWaterFlowRate() { + const bool result = setString(OS_HeatPump_AirToWater_CoolingFields::RatedWaterFlowRate, "autosize"); + OS_ASSERT(result); + } + + bool HeatPumpAirToWaterCooling_Impl::setMinimumOutdoorAirTemperature(double minimumOutdoorAirTemperature) { + const bool result = setDouble(OS_HeatPump_AirToWater_CoolingFields::MinimumOutdoorAirTemperature, minimumOutdoorAirTemperature); + OS_ASSERT(result); + return result; + } + + bool HeatPumpAirToWaterCooling_Impl::setMaximumOutdoorAirTemperature(double maximumOutdoorAirTemperature) { + const bool result = setDouble(OS_HeatPump_AirToWater_CoolingFields::MaximumOutdoorAirTemperature, maximumOutdoorAirTemperature); + OS_ASSERT(result); + return result; + } + + bool HeatPumpAirToWaterCooling_Impl::setMinimumLeavingWaterTemperatureCurve(const Curve& minimumLeavingWaterTemperatureCurve) { + const bool result = + setPointer(OS_HeatPump_AirToWater_CoolingFields::MinimumLeavingWaterTemperatureCurveName, minimumLeavingWaterTemperatureCurve.handle()); + return result; + } + + void HeatPumpAirToWaterCooling_Impl::resetMinimumLeavingWaterTemperatureCurve() { + const bool result = setString(OS_HeatPump_AirToWater_CoolingFields::MinimumLeavingWaterTemperatureCurveName, ""); + OS_ASSERT(result); + } + + bool HeatPumpAirToWaterCooling_Impl::setMaximumLeavingWaterTemperatureCurve(const Curve& maximumLeavingWaterTemperatureCurve) { + const bool result = + setPointer(OS_HeatPump_AirToWater_CoolingFields::MaximumLeavingWaterTemperatureCurveName, maximumLeavingWaterTemperatureCurve.handle()); + return result; + } + + void HeatPumpAirToWaterCooling_Impl::resetMaximumLeavingWaterTemperatureCurve() { + const bool result = setString(OS_HeatPump_AirToWater_CoolingFields::MaximumLeavingWaterTemperatureCurveName, ""); + OS_ASSERT(result); + } + + bool HeatPumpAirToWaterCooling_Impl::setSizingFactor(double sizingFactor) { + const bool result = setDouble(OS_HeatPump_AirToWater_CoolingFields::SizingFactor, sizingFactor); + return result; + } + + bool HeatPumpAirToWaterCooling_Impl::setBoosterModeOnSpeed(const HeatPumpAirToWaterCoolingSpeedData& boosterModeOnSpeed) { + const bool result = setPointer(OS_HeatPump_AirToWater_CoolingFields::BoosterModeOnSpeed, boosterModeOnSpeed.handle()); + return result; + } + + void HeatPumpAirToWaterCooling_Impl::resetBoosterModeOnSpeed() { + const bool result = setString(OS_HeatPump_AirToWater_CoolingFields::BoosterModeOnSpeed, ""); + OS_ASSERT(result); + } + + boost::optional HeatPumpAirToWaterCooling_Impl::autosizedRatedAirFlowRate() const { + boost::optional result; + if (auto awhp_ = heatPumpAirToWater()) { + result = awhp_->autosizedRatedAirFlowRateinCoolingMode(); + } + return result; + } + + boost::optional HeatPumpAirToWaterCooling_Impl::autosizedRatedWaterFlowRate() const { + boost::optional result; + if (auto awhp_ = heatPumpAirToWater()) { + result = awhp_->autosizedRatedWaterFlowRateinCoolingMode(); + } + return result; + } + + boost::optional HeatPumpAirToWaterCooling_Impl::autosizedRatedCoolingCapacity() const { + boost::optional result; + if (auto awhp_ = heatPumpAirToWater()) { + result = awhp_->autosizedRatedCoolingCapacity(); + } + return result; + } + + void HeatPumpAirToWaterCooling_Impl::autosize() { + autosizeRatedAirFlowRate(); + autosizeRatedWaterFlowRate(); + auto speeds = this->speeds(); + if (!speeds.empty()) { + speeds.back().autosize(); // Only need/can autosize the last (highest) speed + } + } + + void HeatPumpAirToWaterCooling_Impl::applySizingValues() { + if (boost::optional val_ = autosizedRatedAirFlowRate()) { + setRatedAirFlowRate(*val_); + } + + if (boost::optional val_ = autosizedRatedWaterFlowRate()) { + setRatedWaterFlowRate(*val_); + } + + if (boost::optional val_ = autosizedRatedCoolingCapacity()) { + auto speeds = this->speeds(); + if (!speeds.empty()) { + speeds.back().setRatedCoolingCapacity(*val_); // Only need/can autosize the last (highest) speed + } + } + } + + ComponentType HeatPumpAirToWaterCooling_Impl::componentType() const { + return ComponentType::Cooling; + } + + std::vector HeatPumpAirToWaterCooling_Impl::coolingFuelTypes() const { + return {FuelType::Electricity}; + } + + std::vector HeatPumpAirToWaterCooling_Impl::heatingFuelTypes() const { + return {}; + } + + std::vector HeatPumpAirToWaterCooling_Impl::appGHeatingFuelTypes() const { + return {}; + } + + boost::optional HeatPumpAirToWaterCooling_Impl::optionalAvailabilitySchedule() const { + return getObject().getModelObjectTarget(OS_HeatPump_AirToWater_CoolingFields::AvailabilityScheduleName); + } + + // Speed API + boost::optional HeatPumpAirToWaterCooling_Impl::optionalSpeedDataList() const { + return getObject().getModelObjectTarget(OS_HeatPump_AirToWater_CoolingFields::SpeedDataList); + } + + ModelObjectList HeatPumpAirToWaterCooling_Impl::speedDataList() const { + boost::optional value = optionalSpeedDataList(); + if (!value) { + LOG_AND_THROW(briefDescription() << " does not have a Speed Data List attached."); + } + return value.get(); + } + + bool HeatPumpAirToWaterCooling_Impl::setSpeedDataList(const ModelObjectList& speedDataList) { + const bool result = setPointer(OS_HeatPump_AirToWater_CoolingFields::SpeedDataList, speedDataList.handle()); + return result; + } + + void HeatPumpAirToWaterCooling_Impl::resetSpeedDataList() { + bool result = setString(OS_HeatPump_AirToWater_CoolingFields::SpeedDataList, ""); + OS_ASSERT(result); + } + + std::vector HeatPumpAirToWaterCooling_Impl::speeds() const { + std::vector result; + auto const modelObjects = speedDataList().modelObjects(); + + for (auto&& elem : modelObjects) { + auto modelObject = elem.optionalCast(); + if (modelObject) { + result.push_back(std::move(*modelObject)); + } + } + return result; + } + + unsigned HeatPumpAirToWaterCooling_Impl::numberOfSpeeds() const { + return speeds().size(); // This is right in all cases, but this is slow + // return speedDataList().size(); // This is fast, but could be wrong if the user put non-SpeedData objects in the list or if there are blanks + } + + boost::optional HeatPumpAirToWaterCooling_Impl::speedIndex(const HeatPumpAirToWaterCoolingSpeedData& speed) const { + const auto speedVector = speeds(); + auto it = std::find_if(speedVector.cbegin(), speedVector.cend(), [&](const HeatPumpAirToWaterCoolingSpeedData& s) { return s == speed; }); + if (it != speedVector.cend()) { + return static_cast(std::distance(speedVector.cbegin(), it)) + 1; // 1-indexed + } + return boost::none; + } + + bool HeatPumpAirToWaterCooling_Impl::addSpeed(const HeatPumpAirToWaterCoolingSpeedData& speed) { + if (numberOfSpeeds() >= HeatPumpAirToWaterCooling::maximum_number_of_speeds) { + LOG(Error, "You have reached the maximum number of speeds (=" << HeatPumpAirToWaterCooling::maximum_number_of_speeds << "), occurred for " + << briefDescription() << "."); + return false; // too many speeds + } + if (speedIndex(speed)) { + return false; // already in the list + } + + return speedDataList().addModelObject(speed); + } + + bool HeatPumpAirToWaterCooling_Impl::setSpeedIndex(const HeatPumpAirToWaterCoolingSpeedData& speed, unsigned index) { + auto cur_idx_ = speedIndex(speed); + if (!cur_idx_) { + LOG(Warn, "For " << briefDescription() << " cannot set the index of speed " << speed.briefDescription() << " since it is not part of it."); + return false; + } + + auto speedVector = speeds(); + + if (index < 1) { + LOG(Debug, "Requested a speed index of " << index << " < 1 to be assigned to " << speed.briefDescription() << ", resetting to 1"); + index = 1; + } else if (index > speedVector.size()) { + LOG(Debug, "Requested a speed index of " << index << " > number of speeds (" << speedVector.size() << ") to be assigned to " + << speed.briefDescription() << ", resetting to " << speedVector.size()); + index = speedVector.size(); + } + + speedVector.erase(speedVector.begin() + (*cur_idx_ - 1)); // stageIndex is 1-indexed, and vector is 0-indexed + speedVector.insert(speedVector.begin() + (index - 1), speed); // index is 1-indexed, and vector is 0-indexed + + return setSpeeds(speedVector); + } + + bool HeatPumpAirToWaterCooling_Impl::addSpeed(const HeatPumpAirToWaterCoolingSpeedData& speed, unsigned index) { + bool ok = addSpeed(speed); + if (!ok) { + return false; + } + ok = setSpeedIndex(speed, index); + return ok; + } + + bool HeatPumpAirToWaterCooling_Impl::setSpeeds(const std::vector& speeds) { + // Clear the extensible groups, and redo them + bool ok = true; + removeAllSpeeds(); + for (const auto& s : speeds) { + ok &= addSpeed(s); + } + return ok; + } + + bool HeatPumpAirToWaterCooling_Impl::removeSpeed(const HeatPumpAirToWaterCoolingSpeedData& speed) { // NOLINT(readability-*) + auto speedList = speedDataList(); + if (!speedList.hasModelObject(speed)) { + LOG(Warn, "For " << briefDescription() << " cannot remove speed " << speed.briefDescription() << " since it is not part of it."); + return false; + } + speedList.removeModelObject(speed); + return true; + } + + bool HeatPumpAirToWaterCooling_Impl::removeSpeed(unsigned index) { + bool result = false; + if (index < 1) { + return false; + } + auto speedVector = speeds(); + if (index <= speedVector.size()) { + result = removeSpeed(speedVector[index - 1]); // index is 1-indexed + } + return result; + } + + void HeatPumpAirToWaterCooling_Impl::removeAllSpeeds() { // NOLINT(readability-make-member-function-const) + speedDataList().removeAllModelObjects(); // This just clears the list, does not delete the underlying objects + } + + } // namespace detail + + HeatPumpAirToWaterCooling::HeatPumpAirToWaterCooling(const Model& model) : StraightComponent(HeatPumpAirToWaterCooling::iddObjectType(), model) { + OS_ASSERT(getImpl()); + + bool ok = true; + + auto speedList = ModelObjectList(model); + speedList.setName(this->nameString() + " Speed Data List"); + ok = getImpl()->setSpeedDataList(speedList); + OS_ASSERT(ok); + + auto availabilitySchedule = model.alwaysOnDiscreteSchedule(); + ok = setAvailabilitySchedule(availabilitySchedule); + OS_ASSERT(ok); + + // IDD Defaults + setRatedInletAirTemperature(8.0); + autosizeRatedAirFlowRate(); + setRatedLeavingWaterTemperature(40.0); + autosizeRatedWaterFlowRate(); + setMinimumOutdoorAirTemperature(-30.0); + setMaximumOutdoorAirTemperature(100.0); + setSizingFactor(1.0); + } + + IddObjectType HeatPumpAirToWaterCooling::iddObjectType() { + return {IddObjectType::OS_HeatPump_AirToWater_Cooling}; + } + + Schedule HeatPumpAirToWaterCooling::availabilitySchedule() const { + return getImpl()->availabilitySchedule(); + } + + double HeatPumpAirToWaterCooling::ratedInletAirTemperature() const { + return getImpl()->ratedInletAirTemperature(); + } + + boost::optional HeatPumpAirToWaterCooling::ratedAirFlowRate() const { + return getImpl()->ratedAirFlowRate(); + } + + bool HeatPumpAirToWaterCooling::isRatedAirFlowRateAutosized() const { + return getImpl()->isRatedAirFlowRateAutosized(); + } + + boost::optional HeatPumpAirToWaterCooling::autosizedRatedAirFlowRate() const { + return getImpl()->autosizedRatedAirFlowRate(); + } + + double HeatPumpAirToWaterCooling::ratedLeavingWaterTemperature() const { + return getImpl()->ratedLeavingWaterTemperature(); + } + + boost::optional HeatPumpAirToWaterCooling::ratedWaterFlowRate() const { + return getImpl()->ratedWaterFlowRate(); + } + + bool HeatPumpAirToWaterCooling::isRatedWaterFlowRateAutosized() const { + return getImpl()->isRatedWaterFlowRateAutosized(); + } + + boost::optional HeatPumpAirToWaterCooling::autosizedRatedWaterFlowRate() const { + return getImpl()->autosizedRatedWaterFlowRate(); + } + + double HeatPumpAirToWaterCooling::minimumOutdoorAirTemperature() const { + return getImpl()->minimumOutdoorAirTemperature(); + } + + double HeatPumpAirToWaterCooling::maximumOutdoorAirTemperature() const { + return getImpl()->maximumOutdoorAirTemperature(); + } + + boost::optional HeatPumpAirToWaterCooling::minimumLeavingWaterTemperatureCurve() const { + return getImpl()->minimumLeavingWaterTemperatureCurve(); + } + + boost::optional HeatPumpAirToWaterCooling::maximumLeavingWaterTemperatureCurve() const { + return getImpl()->maximumLeavingWaterTemperatureCurve(); + } + + double HeatPumpAirToWaterCooling::sizingFactor() const { + return getImpl()->sizingFactor(); + } + + boost::optional HeatPumpAirToWaterCooling::boosterModeOnSpeed() const { + return getImpl()->boosterModeOnSpeed(); + } + + bool HeatPumpAirToWaterCooling::setAvailabilitySchedule(Schedule& availabilitySchedule) { + return getImpl()->setAvailabilitySchedule(availabilitySchedule); + } + + bool HeatPumpAirToWaterCooling::setRatedInletAirTemperature(double ratedInletAirTemperature) { + return getImpl()->setRatedInletAirTemperature(ratedInletAirTemperature); + } + + bool HeatPumpAirToWaterCooling::setRatedAirFlowRate(double ratedAirFlowRate) { + return getImpl()->setRatedAirFlowRate(ratedAirFlowRate); + } + + void HeatPumpAirToWaterCooling::autosizeRatedAirFlowRate() { + getImpl()->autosizeRatedAirFlowRate(); + } + + bool HeatPumpAirToWaterCooling::setRatedLeavingWaterTemperature(double ratedLeavingWaterTemperature) { + return getImpl()->setRatedLeavingWaterTemperature(ratedLeavingWaterTemperature); + } + + bool HeatPumpAirToWaterCooling::setRatedWaterFlowRate(double ratedWaterFlowRate) { + return getImpl()->setRatedWaterFlowRate(ratedWaterFlowRate); + } + + void HeatPumpAirToWaterCooling::autosizeRatedWaterFlowRate() { + getImpl()->autosizeRatedWaterFlowRate(); + } + + bool HeatPumpAirToWaterCooling::setMinimumOutdoorAirTemperature(double minimumOutdoorAirTemperature) { + return getImpl()->setMinimumOutdoorAirTemperature(minimumOutdoorAirTemperature); + } + + bool HeatPumpAirToWaterCooling::setMaximumOutdoorAirTemperature(double maximumOutdoorAirTemperature) { + return getImpl()->setMaximumOutdoorAirTemperature(maximumOutdoorAirTemperature); + } + + bool HeatPumpAirToWaterCooling::setMinimumLeavingWaterTemperatureCurve(const Curve& minimumLeavingWaterTemperatureCurve) { + return getImpl()->setMinimumLeavingWaterTemperatureCurve(minimumLeavingWaterTemperatureCurve); + } + + void HeatPumpAirToWaterCooling::resetMinimumLeavingWaterTemperatureCurve() { + getImpl()->resetMinimumLeavingWaterTemperatureCurve(); + } + + bool HeatPumpAirToWaterCooling::setMaximumLeavingWaterTemperatureCurve(const Curve& maximumLeavingWaterTemperatureCurve) { + return getImpl()->setMaximumLeavingWaterTemperatureCurve(maximumLeavingWaterTemperatureCurve); + } + + void HeatPumpAirToWaterCooling::resetMaximumLeavingWaterTemperatureCurve() { + getImpl()->resetMaximumLeavingWaterTemperatureCurve(); + } + + bool HeatPumpAirToWaterCooling::setSizingFactor(double sizingFactor) { + return getImpl()->setSizingFactor(sizingFactor); + } + + bool HeatPumpAirToWaterCooling::setBoosterModeOnSpeed(const HeatPumpAirToWaterCoolingSpeedData& boosterModeOnSpeed) { + return getImpl()->setBoosterModeOnSpeed(boosterModeOnSpeed); + } + + void HeatPumpAirToWaterCooling::resetBoosterModeOnSpeed() { + getImpl()->resetBoosterModeOnSpeed(); + } + + // Speed Data API + unsigned HeatPumpAirToWaterCooling::numberOfSpeeds() const { + return getImpl()->numberOfSpeeds(); + } + + boost::optional HeatPumpAirToWaterCooling::speedIndex(const HeatPumpAirToWaterCoolingSpeedData& speed) const { + return getImpl()->speedIndex(speed); + } + + std::vector HeatPumpAirToWaterCooling::speeds() const { + return getImpl()->speeds(); + } + + bool HeatPumpAirToWaterCooling::addSpeed(const HeatPumpAirToWaterCoolingSpeedData& speed) { + return getImpl()->addSpeed(speed); + } + + bool HeatPumpAirToWaterCooling::addSpeed(const HeatPumpAirToWaterCoolingSpeedData& speed, unsigned index) { + return getImpl()->addSpeed(speed, index); + } + + bool HeatPumpAirToWaterCooling::setSpeedIndex(const HeatPumpAirToWaterCoolingSpeedData& speed, unsigned index) { + return getImpl()->setSpeedIndex(speed, index); + } + + bool HeatPumpAirToWaterCooling::setSpeeds(const std::vector& speeds) { + return getImpl()->setSpeeds(speeds); + } + + void HeatPumpAirToWaterCooling::removeAllSpeeds() { + getImpl()->removeAllSpeeds(); + } + + bool HeatPumpAirToWaterCooling::removeSpeed(const HeatPumpAirToWaterCoolingSpeedData& speed) { + return getImpl()->removeSpeed(speed); + } + + bool HeatPumpAirToWaterCooling::removeSpeed(unsigned index) { + return getImpl()->removeSpeed(index); + } + + boost::optional HeatPumpAirToWaterCooling::heatPumpAirToWater() const { + return getImpl()->heatPumpAirToWater(); + } + + boost::optional HeatPumpAirToWaterCooling::autosizedRatedCoolingCapacity() const { + return getImpl()->autosizedRatedCoolingCapacity(); + } + + /// @cond + HeatPumpAirToWaterCooling::HeatPumpAirToWaterCooling(std::shared_ptr impl) + : StraightComponent(std::move(impl)) {} + /// @endcond + +} // namespace model +} // namespace openstudio diff --git a/src/model/HeatPumpAirToWaterCooling.hpp b/src/model/HeatPumpAirToWaterCooling.hpp new file mode 100644 index 0000000000..c3a13270ee --- /dev/null +++ b/src/model/HeatPumpAirToWaterCooling.hpp @@ -0,0 +1,202 @@ +/*********************************************************************************************************************** +* OpenStudio(R), Copyright (c) Alliance for Sustainable Energy, LLC. +* See also https://openstudio.net/license +***********************************************************************************************************************/ + +#ifndef MODEL_HEATPUMPAIRTOWATERCOOLING_HPP +#define MODEL_HEATPUMPAIRTOWATERCOOLING_HPP + +#include "ModelAPI.hpp" +#include "StraightComponent.hpp" + +namespace openstudio { + +namespace model { + + class Curve; + class HeatPumpAirToWater; + class HeatPumpAirToWaterCoolingSpeedData; + class Schedule; + + namespace detail { + + class HeatPumpAirToWaterCooling_Impl; + + } // namespace detail + + /** HeatPumpAirToWaterCooling is a StraightComponent that wraps the OpenStudio IDD object 'OS:HeatPump:AirToWater:Cooling'. */ + class MODEL_API HeatPumpAirToWaterCooling : public StraightComponent + { + public: + /** @name Constructors and Destructors */ + //@{ + + explicit HeatPumpAirToWaterCooling(const Model& model); + + virtual ~HeatPumpAirToWaterCooling() = default; + // Default the copy and move operators because the virtual dtor is explicit + HeatPumpAirToWaterCooling(const HeatPumpAirToWaterCooling& other) = default; + HeatPumpAirToWaterCooling(HeatPumpAirToWaterCooling&& other) = default; + HeatPumpAirToWaterCooling& operator=(const HeatPumpAirToWaterCooling&) = default; + HeatPumpAirToWaterCooling& operator=(HeatPumpAirToWaterCooling&&) = default; + + //@} + + static IddObjectType iddObjectType(); + + static constexpr unsigned maximum_number_of_speeds = 5; + + /** @name Getters */ + //@{ + + Schedule availabilitySchedule() const; + + double ratedInletAirTemperature() const; + + boost::optional ratedAirFlowRate() const; + + bool isRatedAirFlowRateAutosized() const; + + double ratedLeavingWaterTemperature() const; + + boost::optional ratedWaterFlowRate() const; + + bool isRatedWaterFlowRateAutosized() const; + + double minimumOutdoorAirTemperature() const; + + double maximumOutdoorAirTemperature() const; + + boost::optional minimumLeavingWaterTemperatureCurve() const; + + boost::optional maximumLeavingWaterTemperatureCurve() const; + + double sizingFactor() const; + + boost::optional boosterModeOnSpeed() const; + + //@} + /** @name Setters */ + //@{ + + bool setAvailabilitySchedule(Schedule& availabilitySchedule); + + bool setRatedInletAirTemperature(double ratedInletAirTemperature); + + bool setRatedAirFlowRate(double ratedAirFlowRate); + + void autosizeRatedAirFlowRate(); + + bool setRatedLeavingWaterTemperature(double ratedLeavingWaterTemperature); + + bool setRatedWaterFlowRate(double ratedWaterFlowRate); + + void autosizeRatedWaterFlowRate(); + + bool setMinimumOutdoorAirTemperature(double minimumOutdoorAirTemperature); + + bool setMaximumOutdoorAirTemperature(double maximumOutdoorAirTemperature); + + bool setMinimumLeavingWaterTemperatureCurve(const Curve& minimumLeavingWaterTemperatureCurve); + + void resetMinimumLeavingWaterTemperatureCurve(); + + bool setMaximumLeavingWaterTemperatureCurve(const Curve& maximumLeavingWaterTemperatureCurve); + + void resetMaximumLeavingWaterTemperatureCurve(); + + bool setSizingFactor(double sizingFactor); + + bool setBoosterModeOnSpeed(const HeatPumpAirToWaterCoolingSpeedData& boosterModeOnSpeed); + + void resetBoosterModeOnSpeed(); + + //@} + /** @name Other */ + //@{ + + /** Return the performance data for each stage. **/ + std::vector speeds() const; + + unsigned numberOfSpeeds() const; + + /* + * Get the index of a given HeatPumpAirToWaterCoolingSpeedData (1-indexed) + */ + boost::optional speedIndex(const HeatPumpAirToWaterCoolingSpeedData& speed) const; + + /* + * Add a new speed after all of the existing speeds. + */ + bool addSpeed(const HeatPumpAirToWaterCoolingSpeedData& speed); + + /* + * Add a new HeatPumpAirToWaterCoolingSpeedData to the list which a given index (1 to x). + * Internally calls addSpeed then setSpeedIndex, see remarks there + */ + bool addSpeed(const HeatPumpAirToWaterCoolingSpeedData& speed, unsigned index); + + /* + * You can shuffle the priority of a given HeatPumpAirToWaterCoolingSpeedData after having added it + * If index is below 1, it's reset to 1. + * If index is greater than the number of speeds, will reset to last + */ + bool setSpeedIndex(const HeatPumpAirToWaterCoolingSpeedData& speed, unsigned index); + + /* + * Set all speeds using a list of HeatPumpAirToWaterCoolingSpeedDatas + * Internally calls addSpeed, and will return the global status, but will continue trying if there are problems + * (eg: if you make a vector larger than the number of accepted speeds, or a vector that has a speed from another model, the valid speeds will be + * added indeed, but it'll eventually return false) + */ + bool setSpeeds(const std::vector& speeds); + + /* + * Removes all HeatPumpAirToWaterCoolingSpeedDatas in this object + */ + void removeAllSpeeds(); + + /* + * Remove the given HeatPumpAirToWaterCoolingSpeedData from this object's speeds + */ + bool removeSpeed(const HeatPumpAirToWaterCoolingSpeedData& speed); + + /* + * Remove the HeatPumpAirToWaterCoolingSpeedData at the given index (1-indexed) + */ + bool removeSpeed(unsigned index); + + // Convenience function to return the HeatPumpAirToWater object that reference this cooling coil if any + boost::optional heatPumpAirToWater() const; + + // Autosize methods + boost::optional autosizedRatedAirFlowRate() const; + boost::optional autosizedRatedWaterFlowRate() const; + boost::optional autosizedRatedCoolingCapacity() const; // Convenience method + + //@} + protected: + /// @cond + using ImplType = detail::HeatPumpAirToWaterCooling_Impl; + + explicit HeatPumpAirToWaterCooling(std::shared_ptr impl); + + friend class detail::HeatPumpAirToWaterCooling_Impl; + friend class Model; + friend class IdfObject; + friend class openstudio::detail::IdfObject_Impl; + /// @endcond + private: + REGISTER_LOGGER("openstudio.model.HeatPumpAirToWaterCooling"); + }; + + /** \relates HeatPumpAirToWaterCooling*/ + using OptionalHeatPumpAirToWaterCooling = boost::optional; + + /** \relates HeatPumpAirToWaterCooling*/ + using HeatPumpAirToWaterCoolingVector = std::vector; + +} // namespace model +} // namespace openstudio + +#endif // MODEL_HEATPUMPAIRTOWATERCOOLING_HPP diff --git a/src/model/HeatPumpAirToWaterCoolingSpeedData.cpp b/src/model/HeatPumpAirToWaterCoolingSpeedData.cpp new file mode 100644 index 0000000000..e9ef9af702 --- /dev/null +++ b/src/model/HeatPumpAirToWaterCoolingSpeedData.cpp @@ -0,0 +1,410 @@ +/*********************************************************************************************************************** +* OpenStudio(R), Copyright (c) Alliance for Sustainable Energy, LLC. +* See also https://openstudio.net/license +***********************************************************************************************************************/ + +#include "HeatPumpAirToWaterCoolingSpeedData.hpp" +#include "HeatPumpAirToWaterCoolingSpeedData_Impl.hpp" + +#include "HeatPumpAirToWaterCooling.hpp" +#include "HeatPumpAirToWaterCooling_Impl.hpp" + +#include "Model.hpp" +#include "Model_Impl.hpp" +#include "Curve.hpp" +#include "Curve_Impl.hpp" +#include "CurveBiquadratic.hpp" +#include "CurveQuadratic.hpp" + +#include "../utilities/core/Assert.hpp" + +#include +#include + +#include + +namespace openstudio { +namespace model { + + namespace detail { + + HeatPumpAirToWaterCoolingSpeedData_Impl::HeatPumpAirToWaterCoolingSpeedData_Impl(const IdfObject& idfObject, Model_Impl* model, bool keepHandle) + : ResourceObject_Impl(idfObject, model, keepHandle) { + OS_ASSERT(idfObject.iddObject().type() == HeatPumpAirToWaterCoolingSpeedData::iddObjectType()); + } + + HeatPumpAirToWaterCoolingSpeedData_Impl::HeatPumpAirToWaterCoolingSpeedData_Impl(const openstudio::detail::WorkspaceObject_Impl& other, + Model_Impl* model, bool keepHandle) + : ResourceObject_Impl(other, model, keepHandle) { + OS_ASSERT(other.iddObject().type() == HeatPumpAirToWaterCoolingSpeedData::iddObjectType()); + } + + HeatPumpAirToWaterCoolingSpeedData_Impl::HeatPumpAirToWaterCoolingSpeedData_Impl(const HeatPumpAirToWaterCoolingSpeedData_Impl& other, + Model_Impl* model, bool keepHandle) + : ResourceObject_Impl(other, model, keepHandle) {} + + const std::vector& HeatPumpAirToWaterCoolingSpeedData_Impl::outputVariableNames() const { + static std::vector result; + if (result.empty()) { + } + return result; + } + + IddObjectType HeatPumpAirToWaterCoolingSpeedData_Impl::iddObjectType() const { + return HeatPumpAirToWaterCoolingSpeedData::iddObjectType(); + } + + std::vector HeatPumpAirToWaterCoolingSpeedData_Impl::children() const { + std::vector children; + if (auto c_ = optionalNormalizedCoolingCapacityFunctionofTemperatureCurve()) { + children.push_back(*c_); + } + if (auto c_ = optionalCoolingEnergyInputRatioFunctionofTemperatureCurve()) { + children.push_back(*c_); + } + if (auto c_ = optionalCoolingEnergyInputRatioFunctionofPLRCurve()) { + children.push_back(*c_); + } + + return children; + } + + ModelObject HeatPumpAirToWaterCoolingSpeedData_Impl::clone(Model model) const { + // Don't clone the curves (listed as children) + return ModelObject_Impl::clone(model); // NOLINT(bugprone-parent-virtual-call) + } + + boost::optional HeatPumpAirToWaterCoolingSpeedData_Impl::ratedCoolingCapacity() const { + return getDouble(OS_HeatPump_AirToWater_Cooling_SpeedDataFields::RatedCoolingCapacity, true); + } + + bool HeatPumpAirToWaterCoolingSpeedData_Impl::isRatedCoolingCapacityAutosized() const { + bool result = false; + boost::optional value = getString(OS_HeatPump_AirToWater_Cooling_SpeedDataFields::RatedCoolingCapacity, true); + if (value) { + result = openstudio::istringEqual(value.get(), "autosize"); + } + return result; + } + + boost::optional HeatPumpAirToWaterCoolingSpeedData_Impl::autosizedRatedCoolingCapacity() { + boost::optional result; + auto awhp_ccs = heatPumpAirToWaterCoolings(); + if (awhp_ccs.empty()) { + return result; + } + size_t n_found = 0; + for (const auto& awhp_cc : awhp_ccs) { + // Check needed because could be the booster speed + if (awhp_cc.speeds().empty()) { + continue; + } + // It has to be the last speed + if (awhp_cc.speeds().back().handle() != this->handle()) { + continue; + } + if (n_found == 0) { + // Setting the first one only + result = awhp_cc.autosizedRatedCoolingCapacity(); + } + ++n_found; + } + if (n_found > 1) { + LOG(Warn, briefDescription() << " is used as the highest speed in multiple HeatPumpAirToWaterCooling objects, " + "returning the autosized value from the first one only."); + } + + return result; + } + + double HeatPumpAirToWaterCoolingSpeedData_Impl::ratedCOPforCooling() const { + boost::optional value = getDouble(OS_HeatPump_AirToWater_Cooling_SpeedDataFields::RatedCOPforCooling, true); + OS_ASSERT(value); + return value.get(); + } + + Curve HeatPumpAirToWaterCoolingSpeedData_Impl::normalizedCoolingCapacityFunctionofTemperatureCurve() const { + boost::optional value = optionalNormalizedCoolingCapacityFunctionofTemperatureCurve(); + if (!value) { + LOG_AND_THROW(briefDescription() << " does not have an Normalized Cooling Capacity Functionof Temperature Curve attached."); + } + return value.get(); + } + + Curve HeatPumpAirToWaterCoolingSpeedData_Impl::coolingEnergyInputRatioFunctionofTemperatureCurve() const { + boost::optional value = optionalCoolingEnergyInputRatioFunctionofTemperatureCurve(); + if (!value) { + LOG_AND_THROW(briefDescription() << " does not have an Cooling Energy Input Ratio Functionof Temperature Curve attached."); + } + return value.get(); + } + + Curve HeatPumpAirToWaterCoolingSpeedData_Impl::coolingEnergyInputRatioFunctionofPLRCurve() const { + boost::optional value = optionalCoolingEnergyInputRatioFunctionofPLRCurve(); + if (!value) { + LOG_AND_THROW(briefDescription() << " does not have an Cooling Energy Input Ratio Functionof PLRCurve attached."); + } + return value.get(); + } + + bool HeatPumpAirToWaterCoolingSpeedData_Impl::setRatedCoolingCapacity(double ratedCoolingCapacity) { + const bool result = setDouble(OS_HeatPump_AirToWater_Cooling_SpeedDataFields::RatedCoolingCapacity, ratedCoolingCapacity); + return result; + } + + void HeatPumpAirToWaterCoolingSpeedData_Impl::autosizeRatedCoolingCapacity() { + const bool result = setString(OS_HeatPump_AirToWater_Cooling_SpeedDataFields::RatedCoolingCapacity, "autosize"); + OS_ASSERT(result); + } + + bool HeatPumpAirToWaterCoolingSpeedData_Impl::setRatedCOPforCooling(double ratedCOPforCooling) { + const bool result = setDouble(OS_HeatPump_AirToWater_Cooling_SpeedDataFields::RatedCOPforCooling, ratedCOPforCooling); + return result; + } + + bool HeatPumpAirToWaterCoolingSpeedData_Impl::setNormalizedCoolingCapacityFunctionofTemperatureCurve(const Curve& bivariateFunctions) { + const bool result = setPointer(OS_HeatPump_AirToWater_Cooling_SpeedDataFields::NormalizedCoolingCapacityFunctionofTemperatureCurveName, + bivariateFunctions.handle()); + return result; + } + + bool HeatPumpAirToWaterCoolingSpeedData_Impl::setCoolingEnergyInputRatioFunctionofTemperatureCurve(const Curve& bivariateFunctions) { + const bool result = setPointer(OS_HeatPump_AirToWater_Cooling_SpeedDataFields::CoolingEnergyInputRatioFunctionofTemperatureCurveName, + bivariateFunctions.handle()); + return result; + } + + bool HeatPumpAirToWaterCoolingSpeedData_Impl::setCoolingEnergyInputRatioFunctionofPLRCurve(const Curve& univariateFunctions) { + const bool result = + setPointer(OS_HeatPump_AirToWater_Cooling_SpeedDataFields::CoolingEnergyInputRatioFunctionofPLRCurveName, univariateFunctions.handle()); + return result; + } + + void HeatPumpAirToWaterCoolingSpeedData_Impl::autosize() { + autosizeRatedCoolingCapacity(); + } + + void HeatPumpAirToWaterCoolingSpeedData_Impl::applySizingValues() { + if (boost::optional val_ = autosizedRatedCoolingCapacity()) { + setRatedCoolingCapacity(*val_); + } + } + + boost::optional HeatPumpAirToWaterCoolingSpeedData_Impl::optionalNormalizedCoolingCapacityFunctionofTemperatureCurve() const { + return getObject().getModelObjectTarget( + OS_HeatPump_AirToWater_Cooling_SpeedDataFields::NormalizedCoolingCapacityFunctionofTemperatureCurveName); + } + + boost::optional HeatPumpAirToWaterCoolingSpeedData_Impl::optionalCoolingEnergyInputRatioFunctionofTemperatureCurve() const { + return getObject().getModelObjectTarget( + OS_HeatPump_AirToWater_Cooling_SpeedDataFields::CoolingEnergyInputRatioFunctionofTemperatureCurveName); + } + + boost::optional HeatPumpAirToWaterCoolingSpeedData_Impl::optionalCoolingEnergyInputRatioFunctionofPLRCurve() const { + return getObject().getModelObjectTarget( + OS_HeatPump_AirToWater_Cooling_SpeedDataFields::CoolingEnergyInputRatioFunctionofPLRCurveName); + } + + std::vector HeatPumpAirToWaterCoolingSpeedData_Impl::heatPumpAirToWaterCoolings() const { + std::vector result; + + const auto handle = this->handle(); + + for (const auto& awhp : model().getConcreteModelObjects()) { + if (awhp.boosterModeOnSpeed() && awhp.boosterModeOnSpeed()->handle() == handle) { + result.push_back(awhp); + } else { + const auto speeds = awhp.speeds(); + if (std::find_if(speeds.cbegin(), speeds.cend(), + [handle](const HeatPumpAirToWaterCoolingSpeedData& speed) { return speed.handle() == handle; }) + != speeds.end()) { + result.push_back(awhp); + } + } + } + return result; + } + + } // namespace detail + + HeatPumpAirToWaterCoolingSpeedData::HeatPumpAirToWaterCoolingSpeedData(const Model& model) + : ResourceObject(HeatPumpAirToWaterCoolingSpeedData::iddObjectType(), model) { + OS_ASSERT(getImpl()); + + autosizeRatedCoolingCapacity(); + setRatedCOPforCooling(3.0); + + bool ok = true; + { + CurveBiquadratic capFT(model); + capFT.setName(fmt::format("{} CapCurveFuncTempHT", this->nameString())); + capFT.setCoefficient1Constant(0.885); + capFT.setCoefficient2x(-0.00040); + capFT.setCoefficient3xPOW2(0.00019); + capFT.setCoefficient4y(0.0202); + capFT.setCoefficient5yPOW2(-0.0096); + capFT.setCoefficient6xTIMESY(0.000580); + capFT.setMinimumValueofx(30.0); + capFT.setMaximumValueofx(50.0); + capFT.setMinimumValueofy(-7.0); + capFT.setMaximumValueofy(20.0); + capFT.setMinimumCurveOutput(0.5); + capFT.setMaximumCurveOutput(1.3); + capFT.setInputUnitTypeforX("Temperature"); + capFT.setInputUnitTypeforY("Temperature"); + capFT.setOutputUnitType("Dimensionless"); + + ok = setNormalizedCoolingCapacityFunctionofTemperatureCurve(capFT); + OS_ASSERT(ok); + } + + { + CurveBiquadratic eirFT(model); + eirFT.setName(fmt::format("{} EIRCurveFuncTempHT", this->nameString())); + eirFT.setCoefficient1Constant(1.055); + eirFT.setCoefficient2x(0.00035); + eirFT.setCoefficient3xPOW2(-0.00027); + eirFT.setCoefficient4y(-0.0108); + eirFT.setCoefficient5yPOW2(0.0145); + eirFT.setCoefficient6xTIMESY(-0.000230); + eirFT.setMinimumValueofx(30.0); + eirFT.setMaximumValueofx(50.0); + eirFT.setMinimumValueofy(-7.0); + eirFT.setMaximumValueofy(20.0); + eirFT.setMinimumCurveOutput(0.7); + eirFT.setMaximumCurveOutput(1.5); + eirFT.setInputUnitTypeforX("Temperature"); + eirFT.setInputUnitTypeforY("Temperature"); + eirFT.setOutputUnitType("Dimensionless"); + + ok = setCoolingEnergyInputRatioFunctionofTemperatureCurve(eirFT); + OS_ASSERT(ok); + } + + { + CurveQuadratic eirfPLR(model); + eirfPLR.setName(fmt::format("{} EIRCurveFuncPLR", this->nameString())); + eirfPLR.setCoefficient1Constant(1.0); + eirfPLR.setCoefficient2x(0.0); + eirfPLR.setCoefficient3xPOW2(0.0); + eirfPLR.setMinimumValueofx(0.0); + eirfPLR.setMaximumValueofx(1.0); + eirfPLR.setMinimumCurveOutput(0.0); + eirfPLR.setMaximumCurveOutput(1.0); + eirfPLR.setInputUnitTypeforX("Dimensionless"); + eirfPLR.setOutputUnitType("Dimensionless"); + ok = setCoolingEnergyInputRatioFunctionofPLRCurve(eirfPLR); + OS_ASSERT(ok); + } + } + + HeatPumpAirToWaterCoolingSpeedData::HeatPumpAirToWaterCoolingSpeedData(const Model& model, + const Curve& normalizedCoolingCapacityFunctionofTemperatureCurve, + const Curve& coolingEnergyInputRatioFunctionofTemperatureCurve, + const Curve& coolingEnergyInputRatioFunctionofPLRCurve) + : ResourceObject(HeatPumpAirToWaterCoolingSpeedData::iddObjectType(), model) { + + auto impl = getImpl(); + OS_ASSERT(impl); + + autosizeRatedCoolingCapacity(); + setRatedCOPforCooling(3.0); + + bool ok = setNormalizedCoolingCapacityFunctionofTemperatureCurve(normalizedCoolingCapacityFunctionofTemperatureCurve); + if (!ok) { + // We don't call remove() (which would do ResourceObject::remove()) because that would try to remove the curves too if they aren't being used, + // so we call ModelObject::remove() directly + impl->detail::ModelObject_Impl::remove(); + LOG_AND_THROW("Unable to set " << briefDescription() << "'s Capacity Curve to " + << normalizedCoolingCapacityFunctionofTemperatureCurve.briefDescription() << "."); + } + ok = setCoolingEnergyInputRatioFunctionofTemperatureCurve(coolingEnergyInputRatioFunctionofTemperatureCurve); + if (!ok) { + impl->detail::ModelObject_Impl::remove(); + LOG_AND_THROW("Unable to set " << briefDescription() << "'s EIRfT Curve to " + << coolingEnergyInputRatioFunctionofTemperatureCurve.briefDescription() << "."); + } + ok = setCoolingEnergyInputRatioFunctionofPLRCurve(coolingEnergyInputRatioFunctionofPLRCurve); + if (!ok) { + impl->detail::ModelObject_Impl::remove(); + LOG_AND_THROW("Unable to set " << briefDescription() << "'s EIRfPLR Curve to " << coolingEnergyInputRatioFunctionofPLRCurve.briefDescription() + << "."); + } + } + + IddObjectType HeatPumpAirToWaterCoolingSpeedData::iddObjectType() { + return {IddObjectType::OS_HeatPump_AirToWater_Cooling_SpeedData}; + } + + boost::optional HeatPumpAirToWaterCoolingSpeedData::ratedCoolingCapacity() const { + return getImpl()->ratedCoolingCapacity(); + } + + bool HeatPumpAirToWaterCoolingSpeedData::isRatedCoolingCapacityAutosized() const { + return getImpl()->isRatedCoolingCapacityAutosized(); + } + + boost::optional HeatPumpAirToWaterCoolingSpeedData::autosizedRatedCoolingCapacity() { + return getImpl()->autosizedRatedCoolingCapacity(); + } + + double HeatPumpAirToWaterCoolingSpeedData::ratedCOPforCooling() const { + return getImpl()->ratedCOPforCooling(); + } + + Curve HeatPumpAirToWaterCoolingSpeedData::normalizedCoolingCapacityFunctionofTemperatureCurve() const { + return getImpl()->normalizedCoolingCapacityFunctionofTemperatureCurve(); + } + + Curve HeatPumpAirToWaterCoolingSpeedData::coolingEnergyInputRatioFunctionofTemperatureCurve() const { + return getImpl()->coolingEnergyInputRatioFunctionofTemperatureCurve(); + } + + Curve HeatPumpAirToWaterCoolingSpeedData::coolingEnergyInputRatioFunctionofPLRCurve() const { + return getImpl()->coolingEnergyInputRatioFunctionofPLRCurve(); + } + + bool HeatPumpAirToWaterCoolingSpeedData::setRatedCoolingCapacity(double ratedCoolingCapacity) { + return getImpl()->setRatedCoolingCapacity(ratedCoolingCapacity); + } + + void HeatPumpAirToWaterCoolingSpeedData::autosizeRatedCoolingCapacity() { + getImpl()->autosizeRatedCoolingCapacity(); + } + + bool HeatPumpAirToWaterCoolingSpeedData::setRatedCOPforCooling(double ratedCOPforCooling) { + return getImpl()->setRatedCOPforCooling(ratedCOPforCooling); + } + + bool HeatPumpAirToWaterCoolingSpeedData::setNormalizedCoolingCapacityFunctionofTemperatureCurve(const Curve& bivariateFunctions) { + return getImpl()->setNormalizedCoolingCapacityFunctionofTemperatureCurve(bivariateFunctions); + } + + bool HeatPumpAirToWaterCoolingSpeedData::setCoolingEnergyInputRatioFunctionofTemperatureCurve(const Curve& bivariateFunctions) { + return getImpl()->setCoolingEnergyInputRatioFunctionofTemperatureCurve(bivariateFunctions); + } + + bool HeatPumpAirToWaterCoolingSpeedData::setCoolingEnergyInputRatioFunctionofPLRCurve(const Curve& univariateFunctions) { + return getImpl()->setCoolingEnergyInputRatioFunctionofPLRCurve(univariateFunctions); + } + + /// @cond + HeatPumpAirToWaterCoolingSpeedData::HeatPumpAirToWaterCoolingSpeedData(std::shared_ptr impl) + : ResourceObject(std::move(impl)) {} + /// @endcond + + void HeatPumpAirToWaterCoolingSpeedData::autosize() { + getImpl()->autosize(); + } + + // TODO: needed? + // void HeatPumpAirToWaterCoolingSpeedData::applySizingValues() { + // getImpl()->applySizingValues(); + // } + + std::vector HeatPumpAirToWaterCoolingSpeedData::heatPumpAirToWaterCoolings() const { + return getImpl()->heatPumpAirToWaterCoolings(); + } + +} // namespace model +} // namespace openstudio diff --git a/src/model/HeatPumpAirToWaterCoolingSpeedData.hpp b/src/model/HeatPumpAirToWaterCoolingSpeedData.hpp new file mode 100644 index 0000000000..0060fdd562 --- /dev/null +++ b/src/model/HeatPumpAirToWaterCoolingSpeedData.hpp @@ -0,0 +1,116 @@ +/*********************************************************************************************************************** +* OpenStudio(R), Copyright (c) Alliance for Sustainable Energy, LLC. +* See also https://openstudio.net/license +***********************************************************************************************************************/ + +#ifndef MODEL_HEATPUMPAIRTOWATERCOOLINGSPEEDDATA_HPP +#define MODEL_HEATPUMPAIRTOWATERCOOLINGSPEEDDATA_HPP + +#include "ModelAPI.hpp" +#include "ResourceObject.hpp" + +namespace openstudio { + +namespace model { + + class Curve; + class HeatPumpAirToWaterCooling; + + namespace detail { + + class HeatPumpAirToWaterCoolingSpeedData_Impl; + + } // namespace detail + + /** HeatPumpAirToWaterCoolingSpeedData is a ResourceObject that wraps the OpenStudio IDD object 'OS:HeatPump:AirToWater:Cooling:SpeedData'. */ + class MODEL_API HeatPumpAirToWaterCoolingSpeedData : public ResourceObject + { + public: + /** @name Constructors and Destructors */ + //@{ + + explicit HeatPumpAirToWaterCoolingSpeedData(const Model& model); + + explicit HeatPumpAirToWaterCoolingSpeedData(const Model& model, const Curve& normalizedCoolingCapacityFunctionofTemperatureCurve, + const Curve& coolingEnergyInputRatioFunctionofTemperatureCurve, + const Curve& coolingEnergyInputRatioFunctionofPLRCurve); + + virtual ~HeatPumpAirToWaterCoolingSpeedData() = default; + // Default the copy and move operators because the virtual dtor is explicit + HeatPumpAirToWaterCoolingSpeedData(const HeatPumpAirToWaterCoolingSpeedData& other) = default; + HeatPumpAirToWaterCoolingSpeedData(HeatPumpAirToWaterCoolingSpeedData&& other) = default; + HeatPumpAirToWaterCoolingSpeedData& operator=(const HeatPumpAirToWaterCoolingSpeedData&) = default; + HeatPumpAirToWaterCoolingSpeedData& operator=(HeatPumpAirToWaterCoolingSpeedData&&) = default; + + //@} + + static IddObjectType iddObjectType(); + + /** @name Getters */ + //@{ + + boost::optional ratedCoolingCapacity() const; + + bool isRatedCoolingCapacityAutosized() const; + + double ratedCOPforCooling() const; + + Curve normalizedCoolingCapacityFunctionofTemperatureCurve() const; + + Curve coolingEnergyInputRatioFunctionofTemperatureCurve() const; + + Curve coolingEnergyInputRatioFunctionofPLRCurve() const; + + //@} + /** @name Setters */ + //@{ + + bool setRatedCoolingCapacity(double ratedCoolingCapacity); + + void autosizeRatedCoolingCapacity(); + + bool setRatedCOPforCooling(double ratedCOPforCooling); + + bool setNormalizedCoolingCapacityFunctionofTemperatureCurve(const Curve& bivariateFunctions); + + bool setCoolingEnergyInputRatioFunctionofTemperatureCurve(const Curve& bivariateFunctions); + + bool setCoolingEnergyInputRatioFunctionofPLRCurve(const Curve& univariateFunctions); + + //@} + /** @name Other */ + //@{ + + // convenience function that returns a vector of HeatPumpAirToWaterCooling that use this as their Speed Data (either regular speed or Booster) + std::vector heatPumpAirToWaterCoolings() const; + + boost::optional autosizedRatedCoolingCapacity(); + + void autosize(); + + //@} + protected: + /// @cond + using ImplType = detail::HeatPumpAirToWaterCoolingSpeedData_Impl; + + explicit HeatPumpAirToWaterCoolingSpeedData(std::shared_ptr impl); + + friend class detail::HeatPumpAirToWaterCoolingSpeedData_Impl; + friend class Model; + friend class IdfObject; + friend class openstudio::detail::IdfObject_Impl; + /// @endcond + private: + REGISTER_LOGGER("openstudio.model.HeatPumpAirToWaterCoolingSpeedData"); + }; + + /** \relates HeatPumpAirToWaterCoolingSpeedData*/ + using OptionalHeatPumpAirToWaterCoolingSpeedData = boost::optional; + + /** \relates HeatPumpAirToWaterCoolingSpeedData*/ + using HeatPumpAirToWaterCoolingSpeedDataVector = std::vector; + +} // namespace model +} // namespace openstudio + +#endif // MODEL_HEATPUMPAIRTOWATERCOOLINGSPEEDDATA_HPP diff --git a/src/model/HeatPumpAirToWaterCoolingSpeedData_Impl.hpp b/src/model/HeatPumpAirToWaterCoolingSpeedData_Impl.hpp new file mode 100644 index 0000000000..c056797123 --- /dev/null +++ b/src/model/HeatPumpAirToWaterCoolingSpeedData_Impl.hpp @@ -0,0 +1,112 @@ +/*********************************************************************************************************************** +* OpenStudio(R), Copyright (c) Alliance for Sustainable Energy, LLC. +* See also https://openstudio.net/license +***********************************************************************************************************************/ + +#ifndef MODEL_HEATPUMPAIRTOWATERCOOLINGSPEEDDATA_IMPL_HPP +#define MODEL_HEATPUMPAIRTOWATERCOOLINGSPEEDDATA_IMPL_HPP + +#include "ModelAPI.hpp" +#include "ResourceObject_Impl.hpp" + +namespace openstudio { +namespace model { + + class Curve; + class HeatPumpAirToWaterCooling; + + namespace detail { + + /** HeatPumpAirToWaterCoolingSpeedData_Impl is a ResourceObject_Impl that is the implementation class for HeatPumpAirToWaterCoolingSpeedData.*/ + class MODEL_API HeatPumpAirToWaterCoolingSpeedData_Impl : public ResourceObject_Impl + { + public: + /** @name Constructors and Destructors */ + //@{ + + HeatPumpAirToWaterCoolingSpeedData_Impl(const IdfObject& idfObject, Model_Impl* model, bool keepHandle); + + HeatPumpAirToWaterCoolingSpeedData_Impl(const openstudio::detail::WorkspaceObject_Impl& other, Model_Impl* model, bool keepHandle); + + HeatPumpAirToWaterCoolingSpeedData_Impl(const HeatPumpAirToWaterCoolingSpeedData_Impl& other, Model_Impl* model, bool keepHandle); + + virtual ~HeatPumpAirToWaterCoolingSpeedData_Impl() = default; + + //@} + /** @name Virtual Methods */ + //@{ + + virtual const std::vector& outputVariableNames() const override; + + virtual IddObjectType iddObjectType() const override; + + // Overriding clone and children here because we want to try to remove the Curves (if they aren't used by something else) + // So we list them as children. But ResourceObject_Impl::clone would also clone them, so we override clone to call ModelObject_Impl::clone + virtual ModelObject clone(Model model) const override; + + virtual std::vector children() const override; + + //@} + /** @name Getters */ + //@{ + + boost::optional ratedCoolingCapacity() const; + + bool isRatedCoolingCapacityAutosized() const; + + boost::optional autosizedRatedCoolingCapacity(); + + double ratedCOPforCooling() const; + + Curve normalizedCoolingCapacityFunctionofTemperatureCurve() const; + + Curve coolingEnergyInputRatioFunctionofTemperatureCurve() const; + + Curve coolingEnergyInputRatioFunctionofPLRCurve() const; + + //@} + /** @name Setters */ + //@{ + + bool setRatedCoolingCapacity(double ratedCoolingCapacity); + + void autosizeRatedCoolingCapacity(); + + bool setRatedCOPforCooling(double ratedCOPforCooling); + + bool setNormalizedCoolingCapacityFunctionofTemperatureCurve(const Curve& bivariateFunctions); + + bool setCoolingEnergyInputRatioFunctionofTemperatureCurve(const Curve& bivariateFunctions); + + bool setCoolingEnergyInputRatioFunctionofPLRCurve(const Curve& univariateFunctions); + + //@} + /** @name Other */ + //@{ + + // convenience function that returns a vector of HeatPumpAirToWaterCooling that use this as their Speed Data (either regular speed or Booster) + std::vector heatPumpAirToWaterCoolings() const; + + void autosize(); + + void applySizingValues(); + + //@} + protected: + private: + REGISTER_LOGGER("openstudio.model.HeatPumpAirToWaterCoolingSpeedData"); + + // Optional getters for use by methods like children() so can remove() if the constructor fails. + // There are other ways for the public versions of these getters to fail--perhaps all required + // objects should be returned as boost::optionals + boost::optional optionalNormalizedCoolingCapacityFunctionofTemperatureCurve() const; + boost::optional optionalCoolingEnergyInputRatioFunctionofTemperatureCurve() const; + boost::optional optionalCoolingEnergyInputRatioFunctionofPLRCurve() const; + }; + + } // namespace detail + +} // namespace model +} // namespace openstudio + +#endif // MODEL_HEATPUMPAIRTOWATERCOOLINGSPEEDDATA_IMPL_HPP diff --git a/src/model/HeatPumpAirToWaterCooling_Impl.hpp b/src/model/HeatPumpAirToWaterCooling_Impl.hpp new file mode 100644 index 0000000000..82ef07d228 --- /dev/null +++ b/src/model/HeatPumpAirToWaterCooling_Impl.hpp @@ -0,0 +1,188 @@ +/*********************************************************************************************************************** +* OpenStudio(R), Copyright (c) Alliance for Sustainable Energy, LLC. +* See also https://openstudio.net/license +***********************************************************************************************************************/ + +#ifndef MODEL_HEATPUMPAIRTOWATERCOOLING_IMPL_HPP +#define MODEL_HEATPUMPAIRTOWATERCOOLING_IMPL_HPP + +#include "ModelAPI.hpp" +#include "StraightComponent_Impl.hpp" + +namespace openstudio { +namespace model { + + class Curve; + class HeatPumpAirToWater; + class HeatPumpAirToWaterCoolingSpeedData; + class ModelObjectList; + class Node; + class Schedule; + + namespace detail { + + /** HeatPumpAirToWaterCooling_Impl is a StraightComponent_Impl that is the implementation class for HeatPumpAirToWaterCooling.*/ + class MODEL_API HeatPumpAirToWaterCooling_Impl : public StraightComponent_Impl + { + public: + /** @name Constructors and Destructors */ + //@{ + + HeatPumpAirToWaterCooling_Impl(const IdfObject& idfObject, Model_Impl* model, bool keepHandle); + + HeatPumpAirToWaterCooling_Impl(const openstudio::detail::WorkspaceObject_Impl& other, Model_Impl* model, bool keepHandle); + + HeatPumpAirToWaterCooling_Impl(const HeatPumpAirToWaterCooling_Impl& other, Model_Impl* model, bool keepHandle); + + virtual ~HeatPumpAirToWaterCooling_Impl() = default; + + //@} + /** @name Virtual Methods */ + //@{ + + virtual const std::vector& outputVariableNames() const override; + + virtual IddObjectType iddObjectType() const override; + + virtual std::vector getScheduleTypeKeys(const Schedule& schedule) const override; + + virtual ModelObject clone(Model model) const override; + + virtual std::vector children() const override; + // virtual std::vector allowableChildTypes() const override; + + virtual std::vector remove() override; + + // Overrides from StraightComponent + virtual unsigned inletPort() const override; + virtual unsigned outletPort() const override; + + virtual bool addToNode(Node& node) override; + + virtual boost::optional containingHVACComponent() const override; + // virtual boost::optional containingZoneHVACComponent() const override; + // virtual boost::optional containingStraightComponent() const override; + + virtual void autosize() override; + + virtual void applySizingValues() override; + + virtual ComponentType componentType() const override; + virtual std::vector coolingFuelTypes() const override; + virtual std::vector heatingFuelTypes() const override; + virtual std::vector appGHeatingFuelTypes() const override; + + //@} + /** @name Getters */ + //@{ + + Schedule availabilitySchedule() const; + + double ratedInletAirTemperature() const; + + boost::optional ratedAirFlowRate() const; + + bool isRatedAirFlowRateAutosized() const; + + double ratedLeavingWaterTemperature() const; + + boost::optional ratedWaterFlowRate() const; + + bool isRatedWaterFlowRateAutosized() const; + + double minimumOutdoorAirTemperature() const; + + double maximumOutdoorAirTemperature() const; + + boost::optional minimumLeavingWaterTemperatureCurve() const; + + boost::optional maximumLeavingWaterTemperatureCurve() const; + + double sizingFactor() const; + + boost::optional boosterModeOnSpeed() const; + + //@} + /** @name Setters */ + //@{ + + bool setAvailabilitySchedule(Schedule& availabilitySchedule); + + bool setRatedInletAirTemperature(double ratedInletAirTemperature); + + bool setRatedAirFlowRate(double ratedAirFlowRate); + + void autosizeRatedAirFlowRate(); + + bool setRatedLeavingWaterTemperature(double ratedLeavingWaterTemperature); + + bool setRatedWaterFlowRate(double ratedWaterFlowRate); + + void autosizeRatedWaterFlowRate(); + + bool setMinimumOutdoorAirTemperature(double minimumOutdoorAirTemperature); + + bool setMaximumOutdoorAirTemperature(double maximumOutdoorAirTemperature); + + bool setMinimumLeavingWaterTemperatureCurve(const Curve& minimumLeavingWaterTemperatureCurve); + + void resetMinimumLeavingWaterTemperatureCurve(); + + bool setMaximumLeavingWaterTemperatureCurve(const Curve& maximumLeavingWaterTemperatureCurve); + + void resetMaximumLeavingWaterTemperatureCurve(); + + bool setSizingFactor(double sizingFactor); + + bool setBoosterModeOnSpeed(const HeatPumpAirToWaterCoolingSpeedData& boosterModeOnSpeed); + + void resetBoosterModeOnSpeed(); + + //@} + /** @name Other */ + //@{ + + bool setSpeedDataList(const ModelObjectList& speedDataList); + void resetSpeedDataList(); + ModelObjectList speedDataList() const; + + std::vector speeds() const; + unsigned numberOfSpeeds() const; + boost::optional speedIndex(const HeatPumpAirToWaterCoolingSpeedData& speed) const; + + bool addSpeed(const HeatPumpAirToWaterCoolingSpeedData& speed); + bool addSpeed(const HeatPumpAirToWaterCoolingSpeedData& speed, unsigned index); + bool setSpeedIndex(const HeatPumpAirToWaterCoolingSpeedData& speed, unsigned index); + bool setSpeeds(const std::vector& speeds); + bool removeSpeed(const HeatPumpAirToWaterCoolingSpeedData& speed); + bool removeSpeed(unsigned index); + void removeAllSpeeds(); + + // Convenience function to return the HeatPumpAirToWater object that reference this cooling coil if any + boost::optional heatPumpAirToWater() const; + + // Autosize methods + + boost::optional autosizedRatedAirFlowRate() const; + boost::optional autosizedRatedWaterFlowRate() const; + boost::optional autosizedRatedCoolingCapacity() const; // Convenience method + + //@} + protected: + private: + REGISTER_LOGGER("openstudio.model.HeatPumpAirToWaterCooling"); + + // TODO: Check the return types of these methods. + // Optional getters for use by methods like children() so can remove() if the constructor fails. + // There are other ways for the public versions of these getters to fail--perhaps all required + // objects should be returned as boost::optionals + boost::optional optionalAvailabilitySchedule() const; + boost::optional optionalSpeedDataList() const; + }; + + } // namespace detail + +} // namespace model +} // namespace openstudio + +#endif // MODEL_HEATPUMPAIRTOWATERCOOLING_IMPL_HPP diff --git a/src/model/HeatPumpAirToWaterHeating.cpp b/src/model/HeatPumpAirToWaterHeating.cpp new file mode 100644 index 0000000000..bd7ee2e147 --- /dev/null +++ b/src/model/HeatPumpAirToWaterHeating.cpp @@ -0,0 +1,747 @@ +/*********************************************************************************************************************** +* OpenStudio(R), Copyright (c) Alliance for Sustainable Energy, LLC. +* See also https://openstudio.net/license +***********************************************************************************************************************/ + +#include "HeatPumpAirToWaterHeating.hpp" +#include "HeatPumpAirToWaterHeating_Impl.hpp" + +#include "HeatPumpAirToWater.hpp" +#include "HeatPumpAirToWater_Impl.hpp" +#include "HeatPumpAirToWaterHeatingSpeedData.hpp" +#include "HeatPumpAirToWaterHeatingSpeedData_Impl.hpp" + +#include "Schedule.hpp" +#include "Schedule_Impl.hpp" +#include "Curve.hpp" +#include "Curve_Impl.hpp" +#include "ModelObjectList.hpp" +#include "ModelObjectList_Impl.hpp" + +#include "ScheduleTypeLimits.hpp" +#include "ScheduleTypeRegistry.hpp" + +#include "PlantLoop.hpp" +#include "PlantLoop_Impl.hpp" +#include "Node.hpp" + +// Need for clone override +#include "Model.hpp" +#include "Model_Impl.hpp" + +#include "../utilities/core/Assert.hpp" +#include "../utilities/core/ContainersMove.hpp" +#include "../utilities/data/DataEnums.hpp" + +#include +#include + +namespace openstudio { +namespace model { + + namespace detail { + + HeatPumpAirToWaterHeating_Impl::HeatPumpAirToWaterHeating_Impl(const IdfObject& idfObject, Model_Impl* model, bool keepHandle) + : StraightComponent_Impl(idfObject, model, keepHandle) { + OS_ASSERT(idfObject.iddObject().type() == HeatPumpAirToWaterHeating::iddObjectType()); + } + + HeatPumpAirToWaterHeating_Impl::HeatPumpAirToWaterHeating_Impl(const openstudio::detail::WorkspaceObject_Impl& other, Model_Impl* model, + bool keepHandle) + : StraightComponent_Impl(other, model, keepHandle) { + OS_ASSERT(other.iddObject().type() == HeatPumpAirToWaterHeating::iddObjectType()); + } + + HeatPumpAirToWaterHeating_Impl::HeatPumpAirToWaterHeating_Impl(const HeatPumpAirToWaterHeating_Impl& other, Model_Impl* model, bool keepHandle) + : StraightComponent_Impl(other, model, keepHandle) {} + + const std::vector& HeatPumpAirToWaterHeating_Impl::outputVariableNames() const { + static std::vector result; + if (result.empty()) { + } + return result; + } + + std::vector HeatPumpAirToWaterHeating_Impl::getScheduleTypeKeys(const Schedule& schedule) const { + std::vector result; + const UnsignedVector fieldIndices = getSourceIndices(schedule.handle()); + if (std::find(fieldIndices.cbegin(), fieldIndices.cend(), OS_HeatPump_AirToWater_HeatingFields::AvailabilityScheduleName) + != fieldIndices.cend()) { + result.emplace_back("HeatPumpAirToWaterHeating", "Availability"); + } + return result; + } + + IddObjectType HeatPumpAirToWaterHeating_Impl::iddObjectType() const { + return HeatPumpAirToWaterHeating::iddObjectType(); + } + + unsigned HeatPumpAirToWaterHeating_Impl::inletPort() const { + return OS_HeatPump_AirToWater_HeatingFields::HotWaterInletNodeName; + } + + unsigned HeatPumpAirToWaterHeating_Impl::outletPort() const { + return OS_HeatPump_AirToWater_HeatingFields::HotWaterOutletNodeName; + } + + bool HeatPumpAirToWaterHeating_Impl::addToNode(Node& node) { + if (boost::optional plant = node.plantLoop()) { + if (plant->supplyComponent(node.handle())) { + return StraightComponent_Impl::addToNode(node); + } + } + + return false; + } + + boost::optional HeatPumpAirToWaterHeating_Impl::containingHVACComponent() const { + auto awhp_ = heatPumpAirToWater(); + if (awhp_) { + return std::move(*awhp_); + } + return boost::none; + } + + boost::optional HeatPumpAirToWaterHeating_Impl::heatPumpAirToWater() const { + auto awhps = getObject().getModelObjectSources(HeatPumpAirToWater::iddObjectType()); + if (awhps.empty()) { + return boost::none; + } + if (awhps.size() > 1) { + LOG(Error, briefDescription() << " is referenced by more than one HeatPumpAirToWater, returning the first"); + } + return awhps[0]; + } + + ModelObject HeatPumpAirToWaterHeating_Impl::clone(Model model) const { + const bool same_model = this->model() == model; + // This handles resetting the ports, and bypassing ParentObject::clone so it doesn't clone children + auto t_clone = StraightComponent_Impl::clone(model).cast(); + + // Make a clean list and repopulate it, without cloning the SpeedData objects + auto newSpeedList = ModelObjectList(model); + newSpeedList.setName(t_clone.nameString() + " Speed Data List"); + bool ok = t_clone.getImpl()->setSpeedDataList(newSpeedList); + OS_ASSERT(ok); + + for (const auto& speed : speeds()) { + if (same_model) { + ok = t_clone.addSpeed(speed); + } else { + auto speedClone = speed.clone(model).cast(); + ok = t_clone.addSpeed(speedClone); + } + OS_ASSERT(ok); + } + + return std::move(t_clone); + } + + std::vector HeatPumpAirToWaterHeating_Impl::children() const { + std::vector children; + if (auto c_ = minimumLeavingWaterTemperatureCurve()) { + children.emplace_back(std::move(*c_)); + } + if (auto c_ = maximumLeavingWaterTemperatureCurve()) { + children.emplace_back(std::move(*c_)); + } + if (auto const speedDataList_ = optionalSpeedDataList()) { + for (const auto& mo : speedDataList_->modelObjects()) { + children.push_back(mo); + } + } + return children; + } + + // std::vector allowableChildTypes() const override; + + std::vector HeatPumpAirToWaterHeating_Impl::remove() { + // ModelObjectList is kinda dumb, it removes all the modelObjects it has, which we don't want + // We listed the SpeedData (ResourceObjects) in children, so it should be handled by ParentObject::remove + auto speedList_ = optionalSpeedDataList(); + std::vector result = StraightComponent_Impl::remove(); + if (!result.empty() && speedList_) { + speedList_->clearExtensibleGroups(); // Clearer than removeAllModelObjects (which does the same thing, but could mean delete all objects) + openstudio::detail::concat_helper(result, speedList_->remove()); + } + + return result; + } + + Schedule HeatPumpAirToWaterHeating_Impl::availabilitySchedule() const { + boost::optional value = optionalAvailabilitySchedule(); + if (!value) { + LOG_AND_THROW(briefDescription() << " does not have an Availability Schedule attached."); + } + return value.get(); + } + + double HeatPumpAirToWaterHeating_Impl::ratedInletAirTemperature() const { + boost::optional value = getDouble(OS_HeatPump_AirToWater_HeatingFields::RatedInletAirTemperature, true); + OS_ASSERT(value); + return value.get(); + } + + boost::optional HeatPumpAirToWaterHeating_Impl::ratedAirFlowRate() const { + return getDouble(OS_HeatPump_AirToWater_HeatingFields::RatedAirFlowRate, true); + } + + bool HeatPumpAirToWaterHeating_Impl::isRatedAirFlowRateAutosized() const { + bool result = false; + boost::optional value = getString(OS_HeatPump_AirToWater_HeatingFields::RatedAirFlowRate, true); + if (value) { + result = openstudio::istringEqual(value.get(), "autosize"); + } + return result; + } + + double HeatPumpAirToWaterHeating_Impl::ratedLeavingWaterTemperature() const { + boost::optional value = getDouble(OS_HeatPump_AirToWater_HeatingFields::RatedLeavingWaterTemperature, true); + OS_ASSERT(value); + return value.get(); + } + + boost::optional HeatPumpAirToWaterHeating_Impl::ratedWaterFlowRate() const { + return getDouble(OS_HeatPump_AirToWater_HeatingFields::RatedWaterFlowRate, true); + } + + bool HeatPumpAirToWaterHeating_Impl::isRatedWaterFlowRateAutosized() const { + bool result = false; + boost::optional value = getString(OS_HeatPump_AirToWater_HeatingFields::RatedWaterFlowRate, true); + if (value) { + result = openstudio::istringEqual(value.get(), "autosize"); + } + return result; + } + + double HeatPumpAirToWaterHeating_Impl::minimumOutdoorAirTemperature() const { + boost::optional value = getDouble(OS_HeatPump_AirToWater_HeatingFields::MinimumOutdoorAirTemperature, true); + OS_ASSERT(value); + return value.get(); + } + + double HeatPumpAirToWaterHeating_Impl::maximumOutdoorAirTemperature() const { + boost::optional value = getDouble(OS_HeatPump_AirToWater_HeatingFields::MaximumOutdoorAirTemperature, true); + OS_ASSERT(value); + return value.get(); + } + + boost::optional HeatPumpAirToWaterHeating_Impl::minimumLeavingWaterTemperatureCurve() const { + return getObject().getModelObjectTarget(OS_HeatPump_AirToWater_HeatingFields::MinimumLeavingWaterTemperatureCurveName); + } + + boost::optional HeatPumpAirToWaterHeating_Impl::maximumLeavingWaterTemperatureCurve() const { + return getObject().getModelObjectTarget(OS_HeatPump_AirToWater_HeatingFields::MaximumLeavingWaterTemperatureCurveName); + } + + double HeatPumpAirToWaterHeating_Impl::sizingFactor() const { + boost::optional value = getDouble(OS_HeatPump_AirToWater_HeatingFields::SizingFactor, true); + OS_ASSERT(value); + return value.get(); + } + + boost::optional HeatPumpAirToWaterHeating_Impl::boosterModeOnSpeed() const { + return getObject().getModelObjectTarget( + OS_HeatPump_AirToWater_HeatingFields::BoosterModeOnSpeed); + } + + bool HeatPumpAirToWaterHeating_Impl::setAvailabilitySchedule(Schedule& availabilitySchedule) { + const bool result = setSchedule(OS_HeatPump_AirToWater_HeatingFields::AvailabilityScheduleName, "HeatPumpAirToWaterHeating", "Availability", + availabilitySchedule); + return result; + } + + bool HeatPumpAirToWaterHeating_Impl::setRatedInletAirTemperature(double ratedInletAirTemperature) { + const bool result = setDouble(OS_HeatPump_AirToWater_HeatingFields::RatedInletAirTemperature, ratedInletAirTemperature); + OS_ASSERT(result); + return result; + } + + bool HeatPumpAirToWaterHeating_Impl::setRatedAirFlowRate(double ratedAirFlowRate) { + const bool result = setDouble(OS_HeatPump_AirToWater_HeatingFields::RatedAirFlowRate, ratedAirFlowRate); + return result; + } + + void HeatPumpAirToWaterHeating_Impl::autosizeRatedAirFlowRate() { + const bool result = setString(OS_HeatPump_AirToWater_HeatingFields::RatedAirFlowRate, "autosize"); + OS_ASSERT(result); + } + + bool HeatPumpAirToWaterHeating_Impl::setRatedLeavingWaterTemperature(double ratedLeavingWaterTemperature) { + const bool result = setDouble(OS_HeatPump_AirToWater_HeatingFields::RatedLeavingWaterTemperature, ratedLeavingWaterTemperature); + OS_ASSERT(result); + return result; + } + + bool HeatPumpAirToWaterHeating_Impl::setRatedWaterFlowRate(double ratedWaterFlowRate) { + const bool result = setDouble(OS_HeatPump_AirToWater_HeatingFields::RatedWaterFlowRate, ratedWaterFlowRate); + return result; + } + + void HeatPumpAirToWaterHeating_Impl::autosizeRatedWaterFlowRate() { + const bool result = setString(OS_HeatPump_AirToWater_HeatingFields::RatedWaterFlowRate, "autosize"); + OS_ASSERT(result); + } + + bool HeatPumpAirToWaterHeating_Impl::setMinimumOutdoorAirTemperature(double minimumOutdoorAirTemperature) { + const bool result = setDouble(OS_HeatPump_AirToWater_HeatingFields::MinimumOutdoorAirTemperature, minimumOutdoorAirTemperature); + OS_ASSERT(result); + return result; + } + + bool HeatPumpAirToWaterHeating_Impl::setMaximumOutdoorAirTemperature(double maximumOutdoorAirTemperature) { + const bool result = setDouble(OS_HeatPump_AirToWater_HeatingFields::MaximumOutdoorAirTemperature, maximumOutdoorAirTemperature); + OS_ASSERT(result); + return result; + } + + bool HeatPumpAirToWaterHeating_Impl::setMinimumLeavingWaterTemperatureCurve(const Curve& minimumLeavingWaterTemperatureCurve) { + const bool result = + setPointer(OS_HeatPump_AirToWater_HeatingFields::MinimumLeavingWaterTemperatureCurveName, minimumLeavingWaterTemperatureCurve.handle()); + return result; + } + + void HeatPumpAirToWaterHeating_Impl::resetMinimumLeavingWaterTemperatureCurve() { + const bool result = setString(OS_HeatPump_AirToWater_HeatingFields::MinimumLeavingWaterTemperatureCurveName, ""); + OS_ASSERT(result); + } + + bool HeatPumpAirToWaterHeating_Impl::setMaximumLeavingWaterTemperatureCurve(const Curve& maximumLeavingWaterTemperatureCurve) { + const bool result = + setPointer(OS_HeatPump_AirToWater_HeatingFields::MaximumLeavingWaterTemperatureCurveName, maximumLeavingWaterTemperatureCurve.handle()); + return result; + } + + void HeatPumpAirToWaterHeating_Impl::resetMaximumLeavingWaterTemperatureCurve() { + const bool result = setString(OS_HeatPump_AirToWater_HeatingFields::MaximumLeavingWaterTemperatureCurveName, ""); + OS_ASSERT(result); + } + + bool HeatPumpAirToWaterHeating_Impl::setSizingFactor(double sizingFactor) { + const bool result = setDouble(OS_HeatPump_AirToWater_HeatingFields::SizingFactor, sizingFactor); + return result; + } + + bool HeatPumpAirToWaterHeating_Impl::setBoosterModeOnSpeed(const HeatPumpAirToWaterHeatingSpeedData& boosterModeOnSpeed) { + const bool result = setPointer(OS_HeatPump_AirToWater_HeatingFields::BoosterModeOnSpeed, boosterModeOnSpeed.handle()); + return result; + } + + void HeatPumpAirToWaterHeating_Impl::resetBoosterModeOnSpeed() { + const bool result = setString(OS_HeatPump_AirToWater_HeatingFields::BoosterModeOnSpeed, ""); + OS_ASSERT(result); + } + + boost::optional HeatPumpAirToWaterHeating_Impl::autosizedRatedAirFlowRate() const { + boost::optional result; + if (auto awhp_ = heatPumpAirToWater()) { + result = awhp_->autosizedRatedAirFlowRateinHeatingMode(); + } + return result; + } + + boost::optional HeatPumpAirToWaterHeating_Impl::autosizedRatedWaterFlowRate() const { + boost::optional result; + if (auto awhp_ = heatPumpAirToWater()) { + result = awhp_->autosizedRatedWaterFlowRateinHeatingMode(); + } + return result; + } + + boost::optional HeatPumpAirToWaterHeating_Impl::autosizedRatedHeatingCapacity() const { + boost::optional result; + if (auto awhp_ = heatPumpAirToWater()) { + result = awhp_->autosizedRatedHeatingCapacity(); + } + return result; + } + + void HeatPumpAirToWaterHeating_Impl::autosize() { + autosizeRatedAirFlowRate(); + autosizeRatedWaterFlowRate(); + auto speeds = this->speeds(); + if (!speeds.empty()) { + speeds.back().autosize(); // Only need/can autosize the last (highest) speed + } + } + + void HeatPumpAirToWaterHeating_Impl::applySizingValues() { + if (boost::optional val_ = autosizedRatedAirFlowRate()) { + setRatedAirFlowRate(*val_); + } + + if (boost::optional val_ = autosizedRatedWaterFlowRate()) { + setRatedWaterFlowRate(*val_); + } + + if (boost::optional val_ = autosizedRatedHeatingCapacity()) { + auto speeds = this->speeds(); + if (!speeds.empty()) { + speeds.back().setRatedHeatingCapacity(*val_); // Only need/can autosize the last (highest) speed + } + } + } + + ComponentType HeatPumpAirToWaterHeating_Impl::componentType() const { + return ComponentType::Heating; + } + + std::vector HeatPumpAirToWaterHeating_Impl::coolingFuelTypes() const { + return {}; + } + + std::vector HeatPumpAirToWaterHeating_Impl::heatingFuelTypes() const { + return {FuelType::Electricity}; + } + + std::vector HeatPumpAirToWaterHeating_Impl::appGHeatingFuelTypes() const { + return {AppGFuelType::HeatPump}; + } + + boost::optional HeatPumpAirToWaterHeating_Impl::optionalAvailabilitySchedule() const { + return getObject().getModelObjectTarget(OS_HeatPump_AirToWater_HeatingFields::AvailabilityScheduleName); + } + + // Speed API + boost::optional HeatPumpAirToWaterHeating_Impl::optionalSpeedDataList() const { + return getObject().getModelObjectTarget(OS_HeatPump_AirToWater_HeatingFields::SpeedDataList); + } + + ModelObjectList HeatPumpAirToWaterHeating_Impl::speedDataList() const { + boost::optional value = optionalSpeedDataList(); + if (!value) { + LOG_AND_THROW(briefDescription() << " does not have a Speed Data List attached."); + } + return value.get(); + } + + bool HeatPumpAirToWaterHeating_Impl::setSpeedDataList(const ModelObjectList& speedDataList) { + const bool result = setPointer(OS_HeatPump_AirToWater_HeatingFields::SpeedDataList, speedDataList.handle()); + return result; + } + + void HeatPumpAirToWaterHeating_Impl::resetSpeedDataList() { + bool result = setString(OS_HeatPump_AirToWater_HeatingFields::SpeedDataList, ""); + OS_ASSERT(result); + } + + std::vector HeatPumpAirToWaterHeating_Impl::speeds() const { + std::vector result; + auto const modelObjects = speedDataList().modelObjects(); + + for (auto&& elem : modelObjects) { + auto modelObject = elem.optionalCast(); + if (modelObject) { + result.push_back(std::move(*modelObject)); + } + } + return result; + } + + unsigned HeatPumpAirToWaterHeating_Impl::numberOfSpeeds() const { + return speeds().size(); // This is right in all cases, but this is slow + // return speedDataList().size(); // This is fast, but could be wrong if the user put non-SpeedData objects in the list or if there are blanks + } + + boost::optional HeatPumpAirToWaterHeating_Impl::speedIndex(const HeatPumpAirToWaterHeatingSpeedData& speed) const { + const auto speedVector = speeds(); + auto it = std::find_if(speedVector.cbegin(), speedVector.cend(), [&](const HeatPumpAirToWaterHeatingSpeedData& s) { return s == speed; }); + if (it != speedVector.cend()) { + return static_cast(std::distance(speedVector.cbegin(), it)) + 1; // 1-indexed + } + return boost::none; + } + + bool HeatPumpAirToWaterHeating_Impl::addSpeed(const HeatPumpAirToWaterHeatingSpeedData& speed) { + if (numberOfSpeeds() >= HeatPumpAirToWaterHeating::maximum_number_of_speeds) { + LOG(Error, "You have reached the maximum number of speeds (=" << HeatPumpAirToWaterHeating::maximum_number_of_speeds << "), occurred for " + << briefDescription() << "."); + return false; // too many speeds + } + if (speedIndex(speed)) { + return false; // already in the list + } + + return speedDataList().addModelObject(speed); + } + + bool HeatPumpAirToWaterHeating_Impl::setSpeedIndex(const HeatPumpAirToWaterHeatingSpeedData& speed, unsigned index) { + auto cur_idx_ = speedIndex(speed); + if (!cur_idx_) { + LOG(Warn, "For " << briefDescription() << " cannot set the index of speed " << speed.briefDescription() << " since it is not part of it."); + return false; + } + + auto speedVector = speeds(); + + if (index < 1) { + LOG(Debug, "Requested a speed index of " << index << " < 1 to be assigned to " << speed.briefDescription() << ", resetting to 1"); + index = 1; + } else if (index > speedVector.size()) { + LOG(Debug, "Requested a speed index of " << index << " > number of speeds (" << speedVector.size() << ") to be assigned to " + << speed.briefDescription() << ", resetting to " << speedVector.size()); + index = speedVector.size(); + } + + speedVector.erase(speedVector.begin() + (*cur_idx_ - 1)); // stageIndex is 1-indexed, and vector is 0-indexed + speedVector.insert(speedVector.begin() + (index - 1), speed); // index is 1-indexed, and vector is 0-indexed + + return setSpeeds(speedVector); + } + + bool HeatPumpAirToWaterHeating_Impl::addSpeed(const HeatPumpAirToWaterHeatingSpeedData& speed, unsigned index) { + bool ok = addSpeed(speed); + if (!ok) { + return false; + } + ok = setSpeedIndex(speed, index); + return ok; + } + + bool HeatPumpAirToWaterHeating_Impl::setSpeeds(const std::vector& speeds) { + // Clear the extensible groups, and redo them + bool ok = true; + removeAllSpeeds(); + for (const auto& s : speeds) { + ok &= addSpeed(s); + } + return ok; + } + + bool HeatPumpAirToWaterHeating_Impl::removeSpeed(const HeatPumpAirToWaterHeatingSpeedData& speed) { // NOLINT(readability-*) + auto speedList = speedDataList(); + if (!speedList.hasModelObject(speed)) { + LOG(Warn, "For " << briefDescription() << " cannot remove speed " << speed.briefDescription() << " since it is not part of it."); + return false; + } + speedList.removeModelObject(speed); + return true; + } + + bool HeatPumpAirToWaterHeating_Impl::removeSpeed(unsigned index) { + bool result = false; + if (index < 1) { + return false; + } + auto speedVector = speeds(); + if (index <= speedVector.size()) { + result = removeSpeed(speedVector[index - 1]); // index is 1-indexed + } + return result; + } + + void HeatPumpAirToWaterHeating_Impl::removeAllSpeeds() { // NOLINT(readability-make-member-function-const) + // TODO: same question, do we want to remove the underlying objects too? + speedDataList().removeAllModelObjects(); // This just clears the list, does not delete the underlying objects + } + + } // namespace detail + + HeatPumpAirToWaterHeating::HeatPumpAirToWaterHeating(const Model& model) : StraightComponent(HeatPumpAirToWaterHeating::iddObjectType(), model) { + OS_ASSERT(getImpl()); + + bool ok = true; + + auto speedList = ModelObjectList(model); + speedList.setName(this->nameString() + " Speed Data List"); + ok = getImpl()->setSpeedDataList(speedList); + OS_ASSERT(ok); + + auto availabilitySchedule = model.alwaysOnDiscreteSchedule(); + ok = setAvailabilitySchedule(availabilitySchedule); + OS_ASSERT(ok); + + // IDD Defaults + setRatedInletAirTemperature(8.0); + autosizeRatedAirFlowRate(); + setRatedLeavingWaterTemperature(40.0); + autosizeRatedWaterFlowRate(); + setMinimumOutdoorAirTemperature(-30.0); + setMaximumOutdoorAirTemperature(100.0); + setSizingFactor(1.0); + } + + IddObjectType HeatPumpAirToWaterHeating::iddObjectType() { + return {IddObjectType::OS_HeatPump_AirToWater_Heating}; + } + + Schedule HeatPumpAirToWaterHeating::availabilitySchedule() const { + return getImpl()->availabilitySchedule(); + } + + double HeatPumpAirToWaterHeating::ratedInletAirTemperature() const { + return getImpl()->ratedInletAirTemperature(); + } + + boost::optional HeatPumpAirToWaterHeating::ratedAirFlowRate() const { + return getImpl()->ratedAirFlowRate(); + } + + bool HeatPumpAirToWaterHeating::isRatedAirFlowRateAutosized() const { + return getImpl()->isRatedAirFlowRateAutosized(); + } + + boost::optional HeatPumpAirToWaterHeating::autosizedRatedAirFlowRate() const { + return getImpl()->autosizedRatedAirFlowRate(); + } + + double HeatPumpAirToWaterHeating::ratedLeavingWaterTemperature() const { + return getImpl()->ratedLeavingWaterTemperature(); + } + + boost::optional HeatPumpAirToWaterHeating::ratedWaterFlowRate() const { + return getImpl()->ratedWaterFlowRate(); + } + + bool HeatPumpAirToWaterHeating::isRatedWaterFlowRateAutosized() const { + return getImpl()->isRatedWaterFlowRateAutosized(); + } + + boost::optional HeatPumpAirToWaterHeating::autosizedRatedWaterFlowRate() const { + return getImpl()->autosizedRatedWaterFlowRate(); + } + + double HeatPumpAirToWaterHeating::minimumOutdoorAirTemperature() const { + return getImpl()->minimumOutdoorAirTemperature(); + } + + double HeatPumpAirToWaterHeating::maximumOutdoorAirTemperature() const { + return getImpl()->maximumOutdoorAirTemperature(); + } + + boost::optional HeatPumpAirToWaterHeating::minimumLeavingWaterTemperatureCurve() const { + return getImpl()->minimumLeavingWaterTemperatureCurve(); + } + + boost::optional HeatPumpAirToWaterHeating::maximumLeavingWaterTemperatureCurve() const { + return getImpl()->maximumLeavingWaterTemperatureCurve(); + } + + double HeatPumpAirToWaterHeating::sizingFactor() const { + return getImpl()->sizingFactor(); + } + + boost::optional HeatPumpAirToWaterHeating::boosterModeOnSpeed() const { + return getImpl()->boosterModeOnSpeed(); + } + + bool HeatPumpAirToWaterHeating::setAvailabilitySchedule(Schedule& availabilitySchedule) { + return getImpl()->setAvailabilitySchedule(availabilitySchedule); + } + + bool HeatPumpAirToWaterHeating::setRatedInletAirTemperature(double ratedInletAirTemperature) { + return getImpl()->setRatedInletAirTemperature(ratedInletAirTemperature); + } + + bool HeatPumpAirToWaterHeating::setRatedAirFlowRate(double ratedAirFlowRate) { + return getImpl()->setRatedAirFlowRate(ratedAirFlowRate); + } + + void HeatPumpAirToWaterHeating::autosizeRatedAirFlowRate() { + getImpl()->autosizeRatedAirFlowRate(); + } + + bool HeatPumpAirToWaterHeating::setRatedLeavingWaterTemperature(double ratedLeavingWaterTemperature) { + return getImpl()->setRatedLeavingWaterTemperature(ratedLeavingWaterTemperature); + } + + bool HeatPumpAirToWaterHeating::setRatedWaterFlowRate(double ratedWaterFlowRate) { + return getImpl()->setRatedWaterFlowRate(ratedWaterFlowRate); + } + + void HeatPumpAirToWaterHeating::autosizeRatedWaterFlowRate() { + getImpl()->autosizeRatedWaterFlowRate(); + } + + bool HeatPumpAirToWaterHeating::setMinimumOutdoorAirTemperature(double minimumOutdoorAirTemperature) { + return getImpl()->setMinimumOutdoorAirTemperature(minimumOutdoorAirTemperature); + } + + bool HeatPumpAirToWaterHeating::setMaximumOutdoorAirTemperature(double maximumOutdoorAirTemperature) { + return getImpl()->setMaximumOutdoorAirTemperature(maximumOutdoorAirTemperature); + } + + bool HeatPumpAirToWaterHeating::setMinimumLeavingWaterTemperatureCurve(const Curve& minimumLeavingWaterTemperatureCurve) { + return getImpl()->setMinimumLeavingWaterTemperatureCurve(minimumLeavingWaterTemperatureCurve); + } + + void HeatPumpAirToWaterHeating::resetMinimumLeavingWaterTemperatureCurve() { + getImpl()->resetMinimumLeavingWaterTemperatureCurve(); + } + + bool HeatPumpAirToWaterHeating::setMaximumLeavingWaterTemperatureCurve(const Curve& maximumLeavingWaterTemperatureCurve) { + return getImpl()->setMaximumLeavingWaterTemperatureCurve(maximumLeavingWaterTemperatureCurve); + } + + void HeatPumpAirToWaterHeating::resetMaximumLeavingWaterTemperatureCurve() { + getImpl()->resetMaximumLeavingWaterTemperatureCurve(); + } + + bool HeatPumpAirToWaterHeating::setSizingFactor(double sizingFactor) { + return getImpl()->setSizingFactor(sizingFactor); + } + + bool HeatPumpAirToWaterHeating::setBoosterModeOnSpeed(const HeatPumpAirToWaterHeatingSpeedData& boosterModeOnSpeed) { + return getImpl()->setBoosterModeOnSpeed(boosterModeOnSpeed); + } + + void HeatPumpAirToWaterHeating::resetBoosterModeOnSpeed() { + getImpl()->resetBoosterModeOnSpeed(); + } + + // Speed Data API + unsigned HeatPumpAirToWaterHeating::numberOfSpeeds() const { + return getImpl()->numberOfSpeeds(); + } + + boost::optional HeatPumpAirToWaterHeating::speedIndex(const HeatPumpAirToWaterHeatingSpeedData& speed) const { + return getImpl()->speedIndex(speed); + } + + std::vector HeatPumpAirToWaterHeating::speeds() const { + return getImpl()->speeds(); + } + + bool HeatPumpAirToWaterHeating::addSpeed(const HeatPumpAirToWaterHeatingSpeedData& speed) { + return getImpl()->addSpeed(speed); + } + + bool HeatPumpAirToWaterHeating::addSpeed(const HeatPumpAirToWaterHeatingSpeedData& speed, unsigned index) { + return getImpl()->addSpeed(speed, index); + } + + bool HeatPumpAirToWaterHeating::setSpeedIndex(const HeatPumpAirToWaterHeatingSpeedData& speed, unsigned index) { + return getImpl()->setSpeedIndex(speed, index); + } + + bool HeatPumpAirToWaterHeating::setSpeeds(const std::vector& speeds) { + return getImpl()->setSpeeds(speeds); + } + + void HeatPumpAirToWaterHeating::removeAllSpeeds() { + getImpl()->removeAllSpeeds(); + } + + bool HeatPumpAirToWaterHeating::removeSpeed(const HeatPumpAirToWaterHeatingSpeedData& speed) { + return getImpl()->removeSpeed(speed); + } + + bool HeatPumpAirToWaterHeating::removeSpeed(unsigned index) { + return getImpl()->removeSpeed(index); + } + + boost::optional HeatPumpAirToWaterHeating::heatPumpAirToWater() const { + return getImpl()->heatPumpAirToWater(); + } + + boost::optional HeatPumpAirToWaterHeating::autosizedRatedHeatingCapacity() const { + return getImpl()->autosizedRatedHeatingCapacity(); + } + + /// @cond + HeatPumpAirToWaterHeating::HeatPumpAirToWaterHeating(std::shared_ptr impl) + : StraightComponent(std::move(impl)) {} + /// @endcond + +} // namespace model +} // namespace openstudio diff --git a/src/model/HeatPumpAirToWaterHeating.hpp b/src/model/HeatPumpAirToWaterHeating.hpp new file mode 100644 index 0000000000..d9f6b0fb6f --- /dev/null +++ b/src/model/HeatPumpAirToWaterHeating.hpp @@ -0,0 +1,202 @@ +/*********************************************************************************************************************** +* OpenStudio(R), Copyright (c) Alliance for Sustainable Energy, LLC. +* See also https://openstudio.net/license +***********************************************************************************************************************/ + +#ifndef MODEL_HEATPUMPAIRTOWATERHEATING_HPP +#define MODEL_HEATPUMPAIRTOWATERHEATING_HPP + +#include "ModelAPI.hpp" +#include "StraightComponent.hpp" + +namespace openstudio { + +namespace model { + + class Curve; + class HeatPumpAirToWater; + class HeatPumpAirToWaterHeatingSpeedData; + class Schedule; + + namespace detail { + + class HeatPumpAirToWaterHeating_Impl; + + } // namespace detail + + /** HeatPumpAirToWaterHeating is a StraightComponent that wraps the OpenStudio IDD object 'OS:HeatPump:AirToWater:Heating'. */ + class MODEL_API HeatPumpAirToWaterHeating : public StraightComponent + { + public: + /** @name Constructors and Destructors */ + //@{ + + explicit HeatPumpAirToWaterHeating(const Model& model); + + virtual ~HeatPumpAirToWaterHeating() = default; + // Default the copy and move operators because the virtual dtor is explicit + HeatPumpAirToWaterHeating(const HeatPumpAirToWaterHeating& other) = default; + HeatPumpAirToWaterHeating(HeatPumpAirToWaterHeating&& other) = default; + HeatPumpAirToWaterHeating& operator=(const HeatPumpAirToWaterHeating&) = default; + HeatPumpAirToWaterHeating& operator=(HeatPumpAirToWaterHeating&&) = default; + + //@} + + static IddObjectType iddObjectType(); + + static constexpr unsigned maximum_number_of_speeds = 5; + + /** @name Getters */ + //@{ + + Schedule availabilitySchedule() const; + + double ratedInletAirTemperature() const; + + boost::optional ratedAirFlowRate() const; + + bool isRatedAirFlowRateAutosized() const; + + double ratedLeavingWaterTemperature() const; + + boost::optional ratedWaterFlowRate() const; + + bool isRatedWaterFlowRateAutosized() const; + + double minimumOutdoorAirTemperature() const; + + double maximumOutdoorAirTemperature() const; + + boost::optional minimumLeavingWaterTemperatureCurve() const; + + boost::optional maximumLeavingWaterTemperatureCurve() const; + + double sizingFactor() const; + + boost::optional boosterModeOnSpeed() const; + + //@} + /** @name Setters */ + //@{ + + bool setAvailabilitySchedule(Schedule& availabilitySchedule); + + bool setRatedInletAirTemperature(double ratedInletAirTemperature); + + bool setRatedAirFlowRate(double ratedAirFlowRate); + + void autosizeRatedAirFlowRate(); + + bool setRatedLeavingWaterTemperature(double ratedLeavingWaterTemperature); + + bool setRatedWaterFlowRate(double ratedWaterFlowRate); + + void autosizeRatedWaterFlowRate(); + + bool setMinimumOutdoorAirTemperature(double minimumOutdoorAirTemperature); + + bool setMaximumOutdoorAirTemperature(double maximumOutdoorAirTemperature); + + bool setMinimumLeavingWaterTemperatureCurve(const Curve& minimumLeavingWaterTemperatureCurve); + + void resetMinimumLeavingWaterTemperatureCurve(); + + bool setMaximumLeavingWaterTemperatureCurve(const Curve& maximumLeavingWaterTemperatureCurve); + + void resetMaximumLeavingWaterTemperatureCurve(); + + bool setSizingFactor(double sizingFactor); + + bool setBoosterModeOnSpeed(const HeatPumpAirToWaterHeatingSpeedData& boosterModeOnSpeed); + + void resetBoosterModeOnSpeed(); + + //@} + /** @name Other */ + //@{ + + /** Return the performance data for each stage. **/ + std::vector speeds() const; + + unsigned numberOfSpeeds() const; + + /* + * Get the index of a given HeatPumpAirToWaterHeatingSpeedData (1-indexed) + */ + boost::optional speedIndex(const HeatPumpAirToWaterHeatingSpeedData& speed) const; + + /* + * Add a new speed after all of the existing speeds. + */ + bool addSpeed(const HeatPumpAirToWaterHeatingSpeedData& speed); + + /* + * Add a new HeatPumpAirToWaterHeatingSpeedData to the list which a given index (1 to x). + * Internally calls addSpeed then setSpeedIndex, see remarks there + */ + bool addSpeed(const HeatPumpAirToWaterHeatingSpeedData& speed, unsigned index); + + /* + * You can shuffle the priority of a given HeatPumpAirToWaterHeatingSpeedData after having added it + * If index is below 1, it's reset to 1. + * If index is greater than the number of speeds, will reset to last + */ + bool setSpeedIndex(const HeatPumpAirToWaterHeatingSpeedData& speed, unsigned index); + + /* + * Set all speeds using a list of HeatPumpAirToWaterHeatingSpeedDatas + * Internally calls addSpeed, and will return the global status, but will continue trying if there are problems + * (eg: if you make a vector larger than the number of accepted speeds, or a vector that has a speed from another model, the valid speeds will be + * added indeed, but it'll eventually return false) + */ + bool setSpeeds(const std::vector& speeds); + + /* + * Removes all HeatPumpAirToWaterHeatingSpeedDatas in this object + */ + void removeAllSpeeds(); + + /* + * Remove the given HeatPumpAirToWaterHeatingSpeedData from this object's speeds + */ + bool removeSpeed(const HeatPumpAirToWaterHeatingSpeedData& speed); + + /* + * Remove the HeatPumpAirToWaterHeatingSpeedData at the given index (1-indexed) + */ + bool removeSpeed(unsigned index); + + // Convenience function to return the HeatPumpAirToWater object that reference this heating coil if any + boost::optional heatPumpAirToWater() const; + + // Autosize methods + boost::optional autosizedRatedAirFlowRate() const; + boost::optional autosizedRatedWaterFlowRate() const; + boost::optional autosizedRatedHeatingCapacity() const; // Convenience method + + //@} + protected: + /// @cond + using ImplType = detail::HeatPumpAirToWaterHeating_Impl; + + explicit HeatPumpAirToWaterHeating(std::shared_ptr impl); + + friend class detail::HeatPumpAirToWaterHeating_Impl; + friend class Model; + friend class IdfObject; + friend class openstudio::detail::IdfObject_Impl; + /// @endcond + private: + REGISTER_LOGGER("openstudio.model.HeatPumpAirToWaterHeating"); + }; + + /** \relates HeatPumpAirToWaterHeating*/ + using OptionalHeatPumpAirToWaterHeating = boost::optional; + + /** \relates HeatPumpAirToWaterHeating*/ + using HeatPumpAirToWaterHeatingVector = std::vector; + +} // namespace model +} // namespace openstudio + +#endif // MODEL_HEATPUMPAIRTOWATERHEATING_HPP diff --git a/src/model/HeatPumpAirToWaterHeatingSpeedData.cpp b/src/model/HeatPumpAirToWaterHeatingSpeedData.cpp new file mode 100644 index 0000000000..30ba81122c --- /dev/null +++ b/src/model/HeatPumpAirToWaterHeatingSpeedData.cpp @@ -0,0 +1,410 @@ +/*********************************************************************************************************************** +* OpenStudio(R), Copyright (c) Alliance for Sustainable Energy, LLC. +* See also https://openstudio.net/license +***********************************************************************************************************************/ + +#include "HeatPumpAirToWaterHeatingSpeedData.hpp" +#include "HeatPumpAirToWaterHeatingSpeedData_Impl.hpp" + +#include "HeatPumpAirToWaterHeating.hpp" +#include "HeatPumpAirToWaterHeating_Impl.hpp" + +#include "Model.hpp" +#include "Model_Impl.hpp" +#include "Curve.hpp" +#include "Curve_Impl.hpp" +#include "CurveBiquadratic.hpp" +#include "CurveQuadratic.hpp" + +#include "../utilities/core/Assert.hpp" + +#include +#include + +#include + +namespace openstudio { +namespace model { + + namespace detail { + + HeatPumpAirToWaterHeatingSpeedData_Impl::HeatPumpAirToWaterHeatingSpeedData_Impl(const IdfObject& idfObject, Model_Impl* model, bool keepHandle) + : ResourceObject_Impl(idfObject, model, keepHandle) { + OS_ASSERT(idfObject.iddObject().type() == HeatPumpAirToWaterHeatingSpeedData::iddObjectType()); + } + + HeatPumpAirToWaterHeatingSpeedData_Impl::HeatPumpAirToWaterHeatingSpeedData_Impl(const openstudio::detail::WorkspaceObject_Impl& other, + Model_Impl* model, bool keepHandle) + : ResourceObject_Impl(other, model, keepHandle) { + OS_ASSERT(other.iddObject().type() == HeatPumpAirToWaterHeatingSpeedData::iddObjectType()); + } + + HeatPumpAirToWaterHeatingSpeedData_Impl::HeatPumpAirToWaterHeatingSpeedData_Impl(const HeatPumpAirToWaterHeatingSpeedData_Impl& other, + Model_Impl* model, bool keepHandle) + : ResourceObject_Impl(other, model, keepHandle) {} + + const std::vector& HeatPumpAirToWaterHeatingSpeedData_Impl::outputVariableNames() const { + static std::vector result; + if (result.empty()) { + } + return result; + } + + IddObjectType HeatPumpAirToWaterHeatingSpeedData_Impl::iddObjectType() const { + return HeatPumpAirToWaterHeatingSpeedData::iddObjectType(); + } + + std::vector HeatPumpAirToWaterHeatingSpeedData_Impl::children() const { + std::vector children; + if (auto c_ = optionalNormalizedHeatingCapacityFunctionofTemperatureCurve()) { + children.push_back(*c_); + } + if (auto c_ = optionalHeatingEnergyInputRatioFunctionofTemperatureCurve()) { + children.push_back(*c_); + } + if (auto c_ = optionalHeatingEnergyInputRatioFunctionofPLRCurve()) { + children.push_back(*c_); + } + + return children; + } + + ModelObject HeatPumpAirToWaterHeatingSpeedData_Impl::clone(Model model) const { + // Don't clone the curves (listed as children) + return ModelObject_Impl::clone(model); // NOLINT(bugprone-parent-virtual-call) + } + + boost::optional HeatPumpAirToWaterHeatingSpeedData_Impl::ratedHeatingCapacity() const { + return getDouble(OS_HeatPump_AirToWater_Heating_SpeedDataFields::RatedHeatingCapacity, true); + } + + bool HeatPumpAirToWaterHeatingSpeedData_Impl::isRatedHeatingCapacityAutosized() const { + bool result = false; + boost::optional value = getString(OS_HeatPump_AirToWater_Heating_SpeedDataFields::RatedHeatingCapacity, true); + if (value) { + result = openstudio::istringEqual(value.get(), "autosize"); + } + return result; + } + + boost::optional HeatPumpAirToWaterHeatingSpeedData_Impl::autosizedRatedHeatingCapacity() { + boost::optional result; + auto awhp_hcs = heatPumpAirToWaterHeatings(); + if (awhp_hcs.empty()) { + return result; + } + size_t n_found = 0; + for (const auto& awhp_hc : awhp_hcs) { + // Check needed because could be the booster speed + if (awhp_hc.speeds().empty()) { + continue; + } + // It has to be the last speed + if (awhp_hc.speeds().back().handle() != this->handle()) { + continue; + } + if (n_found == 0) { + // Setting the first one only + result = awhp_hc.autosizedRatedHeatingCapacity(); + } + ++n_found; + } + if (n_found > 1) { + LOG(Warn, briefDescription() << " is used as the highest speed in multiple HeatPumpAirToWaterHeating objects, " + "returning the autosized value from the first one only."); + } + + return result; + } + + double HeatPumpAirToWaterHeatingSpeedData_Impl::ratedCOPforHeating() const { + boost::optional value = getDouble(OS_HeatPump_AirToWater_Heating_SpeedDataFields::RatedCOPforHeating, true); + OS_ASSERT(value); + return value.get(); + } + + Curve HeatPumpAirToWaterHeatingSpeedData_Impl::normalizedHeatingCapacityFunctionofTemperatureCurve() const { + boost::optional value = optionalNormalizedHeatingCapacityFunctionofTemperatureCurve(); + if (!value) { + LOG_AND_THROW(briefDescription() << " does not have an Normalized Heating Capacity Functionof Temperature Curve attached."); + } + return value.get(); + } + + Curve HeatPumpAirToWaterHeatingSpeedData_Impl::heatingEnergyInputRatioFunctionofTemperatureCurve() const { + boost::optional value = optionalHeatingEnergyInputRatioFunctionofTemperatureCurve(); + if (!value) { + LOG_AND_THROW(briefDescription() << " does not have an Heating Energy Input Ratio Functionof Temperature Curve attached."); + } + return value.get(); + } + + Curve HeatPumpAirToWaterHeatingSpeedData_Impl::heatingEnergyInputRatioFunctionofPLRCurve() const { + boost::optional value = optionalHeatingEnergyInputRatioFunctionofPLRCurve(); + if (!value) { + LOG_AND_THROW(briefDescription() << " does not have an Heating Energy Input Ratio Functionof PLRCurve attached."); + } + return value.get(); + } + + bool HeatPumpAirToWaterHeatingSpeedData_Impl::setRatedHeatingCapacity(double ratedHeatingCapacity) { + const bool result = setDouble(OS_HeatPump_AirToWater_Heating_SpeedDataFields::RatedHeatingCapacity, ratedHeatingCapacity); + return result; + } + + void HeatPumpAirToWaterHeatingSpeedData_Impl::autosizeRatedHeatingCapacity() { + const bool result = setString(OS_HeatPump_AirToWater_Heating_SpeedDataFields::RatedHeatingCapacity, "autosize"); + OS_ASSERT(result); + } + + bool HeatPumpAirToWaterHeatingSpeedData_Impl::setRatedCOPforHeating(double ratedCOPforHeating) { + const bool result = setDouble(OS_HeatPump_AirToWater_Heating_SpeedDataFields::RatedCOPforHeating, ratedCOPforHeating); + return result; + } + + bool HeatPumpAirToWaterHeatingSpeedData_Impl::setNormalizedHeatingCapacityFunctionofTemperatureCurve(const Curve& bivariateFunctions) { + const bool result = setPointer(OS_HeatPump_AirToWater_Heating_SpeedDataFields::NormalizedHeatingCapacityFunctionofTemperatureCurveName, + bivariateFunctions.handle()); + return result; + } + + bool HeatPumpAirToWaterHeatingSpeedData_Impl::setHeatingEnergyInputRatioFunctionofTemperatureCurve(const Curve& bivariateFunctions) { + const bool result = setPointer(OS_HeatPump_AirToWater_Heating_SpeedDataFields::HeatingEnergyInputRatioFunctionofTemperatureCurveName, + bivariateFunctions.handle()); + return result; + } + + bool HeatPumpAirToWaterHeatingSpeedData_Impl::setHeatingEnergyInputRatioFunctionofPLRCurve(const Curve& univariateFunctions) { + const bool result = + setPointer(OS_HeatPump_AirToWater_Heating_SpeedDataFields::HeatingEnergyInputRatioFunctionofPLRCurveName, univariateFunctions.handle()); + return result; + } + + void HeatPumpAirToWaterHeatingSpeedData_Impl::autosize() { + autosizeRatedHeatingCapacity(); + } + + void HeatPumpAirToWaterHeatingSpeedData_Impl::applySizingValues() { + if (boost::optional val_ = autosizedRatedHeatingCapacity()) { + setRatedHeatingCapacity(*val_); + } + } + + boost::optional HeatPumpAirToWaterHeatingSpeedData_Impl::optionalNormalizedHeatingCapacityFunctionofTemperatureCurve() const { + return getObject().getModelObjectTarget( + OS_HeatPump_AirToWater_Heating_SpeedDataFields::NormalizedHeatingCapacityFunctionofTemperatureCurveName); + } + + boost::optional HeatPumpAirToWaterHeatingSpeedData_Impl::optionalHeatingEnergyInputRatioFunctionofTemperatureCurve() const { + return getObject().getModelObjectTarget( + OS_HeatPump_AirToWater_Heating_SpeedDataFields::HeatingEnergyInputRatioFunctionofTemperatureCurveName); + } + + boost::optional HeatPumpAirToWaterHeatingSpeedData_Impl::optionalHeatingEnergyInputRatioFunctionofPLRCurve() const { + return getObject().getModelObjectTarget( + OS_HeatPump_AirToWater_Heating_SpeedDataFields::HeatingEnergyInputRatioFunctionofPLRCurveName); + } + + std::vector HeatPumpAirToWaterHeatingSpeedData_Impl::heatPumpAirToWaterHeatings() const { + std::vector result; + + const auto handle = this->handle(); + + for (const auto& awhp : model().getConcreteModelObjects()) { + if (awhp.boosterModeOnSpeed() && awhp.boosterModeOnSpeed()->handle() == handle) { + result.push_back(awhp); + } else { + const auto speeds = awhp.speeds(); + if (std::find_if(speeds.cbegin(), speeds.cend(), + [handle](const HeatPumpAirToWaterHeatingSpeedData& speed) { return speed.handle() == handle; }) + != speeds.end()) { + result.push_back(awhp); + } + } + } + return result; + } + + } // namespace detail + + HeatPumpAirToWaterHeatingSpeedData::HeatPumpAirToWaterHeatingSpeedData(const Model& model) + : ResourceObject(HeatPumpAirToWaterHeatingSpeedData::iddObjectType(), model) { + OS_ASSERT(getImpl()); + + autosizeRatedHeatingCapacity(); + setRatedCOPforHeating(3.0); + + bool ok = true; + { + CurveBiquadratic capFT(model); + capFT.setName(fmt::format("{} CapCurveFuncTempHT", this->nameString())); + capFT.setCoefficient1Constant(0.885); + capFT.setCoefficient2x(-0.00040); + capFT.setCoefficient3xPOW2(0.00019); + capFT.setCoefficient4y(0.0202); + capFT.setCoefficient5yPOW2(-0.0096); + capFT.setCoefficient6xTIMESY(0.000580); + capFT.setMinimumValueofx(30.0); + capFT.setMaximumValueofx(50.0); + capFT.setMinimumValueofy(-7.0); + capFT.setMaximumValueofy(20.0); + capFT.setMinimumCurveOutput(0.5); + capFT.setMaximumCurveOutput(1.3); + capFT.setInputUnitTypeforX("Temperature"); + capFT.setInputUnitTypeforY("Temperature"); + capFT.setOutputUnitType("Dimensionless"); + + ok = setNormalizedHeatingCapacityFunctionofTemperatureCurve(capFT); + OS_ASSERT(ok); + } + + { + CurveBiquadratic eirFT(model); + eirFT.setName(fmt::format("{} EIRCurveFuncTempHT", this->nameString())); + eirFT.setCoefficient1Constant(1.055); + eirFT.setCoefficient2x(0.00035); + eirFT.setCoefficient3xPOW2(-0.00027); + eirFT.setCoefficient4y(-0.0108); + eirFT.setCoefficient5yPOW2(0.0145); + eirFT.setCoefficient6xTIMESY(-0.000230); + eirFT.setMinimumValueofx(30.0); + eirFT.setMaximumValueofx(50.0); + eirFT.setMinimumValueofy(-7.0); + eirFT.setMaximumValueofy(20.0); + eirFT.setMinimumCurveOutput(0.7); + eirFT.setMaximumCurveOutput(1.5); + eirFT.setInputUnitTypeforX("Temperature"); + eirFT.setInputUnitTypeforY("Temperature"); + eirFT.setOutputUnitType("Dimensionless"); + + ok = setHeatingEnergyInputRatioFunctionofTemperatureCurve(eirFT); + OS_ASSERT(ok); + } + + { + CurveQuadratic eirfPLR(model); + eirfPLR.setName(fmt::format("{} EIRCurveFuncPLR", this->nameString())); + eirfPLR.setCoefficient1Constant(1.0); + eirfPLR.setCoefficient2x(0.0); + eirfPLR.setCoefficient3xPOW2(0.0); + eirfPLR.setMinimumValueofx(0.0); + eirfPLR.setMaximumValueofx(1.0); + eirfPLR.setMinimumCurveOutput(0.0); + eirfPLR.setMaximumCurveOutput(1.0); + eirfPLR.setInputUnitTypeforX("Dimensionless"); + eirfPLR.setOutputUnitType("Dimensionless"); + ok = setHeatingEnergyInputRatioFunctionofPLRCurve(eirfPLR); + OS_ASSERT(ok); + } + } + + HeatPumpAirToWaterHeatingSpeedData::HeatPumpAirToWaterHeatingSpeedData(const Model& model, + const Curve& normalizedHeatingCapacityFunctionofTemperatureCurve, + const Curve& heatingEnergyInputRatioFunctionofTemperatureCurve, + const Curve& heatingEnergyInputRatioFunctionofPLRCurve) + : ResourceObject(HeatPumpAirToWaterHeatingSpeedData::iddObjectType(), model) { + + auto impl = getImpl(); + OS_ASSERT(impl); + + autosizeRatedHeatingCapacity(); + setRatedCOPforHeating(3.0); + + bool ok = setNormalizedHeatingCapacityFunctionofTemperatureCurve(normalizedHeatingCapacityFunctionofTemperatureCurve); + if (!ok) { + // We don't call remove() (which would do ResourceObject::remove()) because that would try to remove the curves too if they aren't being used, + // so we call ModelObject::remove() directly + impl->detail::ModelObject_Impl::remove(); + LOG_AND_THROW("Unable to set " << briefDescription() << "'s Capacity Curve to " + << normalizedHeatingCapacityFunctionofTemperatureCurve.briefDescription() << "."); + } + ok = setHeatingEnergyInputRatioFunctionofTemperatureCurve(heatingEnergyInputRatioFunctionofTemperatureCurve); + if (!ok) { + impl->detail::ModelObject_Impl::remove(); + LOG_AND_THROW("Unable to set " << briefDescription() << "'s EIRfT Curve to " + << heatingEnergyInputRatioFunctionofTemperatureCurve.briefDescription() << "."); + } + ok = setHeatingEnergyInputRatioFunctionofPLRCurve(heatingEnergyInputRatioFunctionofPLRCurve); + if (!ok) { + impl->detail::ModelObject_Impl::remove(); + LOG_AND_THROW("Unable to set " << briefDescription() << "'s EIRfPLR Curve to " << heatingEnergyInputRatioFunctionofPLRCurve.briefDescription() + << "."); + } + } + + IddObjectType HeatPumpAirToWaterHeatingSpeedData::iddObjectType() { + return {IddObjectType::OS_HeatPump_AirToWater_Heating_SpeedData}; + } + + boost::optional HeatPumpAirToWaterHeatingSpeedData::ratedHeatingCapacity() const { + return getImpl()->ratedHeatingCapacity(); + } + + bool HeatPumpAirToWaterHeatingSpeedData::isRatedHeatingCapacityAutosized() const { + return getImpl()->isRatedHeatingCapacityAutosized(); + } + + boost::optional HeatPumpAirToWaterHeatingSpeedData::autosizedRatedHeatingCapacity() { + return getImpl()->autosizedRatedHeatingCapacity(); + } + + double HeatPumpAirToWaterHeatingSpeedData::ratedCOPforHeating() const { + return getImpl()->ratedCOPforHeating(); + } + + Curve HeatPumpAirToWaterHeatingSpeedData::normalizedHeatingCapacityFunctionofTemperatureCurve() const { + return getImpl()->normalizedHeatingCapacityFunctionofTemperatureCurve(); + } + + Curve HeatPumpAirToWaterHeatingSpeedData::heatingEnergyInputRatioFunctionofTemperatureCurve() const { + return getImpl()->heatingEnergyInputRatioFunctionofTemperatureCurve(); + } + + Curve HeatPumpAirToWaterHeatingSpeedData::heatingEnergyInputRatioFunctionofPLRCurve() const { + return getImpl()->heatingEnergyInputRatioFunctionofPLRCurve(); + } + + bool HeatPumpAirToWaterHeatingSpeedData::setRatedHeatingCapacity(double ratedHeatingCapacity) { + return getImpl()->setRatedHeatingCapacity(ratedHeatingCapacity); + } + + void HeatPumpAirToWaterHeatingSpeedData::autosizeRatedHeatingCapacity() { + getImpl()->autosizeRatedHeatingCapacity(); + } + + bool HeatPumpAirToWaterHeatingSpeedData::setRatedCOPforHeating(double ratedCOPforHeating) { + return getImpl()->setRatedCOPforHeating(ratedCOPforHeating); + } + + bool HeatPumpAirToWaterHeatingSpeedData::setNormalizedHeatingCapacityFunctionofTemperatureCurve(const Curve& bivariateFunctions) { + return getImpl()->setNormalizedHeatingCapacityFunctionofTemperatureCurve(bivariateFunctions); + } + + bool HeatPumpAirToWaterHeatingSpeedData::setHeatingEnergyInputRatioFunctionofTemperatureCurve(const Curve& bivariateFunctions) { + return getImpl()->setHeatingEnergyInputRatioFunctionofTemperatureCurve(bivariateFunctions); + } + + bool HeatPumpAirToWaterHeatingSpeedData::setHeatingEnergyInputRatioFunctionofPLRCurve(const Curve& univariateFunctions) { + return getImpl()->setHeatingEnergyInputRatioFunctionofPLRCurve(univariateFunctions); + } + + /// @cond + HeatPumpAirToWaterHeatingSpeedData::HeatPumpAirToWaterHeatingSpeedData(std::shared_ptr impl) + : ResourceObject(std::move(impl)) {} + /// @endcond + + void HeatPumpAirToWaterHeatingSpeedData::autosize() { + getImpl()->autosizeRatedHeatingCapacity(); + } + + // TODO: needed? + // void HeatPumpAirToWaterHeatingSpeedData::applySizingValues() { + // getImpl()->applySizingValues(); + // } + + std::vector HeatPumpAirToWaterHeatingSpeedData::heatPumpAirToWaterHeatings() const { + return getImpl()->heatPumpAirToWaterHeatings(); + } + +} // namespace model +} // namespace openstudio diff --git a/src/model/HeatPumpAirToWaterHeatingSpeedData.hpp b/src/model/HeatPumpAirToWaterHeatingSpeedData.hpp new file mode 100644 index 0000000000..86ede3171f --- /dev/null +++ b/src/model/HeatPumpAirToWaterHeatingSpeedData.hpp @@ -0,0 +1,116 @@ +/*********************************************************************************************************************** +* OpenStudio(R), Copyright (c) Alliance for Sustainable Energy, LLC. +* See also https://openstudio.net/license +***********************************************************************************************************************/ + +#ifndef MODEL_HEATPUMPAIRTOWATERHEATINGSPEEDDATA_HPP +#define MODEL_HEATPUMPAIRTOWATERHEATINGSPEEDDATA_HPP + +#include "ModelAPI.hpp" +#include "ResourceObject.hpp" + +namespace openstudio { + +namespace model { + + class Curve; + class HeatPumpAirToWaterHeating; + + namespace detail { + + class HeatPumpAirToWaterHeatingSpeedData_Impl; + + } // namespace detail + + /** HeatPumpAirToWaterHeatingSpeedData is a ResourceObject that wraps the OpenStudio IDD object 'OS:HeatPump:AirToWater:Heating:SpeedData'. */ + class MODEL_API HeatPumpAirToWaterHeatingSpeedData : public ResourceObject + { + public: + /** @name Constructors and Destructors */ + //@{ + + explicit HeatPumpAirToWaterHeatingSpeedData(const Model& model); + + explicit HeatPumpAirToWaterHeatingSpeedData(const Model& model, const Curve& normalizedHeatingCapacityFunctionofTemperatureCurve, + const Curve& heatingEnergyInputRatioFunctionofTemperatureCurve, + const Curve& heatingEnergyInputRatioFunctionofPLRCurve); + + virtual ~HeatPumpAirToWaterHeatingSpeedData() = default; + // Default the copy and move operators because the virtual dtor is explicit + HeatPumpAirToWaterHeatingSpeedData(const HeatPumpAirToWaterHeatingSpeedData& other) = default; + HeatPumpAirToWaterHeatingSpeedData(HeatPumpAirToWaterHeatingSpeedData&& other) = default; + HeatPumpAirToWaterHeatingSpeedData& operator=(const HeatPumpAirToWaterHeatingSpeedData&) = default; + HeatPumpAirToWaterHeatingSpeedData& operator=(HeatPumpAirToWaterHeatingSpeedData&&) = default; + + //@} + + static IddObjectType iddObjectType(); + + /** @name Getters */ + //@{ + + boost::optional ratedHeatingCapacity() const; + + bool isRatedHeatingCapacityAutosized() const; + + double ratedCOPforHeating() const; + + Curve normalizedHeatingCapacityFunctionofTemperatureCurve() const; + + Curve heatingEnergyInputRatioFunctionofTemperatureCurve() const; + + Curve heatingEnergyInputRatioFunctionofPLRCurve() const; + + //@} + /** @name Setters */ + //@{ + + bool setRatedHeatingCapacity(double ratedHeatingCapacity); + + void autosizeRatedHeatingCapacity(); + + bool setRatedCOPforHeating(double ratedCOPforHeating); + + bool setNormalizedHeatingCapacityFunctionofTemperatureCurve(const Curve& bivariateFunctions); + + bool setHeatingEnergyInputRatioFunctionofTemperatureCurve(const Curve& bivariateFunctions); + + bool setHeatingEnergyInputRatioFunctionofPLRCurve(const Curve& univariateFunctions); + + //@} + /** @name Other */ + //@{ + + // convenience function that returns a vector of HeatPumpAirToWaterHeating that use this as their Speed Data (either regular speed or Booster) + std::vector heatPumpAirToWaterHeatings() const; + + boost::optional autosizedRatedHeatingCapacity(); + + void autosize(); + + //@} + protected: + /// @cond + using ImplType = detail::HeatPumpAirToWaterHeatingSpeedData_Impl; + + explicit HeatPumpAirToWaterHeatingSpeedData(std::shared_ptr impl); + + friend class detail::HeatPumpAirToWaterHeatingSpeedData_Impl; + friend class Model; + friend class IdfObject; + friend class openstudio::detail::IdfObject_Impl; + /// @endcond + private: + REGISTER_LOGGER("openstudio.model.HeatPumpAirToWaterHeatingSpeedData"); + }; + + /** \relates HeatPumpAirToWaterHeatingSpeedData*/ + using OptionalHeatPumpAirToWaterHeatingSpeedData = boost::optional; + + /** \relates HeatPumpAirToWaterHeatingSpeedData*/ + using HeatPumpAirToWaterHeatingSpeedDataVector = std::vector; + +} // namespace model +} // namespace openstudio + +#endif // MODEL_HEATPUMPAIRTOWATERHEATINGSPEEDDATA_HPP diff --git a/src/model/HeatPumpAirToWaterHeatingSpeedData_Impl.hpp b/src/model/HeatPumpAirToWaterHeatingSpeedData_Impl.hpp new file mode 100644 index 0000000000..2999b2497b --- /dev/null +++ b/src/model/HeatPumpAirToWaterHeatingSpeedData_Impl.hpp @@ -0,0 +1,112 @@ +/*********************************************************************************************************************** +* OpenStudio(R), Copyright (c) Alliance for Sustainable Energy, LLC. +* See also https://openstudio.net/license +***********************************************************************************************************************/ + +#ifndef MODEL_HEATPUMPAIRTOWATERHEATINGSPEEDDATA_IMPL_HPP +#define MODEL_HEATPUMPAIRTOWATERHEATINGSPEEDDATA_IMPL_HPP + +#include "ModelAPI.hpp" +#include "ResourceObject_Impl.hpp" + +namespace openstudio { +namespace model { + + class Curve; + class HeatPumpAirToWaterHeating; + + namespace detail { + + /** HeatPumpAirToWaterHeatingSpeedData_Impl is a ResourceObject_Impl that is the implementation class for HeatPumpAirToWaterHeatingSpeedData.*/ + class MODEL_API HeatPumpAirToWaterHeatingSpeedData_Impl : public ResourceObject_Impl + { + public: + /** @name Constructors and Destructors */ + //@{ + + HeatPumpAirToWaterHeatingSpeedData_Impl(const IdfObject& idfObject, Model_Impl* model, bool keepHandle); + + HeatPumpAirToWaterHeatingSpeedData_Impl(const openstudio::detail::WorkspaceObject_Impl& other, Model_Impl* model, bool keepHandle); + + HeatPumpAirToWaterHeatingSpeedData_Impl(const HeatPumpAirToWaterHeatingSpeedData_Impl& other, Model_Impl* model, bool keepHandle); + + virtual ~HeatPumpAirToWaterHeatingSpeedData_Impl() = default; + + //@} + /** @name Virtual Methods */ + //@{ + + virtual const std::vector& outputVariableNames() const override; + + virtual IddObjectType iddObjectType() const override; + + // Overriding clone and children here because we want to try to remove the Curves (if they aren't used by something else) + // So we list them as children. But ResourceObject_Impl::clone would also clone them, so we override clone to call ModelObject_Impl::clone + virtual ModelObject clone(Model model) const override; + + virtual std::vector children() const override; + + //@} + /** @name Getters */ + //@{ + + boost::optional ratedHeatingCapacity() const; + + bool isRatedHeatingCapacityAutosized() const; + + boost::optional autosizedRatedHeatingCapacity(); + + double ratedCOPforHeating() const; + + Curve normalizedHeatingCapacityFunctionofTemperatureCurve() const; + + Curve heatingEnergyInputRatioFunctionofTemperatureCurve() const; + + Curve heatingEnergyInputRatioFunctionofPLRCurve() const; + + //@} + /** @name Setters */ + //@{ + + bool setRatedHeatingCapacity(double ratedHeatingCapacity); + + void autosizeRatedHeatingCapacity(); + + bool setRatedCOPforHeating(double ratedCOPforHeating); + + bool setNormalizedHeatingCapacityFunctionofTemperatureCurve(const Curve& bivariateFunctions); + + bool setHeatingEnergyInputRatioFunctionofTemperatureCurve(const Curve& bivariateFunctions); + + bool setHeatingEnergyInputRatioFunctionofPLRCurve(const Curve& univariateFunctions); + + //@} + /** @name Other */ + //@{ + + // convenience function that returns a vector of HeatPumpAirToWaterHeating that use this as their Speed Data (either regular speed or Booster) + std::vector heatPumpAirToWaterHeatings() const; + + void autosize(); + + void applySizingValues(); + + //@} + protected: + private: + REGISTER_LOGGER("openstudio.model.HeatPumpAirToWaterHeatingSpeedData"); + + // Optional getters for use by methods like children() so can remove() if the constructor fails. + // There are other ways for the public versions of these getters to fail--perhaps all required + // objects should be returned as boost::optionals + boost::optional optionalNormalizedHeatingCapacityFunctionofTemperatureCurve() const; + boost::optional optionalHeatingEnergyInputRatioFunctionofTemperatureCurve() const; + boost::optional optionalHeatingEnergyInputRatioFunctionofPLRCurve() const; + }; + + } // namespace detail + +} // namespace model +} // namespace openstudio + +#endif // MODEL_HEATPUMPAIRTOWATERHEATINGSPEEDDATA_IMPL_HPP diff --git a/src/model/HeatPumpAirToWaterHeating_Impl.hpp b/src/model/HeatPumpAirToWaterHeating_Impl.hpp new file mode 100644 index 0000000000..af65ae3a66 --- /dev/null +++ b/src/model/HeatPumpAirToWaterHeating_Impl.hpp @@ -0,0 +1,188 @@ +/*********************************************************************************************************************** +* OpenStudio(R), Copyright (c) Alliance for Sustainable Energy, LLC. +* See also https://openstudio.net/license +***********************************************************************************************************************/ + +#ifndef MODEL_HEATPUMPAIRTOWATERHEATING_IMPL_HPP +#define MODEL_HEATPUMPAIRTOWATERHEATING_IMPL_HPP + +#include "ModelAPI.hpp" +#include "StraightComponent_Impl.hpp" + +namespace openstudio { +namespace model { + + class Curve; + class HeatPumpAirToWater; + class HeatPumpAirToWaterHeatingSpeedData; + class ModelObjectList; + class Node; + class Schedule; + + namespace detail { + + /** HeatPumpAirToWaterHeating_Impl is a StraightComponent_Impl that is the implementation class for HeatPumpAirToWaterHeating.*/ + class MODEL_API HeatPumpAirToWaterHeating_Impl : public StraightComponent_Impl + { + public: + /** @name Constructors and Destructors */ + //@{ + + HeatPumpAirToWaterHeating_Impl(const IdfObject& idfObject, Model_Impl* model, bool keepHandle); + + HeatPumpAirToWaterHeating_Impl(const openstudio::detail::WorkspaceObject_Impl& other, Model_Impl* model, bool keepHandle); + + HeatPumpAirToWaterHeating_Impl(const HeatPumpAirToWaterHeating_Impl& other, Model_Impl* model, bool keepHandle); + + virtual ~HeatPumpAirToWaterHeating_Impl() = default; + + //@} + /** @name Virtual Methods */ + //@{ + + virtual const std::vector& outputVariableNames() const override; + + virtual IddObjectType iddObjectType() const override; + + virtual std::vector getScheduleTypeKeys(const Schedule& schedule) const override; + + virtual ModelObject clone(Model model) const override; + + virtual std::vector children() const override; + // virtual std::vector allowableChildTypes() const override; + + virtual std::vector remove() override; + + // Overrides from StraightComponent + virtual unsigned inletPort() const override; + virtual unsigned outletPort() const override; + + virtual bool addToNode(Node& node) override; + + virtual boost::optional containingHVACComponent() const override; + // virtual boost::optional containingZoneHVACComponent() const override; + // virtual boost::optional containingStraightComponent() const override; + + virtual void autosize() override; + + virtual void applySizingValues() override; + + virtual ComponentType componentType() const override; + virtual std::vector coolingFuelTypes() const override; + virtual std::vector heatingFuelTypes() const override; + virtual std::vector appGHeatingFuelTypes() const override; + + //@} + /** @name Getters */ + //@{ + + Schedule availabilitySchedule() const; + + double ratedInletAirTemperature() const; + + boost::optional ratedAirFlowRate() const; + + bool isRatedAirFlowRateAutosized() const; + + double ratedLeavingWaterTemperature() const; + + boost::optional ratedWaterFlowRate() const; + + bool isRatedWaterFlowRateAutosized() const; + + double minimumOutdoorAirTemperature() const; + + double maximumOutdoorAirTemperature() const; + + boost::optional minimumLeavingWaterTemperatureCurve() const; + + boost::optional maximumLeavingWaterTemperatureCurve() const; + + double sizingFactor() const; + + boost::optional boosterModeOnSpeed() const; + + //@} + /** @name Setters */ + //@{ + + bool setAvailabilitySchedule(Schedule& availabilitySchedule); + + bool setRatedInletAirTemperature(double ratedInletAirTemperature); + + bool setRatedAirFlowRate(double ratedAirFlowRate); + + void autosizeRatedAirFlowRate(); + + bool setRatedLeavingWaterTemperature(double ratedLeavingWaterTemperature); + + bool setRatedWaterFlowRate(double ratedWaterFlowRate); + + void autosizeRatedWaterFlowRate(); + + bool setMinimumOutdoorAirTemperature(double minimumOutdoorAirTemperature); + + bool setMaximumOutdoorAirTemperature(double maximumOutdoorAirTemperature); + + bool setMinimumLeavingWaterTemperatureCurve(const Curve& minimumLeavingWaterTemperatureCurve); + + void resetMinimumLeavingWaterTemperatureCurve(); + + bool setMaximumLeavingWaterTemperatureCurve(const Curve& maximumLeavingWaterTemperatureCurve); + + void resetMaximumLeavingWaterTemperatureCurve(); + + bool setSizingFactor(double sizingFactor); + + bool setBoosterModeOnSpeed(const HeatPumpAirToWaterHeatingSpeedData& boosterModeOnSpeed); + + void resetBoosterModeOnSpeed(); + + //@} + /** @name Other */ + //@{ + + bool setSpeedDataList(const ModelObjectList& speedDataList); + void resetSpeedDataList(); + ModelObjectList speedDataList() const; + + std::vector speeds() const; + unsigned numberOfSpeeds() const; + boost::optional speedIndex(const HeatPumpAirToWaterHeatingSpeedData& speed) const; + + bool addSpeed(const HeatPumpAirToWaterHeatingSpeedData& speed); + bool addSpeed(const HeatPumpAirToWaterHeatingSpeedData& speed, unsigned index); + bool setSpeedIndex(const HeatPumpAirToWaterHeatingSpeedData& speed, unsigned index); + bool setSpeeds(const std::vector& speeds); + bool removeSpeed(const HeatPumpAirToWaterHeatingSpeedData& speed); + bool removeSpeed(unsigned index); + void removeAllSpeeds(); + + // Convenience function to return the HeatPumpAirToWater object that reference this heating coil if any + boost::optional heatPumpAirToWater() const; + + // Autosize methods + + boost::optional autosizedRatedAirFlowRate() const; + boost::optional autosizedRatedWaterFlowRate() const; + boost::optional autosizedRatedHeatingCapacity() const; // Convenience method + + //@} + protected: + private: + REGISTER_LOGGER("openstudio.model.HeatPumpAirToWaterHeating"); + + // TODO: Check the return types of these methods. + // Optional getters for use by methods like children() so can remove() if the constructor fails. + // There are other ways for the public versions of these getters to fail--perhaps all required + // objects should be returned as boost::optionals + boost::optional optionalAvailabilitySchedule() const; + boost::optional optionalSpeedDataList() const; + }; + + } // namespace detail + +} // namespace model +} // namespace openstudio + +#endif // MODEL_HEATPUMPAIRTOWATERHEATING_IMPL_HPP diff --git a/src/model/HeatPumpAirToWater_Impl.hpp b/src/model/HeatPumpAirToWater_Impl.hpp new file mode 100644 index 0000000000..81f8bd4ad5 --- /dev/null +++ b/src/model/HeatPumpAirToWater_Impl.hpp @@ -0,0 +1,194 @@ +/*********************************************************************************************************************** +* OpenStudio(R), Copyright (c) Alliance for Sustainable Energy, LLC. +* See also https://openstudio.net/license +***********************************************************************************************************************/ + +#ifndef MODEL_HEATPUMPAIRTOWATER_IMPL_HPP +#define MODEL_HEATPUMPAIRTOWATER_IMPL_HPP + +#include "ModelAPI.hpp" +#include "StraightComponent_Impl.hpp" + +namespace openstudio { +namespace model { + + class Schedule; + class Curve; + class HeatPumpAirToWaterHeating; + class HeatPumpAirToWaterCooling; + class PlantLoop; + + namespace detail { + + /** HeatPumpAirToWater_Impl is a StraightComponent_Impl that is the implementation class for HeatPumpAirToWater.*/ + class MODEL_API HeatPumpAirToWater_Impl : public StraightComponent_Impl + { + public: + /** @name Constructors and Destructors */ + //@{ + + HeatPumpAirToWater_Impl(const IdfObject& idfObject, Model_Impl* model, bool keepHandle); + + HeatPumpAirToWater_Impl(const openstudio::detail::WorkspaceObject_Impl& other, Model_Impl* model, bool keepHandle); + + HeatPumpAirToWater_Impl(const HeatPumpAirToWater_Impl& other, Model_Impl* model, bool keepHandle); + + virtual ~HeatPumpAirToWater_Impl() = default; + + //@} + /** @name Virtual Methods */ + //@{ + + virtual const std::vector& outputVariableNames() const override; + + virtual IddObjectType iddObjectType() const override; + + virtual std::vector getScheduleTypeKeys(const Schedule& schedule) const override; + + // base is StraightComponent + virtual ModelObject clone(Model model) const override; + + virtual std::vector children() const override; + // virtual std::vector allowableChildTypes() const override; + + virtual std::vector remove() override; + + // Overrides from StraightComponent + virtual unsigned inletPort() const override; + virtual unsigned outletPort() const override; + + virtual bool addToNode(Node& node) override; + + virtual void autosize() override; + + virtual void applySizingValues() override; + + virtual ComponentType componentType() const override; + virtual std::vector coolingFuelTypes() const override; + virtual std::vector heatingFuelTypes() const override; + virtual std::vector appGHeatingFuelTypes() const override; + + virtual std::vector emsActuatorNames() const override; + virtual std::vector emsInternalVariableNames() const override; + + //@} + /** @name Getters */ + //@{ + + std::string operatingModeControlMethod() const; + + std::string operatingModeControlOptionforMultipleUnit() const; + + boost::optional operatingModeControlSchedule() const; + + double minimumPartLoadRatio() const; + + boost::optional airInletNodeName() const; + + boost::optional airOutletNodeName() const; + + double maximumOutdoorDryBulbTemperatureForDefrostOperation() const; + + std::string heatPumpDefrostControl() const; + + double heatPumpDefrostTimePeriodFraction() const; + + double resistiveDefrostHeaterCapacity() const; + + boost::optional defrostEnergyInputRatioFunctionofTemperatureCurve() const; + + int heatPumpMultiplier() const; + + std::string controlType() const; + + double crankcaseHeaterCapacity() const; + + boost::optional crankcaseHeaterCapacityFunctionofTemperatureCurve() const; + + double maximumAmbientTemperatureforCrankcaseHeaterOperation() const; + + boost::optional heatingOperationMode() const; + + boost::optional coolingOperationMode() const; + + //@} + /** @name Setters */ + //@{ + + bool setOperatingModeControlMethod(const std::string& operatingModeControlMethod); + + bool setOperatingModeControlOptionforMultipleUnit(const std::string& operatingModeControlOptionforMultipleUnit); + + bool setOperatingModeControlSchedule(Schedule& operatingModeControlSchedule); + + void resetOperatingModeControlSchedule(); + + bool setMinimumPartLoadRatio(double minimumPartLoadRatio); + + bool setAirInletNodeName(const std::string& airInletNodeName); + + void resetAirInletNodeName(); + + bool setAirOutletNodeName(const std::string& airOutletNodeName); + + void resetAirOutletNodeName(); + + bool setMaximumOutdoorDryBulbTemperatureForDefrostOperation(double maximumOutdoorDryBulbTemperatureForDefrostOperation); + + bool setHeatPumpDefrostControl(const std::string& heatPumpDefrostControl); + + bool setHeatPumpDefrostTimePeriodFraction(double heatPumpDefrostTimePeriodFraction); + + bool setResistiveDefrostHeaterCapacity(double resistiveDefrostHeaterCapacity); + + bool setDefrostEnergyInputRatioFunctionofTemperatureCurve(const Curve& defrostEnergyInputRatioFunctionofTemperatureCurve); + + void resetDefrostEnergyInputRatioFunctionofTemperatureCurve(); + + bool setHeatPumpMultiplier(int heatPumpMultiplier); + + bool setControlType(const std::string& controlType); + + bool setCrankcaseHeaterCapacity(double crankcaseHeaterCapacity); + + bool setCrankcaseHeaterCapacityFunctionofTemperatureCurve(const Curve& crankcaseHeaterCapacityFunctionofTemperatureCurve); + + void resetCrankcaseHeaterCapacityFunctionofTemperatureCurve(); + + bool setMaximumAmbientTemperatureforCrankcaseHeaterOperation(double maximumAmbientTemperatureforCrankcaseHeaterOperation); + + bool setHeatingOperationMode(const HeatPumpAirToWaterHeating& heatingOperationMode); + + void resetHeatingOperationMode(); + + bool setCoolingOperationMode(const HeatPumpAirToWaterCooling& coolingOperationMode); + + void resetCoolingOperationMode(); + + //@} + /** @name Other */ + //@{ + + boost::optional coolingLoop() const; + boost::optional heatingLoop() const; + + // Autosize methods + boost::optional autosizedRatedAirFlowRateinHeatingMode() const; + boost::optional autosizedRatedWaterFlowRateinHeatingMode() const; + boost::optional autosizedRatedAirFlowRateinCoolingMode() const; + boost::optional autosizedRatedWaterFlowRateinCoolingMode() const; + boost::optional autosizedRatedHeatingCapacity() const; + boost::optional autosizedRatedCoolingCapacity() const; + + //@} + protected: + private: + REGISTER_LOGGER("openstudio.model.HeatPumpAirToWater"); + }; + + } // namespace detail + +} // namespace model +} // namespace openstudio + +#endif // MODEL_HEATPUMPAIRTOWATER_IMPL_HPP diff --git a/src/model/Model.cpp b/src/model/Model.cpp index 657761bf56..780643fdc2 100644 --- a/src/model/Model.cpp +++ b/src/model/Model.cpp @@ -4188,6 +4188,11 @@ namespace model { REGISTER_CONSTRUCTOR(HeatExchangerFluidToFluid); REGISTER_CONSTRUCTOR(HeatPumpAirToWaterFuelFiredHeating); REGISTER_CONSTRUCTOR(HeatPumpAirToWaterFuelFiredCooling); + REGISTER_CONSTRUCTOR(HeatPumpAirToWater); + REGISTER_CONSTRUCTOR(HeatPumpAirToWaterCooling); + REGISTER_CONSTRUCTOR(HeatPumpAirToWaterCoolingSpeedData); + REGISTER_CONSTRUCTOR(HeatPumpAirToWaterHeating); + REGISTER_CONSTRUCTOR(HeatPumpAirToWaterHeatingSpeedData); REGISTER_CONSTRUCTOR(HeatPumpWaterToWaterEquationFitCooling); REGISTER_CONSTRUCTOR(HeatPumpWaterToWaterEquationFitHeating); REGISTER_CONSTRUCTOR(HeatPumpPlantLoopEIRCooling); @@ -4767,6 +4772,11 @@ namespace model { REGISTER_COPYCONSTRUCTORS(HeatExchangerFluidToFluid); REGISTER_COPYCONSTRUCTORS(HeatPumpAirToWaterFuelFiredHeating); REGISTER_COPYCONSTRUCTORS(HeatPumpAirToWaterFuelFiredCooling); + REGISTER_COPYCONSTRUCTORS(HeatPumpAirToWater); + REGISTER_COPYCONSTRUCTORS(HeatPumpAirToWaterCooling); + REGISTER_COPYCONSTRUCTORS(HeatPumpAirToWaterCoolingSpeedData); + REGISTER_COPYCONSTRUCTORS(HeatPumpAirToWaterHeating); + REGISTER_COPYCONSTRUCTORS(HeatPumpAirToWaterHeatingSpeedData); REGISTER_COPYCONSTRUCTORS(HeatPumpWaterToWaterEquationFitCooling); REGISTER_COPYCONSTRUCTORS(HeatPumpWaterToWaterEquationFitHeating); REGISTER_COPYCONSTRUCTORS(HeatPumpPlantLoopEIRCooling); diff --git a/src/model/ModelHVAC.i b/src/model/ModelHVAC.i index 356ce9c494..c8145dd8f2 100644 --- a/src/model/ModelHVAC.i +++ b/src/model/ModelHVAC.i @@ -224,6 +224,11 @@ MODELOBJECT_TEMPLATES(HeatPumpWaterToWaterEquationFitCooling); MODELOBJECT_TEMPLATES(HeatPumpWaterToWaterEquationFitHeating); MODELOBJECT_TEMPLATES(HeatPumpPlantLoopEIRCooling); MODELOBJECT_TEMPLATES(HeatPumpPlantLoopEIRHeating); +MODELOBJECT_TEMPLATES(HeatPumpAirToWater) +MODELOBJECT_TEMPLATES(HeatPumpAirToWaterCooling); +MODELOBJECT_TEMPLATES(HeatPumpAirToWaterCoolingSpeedData); +MODELOBJECT_TEMPLATES(HeatPumpAirToWaterHeating); +MODELOBJECT_TEMPLATES(HeatPumpAirToWaterHeatingSpeedData); MODELOBJECT_TEMPLATES(ThermalStorageChilledWaterStratified); MODELOBJECT_TEMPLATES(ChillerAbsorptionIndirect); MODELOBJECT_TEMPLATES(ChillerAbsorption); @@ -354,6 +359,11 @@ SWIG_MODELOBJECT(HeatPumpWaterToWaterEquationFitCooling,1); SWIG_MODELOBJECT(HeatPumpWaterToWaterEquationFitHeating,1); SWIG_MODELOBJECT(HeatPumpPlantLoopEIRCooling,1); SWIG_MODELOBJECT(HeatPumpPlantLoopEIRHeating,1); +SWIG_MODELOBJECT(HeatPumpAirToWater, 1); +SWIG_MODELOBJECT(HeatPumpAirToWaterCooling, 1); +SWIG_MODELOBJECT(HeatPumpAirToWaterCoolingSpeedData, 1); +SWIG_MODELOBJECT(HeatPumpAirToWaterHeating,1); +SWIG_MODELOBJECT(HeatPumpAirToWaterHeatingSpeedData, 1); SWIG_MODELOBJECT(ThermalStorageChilledWaterStratified, 1); SWIG_MODELOBJECT(ChillerAbsorptionIndirect, 1); SWIG_MODELOBJECT(ChillerAbsorption, 1); diff --git a/src/model/ScheduleTypeRegistry.cpp b/src/model/ScheduleTypeRegistry.cpp index c3fad35a17..6c27110df7 100644 --- a/src/model/ScheduleTypeRegistry.cpp +++ b/src/model/ScheduleTypeRegistry.cpp @@ -333,6 +333,9 @@ namespace model { {"HeatExchangerAirToAirSensibleAndLatent", "Availability", "availabilitySchedule", false, "Availability", 0.0, 1.0}, {"HeatExchangerDesiccantBalancedFlow", "Availability", "availabilitySchedule", false, "Availability", 0.0, 1.0}, {"HeatExchangerFluidToFluid", "Availability", "availabilitySchedule", false, "Availability", 0.0, 1.0}, + {"HeatPumpAirToWater", "Operating Mode Control", "operatingModeControlSchedule", false, "", 0.0, 2.0}, + {"HeatPumpAirToWaterCooling", "Availability", "availabilitySchedule", false, "Availability", 0.0, 1.0}, + {"HeatPumpAirToWaterHeating", "Availability", "availabilitySchedule", false, "Availability", 0.0, 1.0}, {"HumidifierSteamElectric", "Availability", "availabilitySchedule", false, "Availability", 0.0, 1.0}, {"HumidifierSteamGas", "Availability", "availabilitySchedule", false, "Availability", 0.0, 1.0}, {"HotWaterEquipment", "Hot Water Equipment", "schedule", true, "", 0.0, 1.0}, diff --git a/src/model/test/HeatPumpAirToWaterCoolingSpeedData_GTest.cpp b/src/model/test/HeatPumpAirToWaterCoolingSpeedData_GTest.cpp new file mode 100644 index 0000000000..e2c607795e --- /dev/null +++ b/src/model/test/HeatPumpAirToWaterCoolingSpeedData_GTest.cpp @@ -0,0 +1,230 @@ +/*********************************************************************************************************************** +* OpenStudio(R), Copyright (c) Alliance for Sustainable Energy, LLC. +* See also https://openstudio.net/license +***********************************************************************************************************************/ + +#include "ModelFixture.hpp" + +#include "../HeatPumpAirToWaterCoolingSpeedData.hpp" +#include "../HeatPumpAirToWaterCoolingSpeedData_Impl.hpp" + +#include "../HeatPumpAirToWaterCooling.hpp" + +#include "../Model.hpp" +#include "../Curve.hpp" +#include "../Curve_Impl.hpp" +#include "../CurveBiquadratic.hpp" +#include "../CurveBiquadratic_Impl.hpp" +#include "../CurveQuadratic.hpp" +#include "../CurveQuadratic_Impl.hpp" + +using namespace openstudio; +using namespace openstudio::model; + +TEST_F(ModelFixture, HeatPumpAirToWaterCoolingSpeedData_GettersSetters) { + Model m; + HeatPumpAirToWaterCoolingSpeedData heatPumpAirToWaterCoolingSpeedData(m); + + heatPumpAirToWaterCoolingSpeedData.setName("My HeatPumpAirToWaterCoolingSpeedData"); + + // Rated Cooling Capacity: Required Double, ctor defaults to autosize + EXPECT_TRUE(heatPumpAirToWaterCoolingSpeedData.isRatedCoolingCapacityAutosized()); + // Set + EXPECT_TRUE(heatPumpAirToWaterCoolingSpeedData.setRatedCoolingCapacity(40000.0)); + ASSERT_TRUE(heatPumpAirToWaterCoolingSpeedData.ratedCoolingCapacity()); + EXPECT_EQ(40000.0, heatPumpAirToWaterCoolingSpeedData.ratedCoolingCapacity().get()); + EXPECT_FALSE(heatPumpAirToWaterCoolingSpeedData.isRatedCoolingCapacityAutosized()); + // Bad Value + EXPECT_FALSE(heatPumpAirToWaterCoolingSpeedData.setRatedCoolingCapacity(-10.0)); + ASSERT_TRUE(heatPumpAirToWaterCoolingSpeedData.ratedCoolingCapacity()); + EXPECT_EQ(40000.0, heatPumpAirToWaterCoolingSpeedData.ratedCoolingCapacity().get()); + EXPECT_FALSE(heatPumpAirToWaterCoolingSpeedData.isRatedCoolingCapacityAutosized()); + // Autosize + heatPumpAirToWaterCoolingSpeedData.autosizeRatedCoolingCapacity(); + EXPECT_TRUE(heatPumpAirToWaterCoolingSpeedData.isRatedCoolingCapacityAutosized()); + + // Rated COP for Cooling: Required Double, defaults to 3.0 + EXPECT_EQ(3.0, heatPumpAirToWaterCoolingSpeedData.ratedCOPforCooling()); + // Set + EXPECT_TRUE(heatPumpAirToWaterCoolingSpeedData.setRatedCOPforCooling(4.0)); + EXPECT_EQ(4.0, heatPumpAirToWaterCoolingSpeedData.ratedCOPforCooling()); + // Bad Value + EXPECT_FALSE(heatPumpAirToWaterCoolingSpeedData.setRatedCOPforCooling(-10.0)); + EXPECT_EQ(4.0, heatPumpAirToWaterCoolingSpeedData.ratedCOPforCooling()); + + // Normalized Cooling Capacity Function of Temperature Curve Name: Required Object + auto oriCapFT = heatPumpAirToWaterCoolingSpeedData.normalizedCoolingCapacityFunctionofTemperatureCurve(); + CurveBiquadratic capFT(m); + EXPECT_TRUE(heatPumpAirToWaterCoolingSpeedData.setNormalizedCoolingCapacityFunctionofTemperatureCurve(capFT)); + EXPECT_EQ(capFT, heatPumpAirToWaterCoolingSpeedData.normalizedCoolingCapacityFunctionofTemperatureCurve()); + EXPECT_NE(capFT, oriCapFT); + + // Cooling Energy Input Ratio Function of Temperature Curve Name: Required Object + auto oriEIRFT = heatPumpAirToWaterCoolingSpeedData.coolingEnergyInputRatioFunctionofTemperatureCurve(); + CurveBiquadratic eirFT(m); + EXPECT_TRUE(heatPumpAirToWaterCoolingSpeedData.setCoolingEnergyInputRatioFunctionofTemperatureCurve(eirFT)); + EXPECT_EQ(eirFT, heatPumpAirToWaterCoolingSpeedData.coolingEnergyInputRatioFunctionofTemperatureCurve()); + EXPECT_NE(eirFT, oriEIRFT); + + // Cooling Energy Input Ratio Function of PLR Curve Name: Required Object + auto oriEIRFPLR = heatPumpAirToWaterCoolingSpeedData.coolingEnergyInputRatioFunctionofPLRCurve(); + CurveQuadratic eirFPLR(m); + EXPECT_TRUE(heatPumpAirToWaterCoolingSpeedData.setCoolingEnergyInputRatioFunctionofPLRCurve(eirFPLR)); + EXPECT_EQ(eirFPLR, heatPumpAirToWaterCoolingSpeedData.coolingEnergyInputRatioFunctionofPLRCurve()); + EXPECT_NE(eirFPLR, oriEIRFPLR); +} + +TEST_F(ModelFixture, HeatPumpAirToWaterCoolingSpeedData_CreateInvalid) { + { + Model m; + CurveQuadratic univariate(m); + CurveBiquadratic bivariate(m); + + EXPECT_EQ(1, univariate.numVariables()); + EXPECT_EQ(2, bivariate.numVariables()); + + // Valid + { + HeatPumpAirToWaterCoolingSpeedData awhpHCSpeed(m, bivariate, bivariate, univariate); + EXPECT_EQ(1, m.getConcreteModelObjects().size()); + EXPECT_EQ(1, m.getConcreteModelObjects().size()); + EXPECT_EQ(1, m.getConcreteModelObjects().size()); + } + + // Invalid: wrong curve type + EXPECT_THROW({ HeatPumpAirToWaterCoolingSpeedData(m, bivariate, univariate, univariate); }, openstudio::Exception); + EXPECT_THROW({ HeatPumpAirToWaterCoolingSpeedData(m, univariate, bivariate, univariate); }, openstudio::Exception); + EXPECT_THROW({ HeatPumpAirToWaterCoolingSpeedData(m, bivariate, bivariate, bivariate); }, openstudio::Exception); + EXPECT_EQ(1, m.getConcreteModelObjects().size()); + EXPECT_EQ(1, m.getConcreteModelObjects().size()); + EXPECT_EQ(1, m.getConcreteModelObjects().size()); + } + + // There is a nasty side effect that curves may be removed if ctor failed and they are not used anywhere else, so we'll want to call + // ModelObject::remove() and not HeatPumpAirToWaterCoolingSpeedData::remove() (=ParentObject::remove) in the ctor + { + Model m; + CurveQuadratic univariate(m); + CurveBiquadratic bivariate(m); + EXPECT_EQ(0, univariate.directUseCount()); + EXPECT_EQ(0, bivariate.directUseCount()); + + EXPECT_THROW({ HeatPumpAirToWaterCoolingSpeedData(m, bivariate, univariate, univariate); }, openstudio::Exception); + EXPECT_EQ(0, m.getConcreteModelObjects().size()); + // Curves should not be removed in case the expliclit ctor with curves failed + EXPECT_EQ(1, m.getConcreteModelObjects().size()); + EXPECT_EQ(1, m.getConcreteModelObjects().size()); + } +} + +TEST_F(ModelFixture, HeatPumpAirToWaterCoolingSpeedData_clone_remove) { + + Model m; + CurveBiquadratic capFT(m); + CurveBiquadratic eirFT(m); + CurveQuadratic eirFPLR(m); + + constexpr double cap = 40000.0; + constexpr double cop = 4.0; + + HeatPumpAirToWaterCoolingSpeedData awhpHCSpeed(m, capFT, eirFT, eirFPLR); + EXPECT_TRUE(awhpHCSpeed.setRatedCoolingCapacity(cap)); + EXPECT_TRUE(awhpHCSpeed.setRatedCOPforCooling(cop)); + + EXPECT_EQ(1, m.getConcreteModelObjects().size()); + EXPECT_EQ(2, m.getConcreteModelObjects().size()); + EXPECT_EQ(1, m.getConcreteModelObjects().size()); + + auto awhpHCSpeedClone = awhpHCSpeed.clone(m).cast(); + EXPECT_EQ(cap, awhpHCSpeedClone.ratedCoolingCapacity().get()); + EXPECT_EQ(cop, awhpHCSpeedClone.ratedCOPforCooling()); + EXPECT_EQ(capFT, awhpHCSpeedClone.normalizedCoolingCapacityFunctionofTemperatureCurve()); + EXPECT_EQ(eirFT, awhpHCSpeedClone.coolingEnergyInputRatioFunctionofTemperatureCurve()); + EXPECT_EQ(eirFPLR, awhpHCSpeedClone.coolingEnergyInputRatioFunctionofPLRCurve()); + + EXPECT_EQ(2, m.getConcreteModelObjects().size()); + EXPECT_EQ(2, m.getConcreteModelObjects().size()); + EXPECT_EQ(1, m.getConcreteModelObjects().size()); + + { + Model m2; + auto awhpHCSpeedClone2 = awhpHCSpeed.clone(m2).cast(); + EXPECT_EQ(cap, awhpHCSpeedClone2.ratedCoolingCapacity().get()); + EXPECT_EQ(cop, awhpHCSpeedClone2.ratedCOPforCooling()); + EXPECT_NE(capFT, awhpHCSpeedClone2.normalizedCoolingCapacityFunctionofTemperatureCurve()); + EXPECT_NE(eirFT, awhpHCSpeedClone2.coolingEnergyInputRatioFunctionofTemperatureCurve()); + EXPECT_NE(eirFPLR, awhpHCSpeedClone2.coolingEnergyInputRatioFunctionofPLRCurve()); + + EXPECT_EQ(1, m2.getConcreteModelObjects().size()); + EXPECT_EQ(2, m2.getConcreteModelObjects().size()); + EXPECT_EQ(1, m2.getConcreteModelObjects().size()); + auto rmed = awhpHCSpeedClone2.remove(); + EXPECT_EQ(4u, rmed.size()); + EXPECT_EQ(0, m2.getConcreteModelObjects().size()); + EXPECT_EQ(0, m2.getConcreteModelObjects().size()); + EXPECT_EQ(0, m2.getConcreteModelObjects().size()); + } + + auto rmed = awhpHCSpeed.remove(); + EXPECT_EQ(1u, rmed.size()); + EXPECT_EQ(1, m.getConcreteModelObjects().size()); + EXPECT_EQ(2, m.getConcreteModelObjects().size()); + EXPECT_EQ(1, m.getConcreteModelObjects().size()); + EXPECT_EQ(cap, awhpHCSpeedClone.ratedCoolingCapacity().get()); + EXPECT_EQ(cop, awhpHCSpeedClone.ratedCOPforCooling()); + EXPECT_EQ(capFT, awhpHCSpeedClone.normalizedCoolingCapacityFunctionofTemperatureCurve()); + EXPECT_EQ(eirFT, awhpHCSpeedClone.coolingEnergyInputRatioFunctionofTemperatureCurve()); + EXPECT_EQ(eirFPLR, awhpHCSpeedClone.coolingEnergyInputRatioFunctionofPLRCurve()); + + rmed = awhpHCSpeedClone.remove(); + EXPECT_EQ(4u, rmed.size()); + EXPECT_EQ(0, m.getConcreteModelObjects().size()); + EXPECT_EQ(0, m.getConcreteModelObjects().size()); + EXPECT_EQ(0, m.getConcreteModelObjects().size()); +} + +TEST_F(ModelFixture, HeatPumpAirToWaterCoolingSpeedData_RemoveParentModelObjectList) { + Model m; + + HeatPumpAirToWaterCooling awhp(m); + HeatPumpAirToWaterCooling awhp2(m); + + HeatPumpAirToWaterCoolingSpeedData speed1(m); + HeatPumpAirToWaterCoolingSpeedData speed2(m); + + // Test convenience method + EXPECT_EQ(0, speed1.heatPumpAirToWaterCoolings().size()); + EXPECT_EQ(0, speed2.heatPumpAirToWaterCoolings().size()); + + EXPECT_TRUE(awhp.addSpeed(speed1)); + EXPECT_EQ(1, speed1.heatPumpAirToWaterCoolings().size()); + EXPECT_EQ(0, speed2.heatPumpAirToWaterCoolings().size()); + + EXPECT_TRUE(awhp2.addSpeed(speed1)); + EXPECT_EQ(2, speed1.heatPumpAirToWaterCoolings().size()); + EXPECT_EQ(0, speed2.heatPumpAirToWaterCoolings().size()); + + EXPECT_TRUE(awhp.addSpeed(speed2)); + EXPECT_EQ(2, speed1.heatPumpAirToWaterCoolings().size()); + EXPECT_EQ(1, speed2.heatPumpAirToWaterCoolings().size()); + + EXPECT_TRUE(awhp2.addSpeed(speed2)); + EXPECT_EQ(2, speed1.heatPumpAirToWaterCoolings().size()); + EXPECT_EQ(2, speed2.heatPumpAirToWaterCoolings().size()); + + { + const std::vector speeds{speed1, speed2}; + EXPECT_EQ(speeds, awhp.speeds()); + EXPECT_EQ(speeds, awhp2.speeds()); + } + + speed1.remove(); + { + const std::vector speeds{speed2}; + EXPECT_EQ(speeds, awhp.speeds()); + EXPECT_EQ(speeds, awhp2.speeds()); + } + + awhp.remove(); + EXPECT_EQ(1, speed2.heatPumpAirToWaterCoolings().size()); +} diff --git a/src/model/test/HeatPumpAirToWaterCooling_GTest.cpp b/src/model/test/HeatPumpAirToWaterCooling_GTest.cpp new file mode 100644 index 0000000000..40353795ee --- /dev/null +++ b/src/model/test/HeatPumpAirToWaterCooling_GTest.cpp @@ -0,0 +1,501 @@ +/*********************************************************************************************************************** +* OpenStudio(R), Copyright (c) Alliance for Sustainable Energy, LLC. +* See also https://openstudio.net/license +***********************************************************************************************************************/ + +#include "ModelFixture.hpp" + +#include "../HeatPumpAirToWaterCooling.hpp" +#include "../HeatPumpAirToWaterCooling_Impl.hpp" + +#include "../HeatPumpAirToWater.hpp" +#include "../HeatPumpAirToWater_Impl.hpp" +#include "../HeatPumpAirToWaterCoolingSpeedData.hpp" +#include "../HeatPumpAirToWaterCoolingSpeedData_Impl.hpp" +#include "../HeatPumpAirToWaterHeating.hpp" + +#include "../Model.hpp" +#include "../Schedule.hpp" +#include "../ScheduleConstant.hpp" +#include "../Curve.hpp" +#include "../Curve_Impl.hpp" +#include "../CurveCubic.hpp" +#include "../CurveBicubic.hpp" + +#include "../AirLoopHVAC.hpp" +#include "../PlantLoop.hpp" +#include "../Node.hpp" +#include "../Node_Impl.hpp" +#include "../Splitter.hpp" + +#include "../ModelObjectList.hpp" +#include "../ModelObjectList_Impl.hpp" + +#include + +#include +#include +#include + +using namespace openstudio; +using namespace openstudio::model; + +std::string getObjectNames(const auto& rmed) { + std::vector rm_names; + rm_names.reserve(rmed.size()); + std::transform(rmed.cbegin(), rmed.cend(), std::back_inserter(rm_names), [](const auto& idfObjet) { return idfObjet.nameString(); }); + return fmt::format("Removed objects: {}", rm_names); +}; + +TEST_F(ModelFixture, HeatPumpAirToWaterCooling_GettersSetters) { + Model m; + HeatPumpAirToWaterCooling awhp(m); + + awhp.setName("My HeatPumpAirToWaterCooling"); + + // Availability Schedule Name: Required Object + // Ctor default: Always On + EXPECT_EQ(m.alwaysOnDiscreteSchedule(), awhp.availabilitySchedule()); + ScheduleConstant availabilitySchedule(m); + EXPECT_TRUE(awhp.setAvailabilitySchedule(availabilitySchedule)); + EXPECT_EQ(availabilitySchedule, awhp.availabilitySchedule()); + + // Rated Inlet Air Temperature: Required Double + // Ctor default: 8.0 + EXPECT_EQ(8.0, awhp.ratedInletAirTemperature()); + EXPECT_TRUE(awhp.setRatedInletAirTemperature(0.4)); + EXPECT_EQ(0.4, awhp.ratedInletAirTemperature()); + + // Rated Air Flow Rate: Required Double + // Ctor default: Autosize + EXPECT_TRUE(awhp.isRatedAirFlowRateAutosized()); + // Set + EXPECT_TRUE(awhp.setRatedAirFlowRate(0.5)); + ASSERT_TRUE(awhp.ratedAirFlowRate()); + EXPECT_EQ(0.5, awhp.ratedAirFlowRate().get()); + // Bad Value + EXPECT_FALSE(awhp.setRatedAirFlowRate(-10.0)); + ASSERT_TRUE(awhp.ratedAirFlowRate()); + EXPECT_EQ(0.5, awhp.ratedAirFlowRate().get()); + EXPECT_FALSE(awhp.isRatedAirFlowRateAutosized()); + // Autosize + awhp.autosizeRatedAirFlowRate(); + EXPECT_TRUE(awhp.isRatedAirFlowRateAutosized()); + + // Rated Leaving Water Temperature: Required Double + // Ctor default: 40.0 + EXPECT_EQ(40.0, awhp.ratedLeavingWaterTemperature()); + EXPECT_TRUE(awhp.setRatedLeavingWaterTemperature(0.6)); + EXPECT_EQ(0.6, awhp.ratedLeavingWaterTemperature()); + + // Rated Water Flow Rate: Required Double + // Ctor default: Autosize + EXPECT_TRUE(awhp.isRatedWaterFlowRateAutosized()); + // Set + EXPECT_TRUE(awhp.setRatedWaterFlowRate(0.7)); + ASSERT_TRUE(awhp.ratedWaterFlowRate()); + EXPECT_EQ(0.7, awhp.ratedWaterFlowRate().get()); + // Bad Value + EXPECT_FALSE(awhp.setRatedWaterFlowRate(-10.0)); + ASSERT_TRUE(awhp.ratedWaterFlowRate()); + EXPECT_EQ(0.7, awhp.ratedWaterFlowRate().get()); + EXPECT_FALSE(awhp.isRatedWaterFlowRateAutosized()); + // Autosize + awhp.autosizeRatedWaterFlowRate(); + EXPECT_TRUE(awhp.isRatedWaterFlowRateAutosized()); + + // Minimum Outdoor Air Temperature: Required Double + // Ctor default: -30.0 + EXPECT_EQ(-30.0, awhp.minimumOutdoorAirTemperature()); + EXPECT_TRUE(awhp.setMinimumOutdoorAirTemperature(0.8)); + EXPECT_EQ(0.8, awhp.minimumOutdoorAirTemperature()); + + // Maximum Outdoor Air Temperature: Required Double + // Ctor default: 100.0 + EXPECT_EQ(100.0, awhp.maximumOutdoorAirTemperature()); + EXPECT_TRUE(awhp.setMaximumOutdoorAirTemperature(0.9)); + EXPECT_EQ(0.9, awhp.maximumOutdoorAirTemperature()); + + // Minimum Leaving Water Temperature Curve Name: Optional Object + CurveCubic minimumLeavingWaterTemperatureCurve(m); + EXPECT_TRUE(awhp.setMinimumLeavingWaterTemperatureCurve(minimumLeavingWaterTemperatureCurve)); + ASSERT_TRUE(awhp.minimumLeavingWaterTemperatureCurve()); + EXPECT_EQ(minimumLeavingWaterTemperatureCurve, awhp.minimumLeavingWaterTemperatureCurve().get()); + awhp.resetMinimumLeavingWaterTemperatureCurve(); + EXPECT_FALSE(awhp.minimumLeavingWaterTemperatureCurve()); + + // Maximum Leaving Water Temperature Curve Name: Optional Object + CurveCubic maximumLeavingWaterTemperatureCurve(m); + EXPECT_TRUE(awhp.setMaximumLeavingWaterTemperatureCurve(maximumLeavingWaterTemperatureCurve)); + ASSERT_TRUE(awhp.maximumLeavingWaterTemperatureCurve()); + EXPECT_EQ(maximumLeavingWaterTemperatureCurve, awhp.maximumLeavingWaterTemperatureCurve().get()); + + // Sizing Factor: Required Double + // Ctor default: 1.0 + EXPECT_EQ(1.0, awhp.sizingFactor()); + // Set + EXPECT_TRUE(awhp.setSizingFactor(1.2)); + EXPECT_EQ(1.2, awhp.sizingFactor()); + // Bad Value + EXPECT_FALSE(awhp.setSizingFactor(-10.0)); + EXPECT_EQ(1.2, awhp.sizingFactor()); + + // Booster Mode On Speed: Optional Object + HeatPumpAirToWaterCoolingSpeedData boosterModeOnSpeed(m); + EXPECT_TRUE(awhp.setBoosterModeOnSpeed(boosterModeOnSpeed)); + ASSERT_TRUE(awhp.boosterModeOnSpeed()); + EXPECT_EQ(boosterModeOnSpeed, awhp.boosterModeOnSpeed().get()); + awhp.resetBoosterModeOnSpeed(); + EXPECT_FALSE(awhp.boosterModeOnSpeed()); +} + +TEST_F(ModelFixture, HeatPumpAirToWaterCooling_HeatCoolFuelTypes) { + Model m; + HeatPumpAirToWaterCooling awhp(m); + + EXPECT_EQ(ComponentType(ComponentType::Cooling), awhp.componentType()); + testFuelTypeEquality({FuelType::Electricity}, awhp.coolingFuelTypes()); + testFuelTypeEquality({}, awhp.heatingFuelTypes()); + testAppGFuelTypeEquality({}, awhp.appGHeatingFuelTypes()); +} + +TEST_F(ModelFixture, HeatPumpAirToWaterCooling_Speeds) { + + // create a model to use + Model model; + + HeatPumpAirToWaterCooling awhp(model); + + std::vector speeds; + EXPECT_EQ(5, HeatPumpAirToWaterCooling::maximum_number_of_speeds); + for (unsigned i = 1; i <= HeatPumpAirToWaterCooling::maximum_number_of_speeds; ++i) { + HeatPumpAirToWaterCoolingSpeedData speed(model); + speed.setName("Speed " + std::to_string(i)); + speeds.push_back(speed); + EXPECT_TRUE(awhp.addSpeed(speed)); + EXPECT_EQ(i, awhp.numberOfSpeeds()); + EXPECT_EQ(speeds, awhp.speeds()); + } + + // Can't add more than 5 Speeds + HeatPumpAirToWaterCoolingSpeedData anotherSpeed(model); + EXPECT_FALSE(awhp.addSpeed(anotherSpeed)); + EXPECT_EQ(5, awhp.numberOfSpeeds()); + EXPECT_EQ(speeds, awhp.speeds()); + + // Can't remove a speed that's not in there... + EXPECT_FALSE(awhp.removeSpeed(anotherSpeed)); + EXPECT_EQ(5, awhp.numberOfSpeeds()); + EXPECT_EQ(speeds, awhp.speeds()); + + { + int speedIndex = 3; + std::vector thisSpeeds = awhp.speeds(); + // Explicit copy, so we can keep using it after it's been removed + const auto speedAtIndex = thisSpeeds[speedIndex - 1]; + EXPECT_TRUE(std::find(thisSpeeds.begin(), thisSpeeds.end(), speedAtIndex) != thisSpeeds.end()); + auto optIndex = awhp.speedIndex(speedAtIndex); + ASSERT_TRUE(optIndex); + EXPECT_EQ(speedIndex, optIndex.get()); + EXPECT_TRUE(awhp.removeSpeed(speedIndex)); + EXPECT_EQ(4, awhp.numberOfSpeeds()); + thisSpeeds = awhp.speeds(); + EXPECT_FALSE(std::find(thisSpeeds.begin(), thisSpeeds.end(), speedAtIndex) != thisSpeeds.end()); + // Do the same on our vector, so we're up to date... + speeds.erase(speeds.begin() + speedIndex - 1); + EXPECT_EQ(speeds, awhp.speeds()); + } + + { + int speedIndex = 2; + std::vector thisSpeeds = awhp.speeds(); + // Explicit copy, so we can keep using it after it's been removed + const auto speedAtIndex = thisSpeeds[speedIndex - 1]; + EXPECT_TRUE(std::find(thisSpeeds.begin(), thisSpeeds.end(), speedAtIndex) != thisSpeeds.end()); + auto optIndex = awhp.speedIndex(speedAtIndex); + ASSERT_TRUE(optIndex); + EXPECT_EQ(speedIndex, optIndex.get()); + EXPECT_TRUE(awhp.removeSpeed(speedAtIndex)); + EXPECT_EQ(3, awhp.numberOfSpeeds()); + thisSpeeds = awhp.speeds(); + EXPECT_FALSE(std::find(thisSpeeds.begin(), thisSpeeds.end(), speedAtIndex) != thisSpeeds.end()); + // Do the same on our vector, so we're up to date... + speeds.erase(std::find(speeds.begin(), speeds.end(), speedAtIndex)); + EXPECT_EQ(speeds, awhp.speeds()); + } + + { + const auto& speedAtEnd = speeds.back(); + auto optIndex = awhp.speedIndex(speedAtEnd); + ASSERT_TRUE(optIndex); + EXPECT_EQ(awhp.numberOfSpeeds(), optIndex.get()); + + EXPECT_TRUE(awhp.setSpeedIndex(speedAtEnd, 2)); + std::vector thisSpeeds = awhp.speeds(); + optIndex = awhp.speedIndex(speedAtEnd); + ASSERT_TRUE(optIndex); + EXPECT_EQ(2, optIndex.get()); + EXPECT_EQ(3, awhp.numberOfSpeeds()); + for (unsigned i = 1; i <= awhp.numberOfSpeeds(); ++i) { + if (i < optIndex.get()) { + EXPECT_EQ(speeds[i - 1], awhp.speeds()[i - 1]); + } else if (i > optIndex.get()) { + EXPECT_EQ(speeds[i - 2], awhp.speeds()[i - 1]); + } + } + } + + awhp.removeAllSpeeds(); + EXPECT_EQ(0, awhp.numberOfSpeeds()); + + EXPECT_TRUE(awhp.setSpeeds(speeds)); + EXPECT_EQ(3, awhp.numberOfSpeeds()); + EXPECT_EQ(speeds, awhp.speeds()); + + for (unsigned i = 6; i <= 8; ++i) { + HeatPumpAirToWaterCoolingSpeedData speed(model); + awhp.setName("Speed " + std::to_string(i)); + speeds.push_back(speed); + } + EXPECT_EQ(6, speeds.size()); + awhp.removeAllSpeeds(); + EXPECT_TRUE(awhp.addSpeed(anotherSpeed)); + + // This should clear, then assign the first 5, but then return false since the 6th failed + EXPECT_FALSE(awhp.setSpeeds(speeds)); + EXPECT_EQ(5, awhp.numberOfSpeeds()); + { + std::vector thisSpeeds = awhp.speeds(); + for (unsigned i = 0; i < 5; ++i) { + EXPECT_EQ(speeds[i], thisSpeeds[i]); + } + } + + awhp.removeAllSpeeds(); + EXPECT_EQ(0, awhp.numberOfSpeeds()); + EXPECT_EQ(0, awhp.speeds().size()); + + // Test that added a speed from another model will fail but not add a blank extensible group + Model model2; + HeatPumpAirToWaterCoolingSpeedData speedFromAnotherModel(model2); + EXPECT_FALSE(awhp.addSpeed(speedFromAnotherModel)); + EXPECT_EQ(0, awhp.numExtensibleGroups()); + EXPECT_EQ(0, awhp.numberOfSpeeds()); + EXPECT_EQ(0, awhp.speeds().size()); +} + +TEST_F(ModelFixture, HeatPumpAirToWaterCooling_clone) { + + // create a model to use + Model model; + + HeatPumpAirToWaterCooling awhp(model); + + std::vector speeds; + for (unsigned i = 1; i <= HeatPumpAirToWaterCooling::maximum_number_of_speeds; ++i) { + HeatPumpAirToWaterCoolingSpeedData speed(model); + speed.setName("Speed " + std::to_string(i)); + speeds.push_back(speed); + EXPECT_TRUE(awhp.addSpeed(speed)); + } + + EXPECT_EQ(5, awhp.numberOfSpeeds()); + EXPECT_EQ(speeds, awhp.speeds()); + + EXPECT_EQ(1, model.getConcreteModelObjects().size()); + EXPECT_EQ(1, model.getConcreteModelObjects().size()); + EXPECT_EQ(5, model.getConcreteModelObjects().size()); + EXPECT_EQ(5 * 3, model.getModelObjects().size()); + + auto awhpClone = awhp.clone(model).cast(); + EXPECT_EQ(2, model.getConcreteModelObjects().size()); + EXPECT_EQ(2, model.getConcreteModelObjects().size()); + EXPECT_EQ(5, model.getConcreteModelObjects().size()); + EXPECT_EQ(5 * 3, model.getModelObjects().size()); + + EXPECT_EQ(5, awhp.numberOfSpeeds()); + EXPECT_EQ(speeds, awhp.speeds()); + + EXPECT_EQ(5, awhpClone.numberOfSpeeds()); + EXPECT_EQ(speeds, awhpClone.speeds()); + + auto rmed = awhp.remove(); + + EXPECT_EQ(2, rmed.size()) << getObjectNames(rmed); + EXPECT_EQ(IddObjectType::OS_HeatPump_AirToWater_Cooling, rmed[0].iddObject().type().value()); + EXPECT_EQ(IddObjectType::OS_ModelObjectList, rmed[1].iddObject().type().value()); + EXPECT_EQ(1, model.getConcreteModelObjects().size()); + EXPECT_EQ(1, model.getConcreteModelObjects().size()); + EXPECT_EQ(5, model.getConcreteModelObjects().size()); + EXPECT_EQ(5 * 3, model.getModelObjects().size()); + EXPECT_EQ(5, awhpClone.numberOfSpeeds()); + EXPECT_EQ(speeds, awhpClone.speeds()); + + rmed = awhpClone.remove(); + + const unsigned expectedRemoved = 1 /* HeatPumpAirToWaterCooling */ + 1 /* ModelObjectList */ + 5 * (1 /* SpeedData */ + 3 /* Curves */); + EXPECT_EQ(expectedRemoved, rmed.size()) << getObjectNames(rmed); + EXPECT_EQ(0, model.getConcreteModelObjects().size()); + EXPECT_EQ(0, model.getConcreteModelObjects().size()); + EXPECT_EQ(0, model.getConcreteModelObjects().size()); + EXPECT_EQ(0, model.getModelObjects().size()); +} + +TEST_F(ModelFixture, HeatPumpAirToWaterCooling_addToNode) { + // create a model to use + Model m; + + HeatPumpAirToWaterCooling awhp(m); + + PlantLoop p(m); + { + Node n = p.demandInletNode(); + EXPECT_FALSE(awhp.addToNode(n)); + } + EXPECT_FALSE(p.addDemandBranchForComponent(awhp)); + + AirLoopHVAC a(m); + { + Node n = a.supplyInletNode(); + EXPECT_FALSE(awhp.addToNode(n)); + } + { + Node n = a.demandInletNode(); + EXPECT_FALSE(awhp.addToNode(n)); + } + + EXPECT_FALSE(awhp.plantLoop()); + EXPECT_FALSE(awhp.inletModelObject()); + EXPECT_FALSE(awhp.outletModelObject()); + + // Plant Side connections + EXPECT_EQ(5, p.demandComponents().size()); // o --- Splitter --- o --- Mixer --- o + EXPECT_EQ(5, p.supplyComponents().size()); // o --- Splitter --- o --- Mixer --- o + + EXPECT_TRUE(p.addSupplyBranchForComponent(awhp)); + EXPECT_EQ(7, p.supplyComponents().size()); // o --- Splitter --- o --- awhp_cc --- Mixer --- o + EXPECT_TRUE(awhp.plantLoop()); + EXPECT_TRUE(awhp.inletModelObject()); + EXPECT_TRUE(awhp.outletModelObject()); + + auto awhpClone = awhp.clone(m).cast(); + EXPECT_FALSE(awhpClone.plantLoop()); + EXPECT_FALSE(awhpClone.inletModelObject()); + EXPECT_FALSE(awhpClone.outletModelObject()); + + EXPECT_EQ(2, m.getConcreteModelObjects().size()); + + awhp.removeFromLoop(); + EXPECT_FALSE(awhp.plantLoop()); + EXPECT_FALSE(awhp.inletModelObject()); + EXPECT_FALSE(awhp.outletModelObject()); + + EXPECT_EQ(2, m.getConcreteModelObjects().size()); + EXPECT_EQ(5, p.supplyComponents().size()); // o --- Splitter --- o --- Mixer --- o + EXPECT_EQ(5, p.demandComponents().size()); // o --- Splitter --- o --- Mixer --- o + + auto supply_inlet_node = p.supplyInletNode(); + EXPECT_TRUE(awhpClone.addToNode(supply_inlet_node)); + + EXPECT_EQ(7, p.supplyComponents().size()); // o --- awhpClone --- Splitter --- o --- Mixer --- o + EXPECT_EQ(5, p.demandComponents().size()); // o --- Splitter --- o --- Mixer --- o + EXPECT_TRUE(awhpClone.plantLoop()); + ASSERT_TRUE(awhpClone.inletModelObject()); + EXPECT_EQ(supply_inlet_node, awhpClone.inletModelObject().get()); + ASSERT_TRUE(awhpClone.outletModelObject()); + auto outlet_ = awhpClone.outletModelObject().get().optionalCast(); + ASSERT_TRUE(outlet_); + ASSERT_TRUE(outlet_->outletModelObject()); + EXPECT_EQ(p.supplySplitter(), outlet_->outletModelObject().get()); + + awhpClone.remove(); + EXPECT_EQ(1, m.getConcreteModelObjects().size()); + EXPECT_EQ(5, p.supplyComponents().size()); // o --- Splitter --- o --- Mixer --- o + EXPECT_EQ(5, p.demandComponents().size()); // o --- Splitter --- o --- Mixer --- o +} + +TEST_F(ModelFixture, HeatPumpAirToWaterCooling_containingHVACComponent) { + + Model m; + + HeatPumpAirToWater awhp(m); + HeatPumpAirToWater awhp2(m); + + HeatPumpAirToWaterCooling awhp_cc(m); + for (unsigned i = 1; i <= HeatPumpAirToWaterCooling::maximum_number_of_speeds; ++i) { + HeatPumpAirToWaterCoolingSpeedData speed(m); + speed.setName("Cooling Speed " + std::to_string(i)); + EXPECT_TRUE(awhp_cc.addSpeed(speed)); + } + EXPECT_FALSE(awhp_cc.containingHVACComponent()); + EXPECT_FALSE(awhp_cc.heatPumpAirToWater()); + + EXPECT_TRUE(awhp.setCoolingOperationMode(awhp_cc)); + ASSERT_TRUE(awhp_cc.containingHVACComponent()); + EXPECT_EQ(awhp, awhp_cc.containingHVACComponent().get()); + ASSERT_TRUE(awhp_cc.heatPumpAirToWater()); + EXPECT_EQ(awhp, awhp_cc.heatPumpAirToWater().get()); + EXPECT_EQ(5, m.getConcreteModelObjects().size()); + // Each speed has 3 curves + EXPECT_EQ(5 * 3, m.getModelObjects().size()); + + // Already assigned to another AWHP, not letting it be assigned again + EXPECT_FALSE(awhp2.setCoolingOperationMode(awhp_cc)); + ASSERT_TRUE(awhp_cc.containingHVACComponent()); + ASSERT_TRUE(awhp_cc.heatPumpAirToWater()); + EXPECT_EQ(awhp, awhp_cc.heatPumpAirToWater().get()); + + EXPECT_TRUE(awhp.coolingOperationMode()); + EXPECT_FALSE(awhp2.coolingOperationMode()); + EXPECT_FALSE(awhp.heatingOperationMode()); + EXPECT_FALSE(awhp2.heatingOperationMode()); + + // Is contained so not removable + EXPECT_FALSE(awhp_cc.isRemovable()); + + EXPECT_EQ(2, m.getConcreteModelObjects().size()); + EXPECT_EQ(1, m.getConcreteModelObjects().size()); + EXPECT_EQ(1, m.getConcreteModelObjects().size()); + EXPECT_EQ(5, m.getConcreteModelObjects().size()); + EXPECT_EQ(5 * 3, m.getModelObjects().size()); + + EXPECT_EQ(0, awhp_cc.remove().size()); + EXPECT_EQ(2, m.getConcreteModelObjects().size()); + EXPECT_EQ(1, m.getConcreteModelObjects().size()); + EXPECT_EQ(1, m.getConcreteModelObjects().size()); + EXPECT_EQ(5, m.getConcreteModelObjects().size()); + EXPECT_EQ(5 * 3, m.getModelObjects().size()); + + // Remove the second awhp + auto rmed = awhp2.remove(); + EXPECT_EQ(1, rmed.size()) << getObjectNames(rmed); + EXPECT_TRUE(awhp_cc.heatPumpAirToWater()); + ASSERT_TRUE(awhp_cc.containingHVACComponent()); + EXPECT_EQ(awhp, awhp_cc.containingHVACComponent().get()); + EXPECT_EQ(1, m.getConcreteModelObjects().size()); + EXPECT_EQ(1, m.getConcreteModelObjects().size()); + EXPECT_EQ(1, m.getConcreteModelObjects().size()); + EXPECT_EQ(5, m.getConcreteModelObjects().size()); + EXPECT_EQ(5 * 3, m.getModelObjects().size()); + + // Still not removable + EXPECT_FALSE(awhp_cc.isRemovable()); + rmed = awhp_cc.remove(); + EXPECT_EQ(0, rmed.size()) << getObjectNames(rmed); + EXPECT_EQ(1, m.getConcreteModelObjects().size()); + EXPECT_EQ(1, m.getConcreteModelObjects().size()); + EXPECT_EQ(1, m.getConcreteModelObjects().size()); + EXPECT_EQ(5, m.getConcreteModelObjects().size()); + EXPECT_EQ(5 * 3, m.getModelObjects().size()); + + // Remove the first awhp: this time it's gone! + rmed = awhp.remove(); + const unsigned expectedRemoved = + 1 /* HeatPumpAirToWater */ + 1 /* HeatPumpAirToWaterCooling */ + 1 /* ModelObjectList */ + 5 * (1 /* SpeedData */ + 3 /* Curves */); + EXPECT_EQ(expectedRemoved, rmed.size()) << getObjectNames(rmed); + EXPECT_EQ(0, m.getConcreteModelObjects().size()); + EXPECT_EQ(0, m.getConcreteModelObjects().size()); + EXPECT_EQ(0, m.getConcreteModelObjects().size()); + EXPECT_EQ(0, m.getConcreteModelObjects().size()); + EXPECT_EQ(0, m.getModelObjects().size()); +} diff --git a/src/model/test/HeatPumpAirToWaterHeatingSpeedData_GTest.cpp b/src/model/test/HeatPumpAirToWaterHeatingSpeedData_GTest.cpp new file mode 100644 index 0000000000..ed9f529e1d --- /dev/null +++ b/src/model/test/HeatPumpAirToWaterHeatingSpeedData_GTest.cpp @@ -0,0 +1,230 @@ +/*********************************************************************************************************************** +* OpenStudio(R), Copyright (c) Alliance for Sustainable Energy, LLC. +* See also https://openstudio.net/license +***********************************************************************************************************************/ + +#include "ModelFixture.hpp" + +#include "../HeatPumpAirToWaterHeatingSpeedData.hpp" +#include "../HeatPumpAirToWaterHeatingSpeedData_Impl.hpp" + +#include "../HeatPumpAirToWaterHeating.hpp" + +#include "../Model.hpp" +#include "../Curve.hpp" +#include "../Curve_Impl.hpp" +#include "../CurveBiquadratic.hpp" +#include "../CurveBiquadratic_Impl.hpp" +#include "../CurveQuadratic.hpp" +#include "../CurveQuadratic_Impl.hpp" + +using namespace openstudio; +using namespace openstudio::model; + +TEST_F(ModelFixture, HeatPumpAirToWaterHeatingSpeedData_GettersSetters) { + Model m; + HeatPumpAirToWaterHeatingSpeedData heatPumpAirToWaterHeatingSpeedData(m); + + heatPumpAirToWaterHeatingSpeedData.setName("My HeatPumpAirToWaterHeatingSpeedData"); + + // Rated Heating Capacity: Required Double, ctor defaults to autosize + EXPECT_TRUE(heatPumpAirToWaterHeatingSpeedData.isRatedHeatingCapacityAutosized()); + // Set + EXPECT_TRUE(heatPumpAirToWaterHeatingSpeedData.setRatedHeatingCapacity(40000.0)); + ASSERT_TRUE(heatPumpAirToWaterHeatingSpeedData.ratedHeatingCapacity()); + EXPECT_EQ(40000.0, heatPumpAirToWaterHeatingSpeedData.ratedHeatingCapacity().get()); + EXPECT_FALSE(heatPumpAirToWaterHeatingSpeedData.isRatedHeatingCapacityAutosized()); + // Bad Value + EXPECT_FALSE(heatPumpAirToWaterHeatingSpeedData.setRatedHeatingCapacity(-10.0)); + ASSERT_TRUE(heatPumpAirToWaterHeatingSpeedData.ratedHeatingCapacity()); + EXPECT_EQ(40000.0, heatPumpAirToWaterHeatingSpeedData.ratedHeatingCapacity().get()); + EXPECT_FALSE(heatPumpAirToWaterHeatingSpeedData.isRatedHeatingCapacityAutosized()); + // Autosize + heatPumpAirToWaterHeatingSpeedData.autosizeRatedHeatingCapacity(); + EXPECT_TRUE(heatPumpAirToWaterHeatingSpeedData.isRatedHeatingCapacityAutosized()); + + // Rated COP for Heating: Required Double, defaults to 3.0 + EXPECT_EQ(3.0, heatPumpAirToWaterHeatingSpeedData.ratedCOPforHeating()); + // Set + EXPECT_TRUE(heatPumpAirToWaterHeatingSpeedData.setRatedCOPforHeating(4.0)); + EXPECT_EQ(4.0, heatPumpAirToWaterHeatingSpeedData.ratedCOPforHeating()); + // Bad Value + EXPECT_FALSE(heatPumpAirToWaterHeatingSpeedData.setRatedCOPforHeating(-10.0)); + EXPECT_EQ(4.0, heatPumpAirToWaterHeatingSpeedData.ratedCOPforHeating()); + + // Normalized Heating Capacity Function of Temperature Curve Name: Required Object + auto oriCapFT = heatPumpAirToWaterHeatingSpeedData.normalizedHeatingCapacityFunctionofTemperatureCurve(); + CurveBiquadratic capFT(m); + EXPECT_TRUE(heatPumpAirToWaterHeatingSpeedData.setNormalizedHeatingCapacityFunctionofTemperatureCurve(capFT)); + EXPECT_EQ(capFT, heatPumpAirToWaterHeatingSpeedData.normalizedHeatingCapacityFunctionofTemperatureCurve()); + EXPECT_NE(capFT, oriCapFT); + + // Heating Energy Input Ratio Function of Temperature Curve Name: Required Object + auto oriEIRFT = heatPumpAirToWaterHeatingSpeedData.heatingEnergyInputRatioFunctionofTemperatureCurve(); + CurveBiquadratic eirFT(m); + EXPECT_TRUE(heatPumpAirToWaterHeatingSpeedData.setHeatingEnergyInputRatioFunctionofTemperatureCurve(eirFT)); + EXPECT_EQ(eirFT, heatPumpAirToWaterHeatingSpeedData.heatingEnergyInputRatioFunctionofTemperatureCurve()); + EXPECT_NE(eirFT, oriEIRFT); + + // Heating Energy Input Ratio Function of PLR Curve Name: Required Object + auto oriEIRFPLR = heatPumpAirToWaterHeatingSpeedData.heatingEnergyInputRatioFunctionofPLRCurve(); + CurveQuadratic eirFPLR(m); + EXPECT_TRUE(heatPumpAirToWaterHeatingSpeedData.setHeatingEnergyInputRatioFunctionofPLRCurve(eirFPLR)); + EXPECT_EQ(eirFPLR, heatPumpAirToWaterHeatingSpeedData.heatingEnergyInputRatioFunctionofPLRCurve()); + EXPECT_NE(eirFPLR, oriEIRFPLR); +} + +TEST_F(ModelFixture, HeatPumpAirToWaterHeatingSpeedData_CreateInvalid) { + { + Model m; + CurveQuadratic univariate(m); + CurveBiquadratic bivariate(m); + + EXPECT_EQ(1, univariate.numVariables()); + EXPECT_EQ(2, bivariate.numVariables()); + + // Valid + { + HeatPumpAirToWaterHeatingSpeedData awhpHCSpeed(m, bivariate, bivariate, univariate); + EXPECT_EQ(1, m.getConcreteModelObjects().size()); + EXPECT_EQ(1, m.getConcreteModelObjects().size()); + EXPECT_EQ(1, m.getConcreteModelObjects().size()); + } + + // Invalid: wrong curve type + EXPECT_THROW({ HeatPumpAirToWaterHeatingSpeedData(m, bivariate, univariate, univariate); }, openstudio::Exception); + EXPECT_THROW({ HeatPumpAirToWaterHeatingSpeedData(m, univariate, bivariate, univariate); }, openstudio::Exception); + EXPECT_THROW({ HeatPumpAirToWaterHeatingSpeedData(m, bivariate, bivariate, bivariate); }, openstudio::Exception); + EXPECT_EQ(1, m.getConcreteModelObjects().size()); + EXPECT_EQ(1, m.getConcreteModelObjects().size()); + EXPECT_EQ(1, m.getConcreteModelObjects().size()); + } + + // There is a nasty side effect that curves may be removed if ctor failed and they are not used anywhere else, so we'll want to call + // ModelObject::remove() and not HeatPumpAirToWaterHeatingSpeedData::remove() (=ParentObject::remove) in the ctor + { + Model m; + CurveQuadratic univariate(m); + CurveBiquadratic bivariate(m); + EXPECT_EQ(0, univariate.directUseCount()); + EXPECT_EQ(0, bivariate.directUseCount()); + + EXPECT_THROW({ HeatPumpAirToWaterHeatingSpeedData(m, bivariate, univariate, univariate); }, openstudio::Exception); + EXPECT_EQ(0, m.getConcreteModelObjects().size()); + // Curves should not be removed in case the expliclit ctor with curves failed + EXPECT_EQ(1, m.getConcreteModelObjects().size()); + EXPECT_EQ(1, m.getConcreteModelObjects().size()); + } +} + +TEST_F(ModelFixture, HeatPumpAirToWaterHeatingSpeedData_clone_remove) { + + Model m; + CurveBiquadratic capFT(m); + CurveBiquadratic eirFT(m); + CurveQuadratic eirFPLR(m); + + constexpr double cap = 40000.0; + constexpr double cop = 4.0; + + HeatPumpAirToWaterHeatingSpeedData awhpHCSpeed(m, capFT, eirFT, eirFPLR); + EXPECT_TRUE(awhpHCSpeed.setRatedHeatingCapacity(cap)); + EXPECT_TRUE(awhpHCSpeed.setRatedCOPforHeating(cop)); + + EXPECT_EQ(1, m.getConcreteModelObjects().size()); + EXPECT_EQ(2, m.getConcreteModelObjects().size()); + EXPECT_EQ(1, m.getConcreteModelObjects().size()); + + auto awhpHCSpeedClone = awhpHCSpeed.clone(m).cast(); + EXPECT_EQ(cap, awhpHCSpeedClone.ratedHeatingCapacity().get()); + EXPECT_EQ(cop, awhpHCSpeedClone.ratedCOPforHeating()); + EXPECT_EQ(capFT, awhpHCSpeedClone.normalizedHeatingCapacityFunctionofTemperatureCurve()); + EXPECT_EQ(eirFT, awhpHCSpeedClone.heatingEnergyInputRatioFunctionofTemperatureCurve()); + EXPECT_EQ(eirFPLR, awhpHCSpeedClone.heatingEnergyInputRatioFunctionofPLRCurve()); + + EXPECT_EQ(2, m.getConcreteModelObjects().size()); + EXPECT_EQ(2, m.getConcreteModelObjects().size()); + EXPECT_EQ(1, m.getConcreteModelObjects().size()); + + { + Model m2; + auto awhpHCSpeedClone2 = awhpHCSpeed.clone(m2).cast(); + EXPECT_EQ(cap, awhpHCSpeedClone2.ratedHeatingCapacity().get()); + EXPECT_EQ(cop, awhpHCSpeedClone2.ratedCOPforHeating()); + EXPECT_NE(capFT, awhpHCSpeedClone2.normalizedHeatingCapacityFunctionofTemperatureCurve()); + EXPECT_NE(eirFT, awhpHCSpeedClone2.heatingEnergyInputRatioFunctionofTemperatureCurve()); + EXPECT_NE(eirFPLR, awhpHCSpeedClone2.heatingEnergyInputRatioFunctionofPLRCurve()); + + EXPECT_EQ(1, m2.getConcreteModelObjects().size()); + EXPECT_EQ(2, m2.getConcreteModelObjects().size()); + EXPECT_EQ(1, m2.getConcreteModelObjects().size()); + auto rmed = awhpHCSpeedClone2.remove(); + EXPECT_EQ(4u, rmed.size()); + EXPECT_EQ(0, m2.getConcreteModelObjects().size()); + EXPECT_EQ(0, m2.getConcreteModelObjects().size()); + EXPECT_EQ(0, m2.getConcreteModelObjects().size()); + } + + auto rmed = awhpHCSpeed.remove(); + EXPECT_EQ(1u, rmed.size()); + EXPECT_EQ(1, m.getConcreteModelObjects().size()); + EXPECT_EQ(2, m.getConcreteModelObjects().size()); + EXPECT_EQ(1, m.getConcreteModelObjects().size()); + EXPECT_EQ(cap, awhpHCSpeedClone.ratedHeatingCapacity().get()); + EXPECT_EQ(cop, awhpHCSpeedClone.ratedCOPforHeating()); + EXPECT_EQ(capFT, awhpHCSpeedClone.normalizedHeatingCapacityFunctionofTemperatureCurve()); + EXPECT_EQ(eirFT, awhpHCSpeedClone.heatingEnergyInputRatioFunctionofTemperatureCurve()); + EXPECT_EQ(eirFPLR, awhpHCSpeedClone.heatingEnergyInputRatioFunctionofPLRCurve()); + + rmed = awhpHCSpeedClone.remove(); + EXPECT_EQ(4u, rmed.size()); + EXPECT_EQ(0, m.getConcreteModelObjects().size()); + EXPECT_EQ(0, m.getConcreteModelObjects().size()); + EXPECT_EQ(0, m.getConcreteModelObjects().size()); +} + +TEST_F(ModelFixture, HeatPumpAirToWaterHeatingSpeedData_RemoveParentModelObjectList) { + Model m; + + HeatPumpAirToWaterHeating awhp(m); + HeatPumpAirToWaterHeating awhp2(m); + + HeatPumpAirToWaterHeatingSpeedData speed1(m); + HeatPumpAirToWaterHeatingSpeedData speed2(m); + + // Test convenience method + EXPECT_EQ(0, speed1.heatPumpAirToWaterHeatings().size()); + EXPECT_EQ(0, speed2.heatPumpAirToWaterHeatings().size()); + + EXPECT_TRUE(awhp.addSpeed(speed1)); + EXPECT_EQ(1, speed1.heatPumpAirToWaterHeatings().size()); + EXPECT_EQ(0, speed2.heatPumpAirToWaterHeatings().size()); + + EXPECT_TRUE(awhp2.addSpeed(speed1)); + EXPECT_EQ(2, speed1.heatPumpAirToWaterHeatings().size()); + EXPECT_EQ(0, speed2.heatPumpAirToWaterHeatings().size()); + + EXPECT_TRUE(awhp.addSpeed(speed2)); + EXPECT_EQ(2, speed1.heatPumpAirToWaterHeatings().size()); + EXPECT_EQ(1, speed2.heatPumpAirToWaterHeatings().size()); + + EXPECT_TRUE(awhp2.addSpeed(speed2)); + EXPECT_EQ(2, speed1.heatPumpAirToWaterHeatings().size()); + EXPECT_EQ(2, speed2.heatPumpAirToWaterHeatings().size()); + + { + const std::vector speeds{speed1, speed2}; + EXPECT_EQ(speeds, awhp.speeds()); + EXPECT_EQ(speeds, awhp2.speeds()); + } + + speed1.remove(); + { + const std::vector speeds{speed2}; + EXPECT_EQ(speeds, awhp.speeds()); + EXPECT_EQ(speeds, awhp2.speeds()); + } + + awhp.remove(); + EXPECT_EQ(1, speed2.heatPumpAirToWaterHeatings().size()); +} diff --git a/src/model/test/HeatPumpAirToWaterHeating_GTest.cpp b/src/model/test/HeatPumpAirToWaterHeating_GTest.cpp new file mode 100644 index 0000000000..47d1b288f1 --- /dev/null +++ b/src/model/test/HeatPumpAirToWaterHeating_GTest.cpp @@ -0,0 +1,501 @@ +/*********************************************************************************************************************** +* OpenStudio(R), Copyright (c) Alliance for Sustainable Energy, LLC. +* See also https://openstudio.net/license +***********************************************************************************************************************/ + +#include "ModelFixture.hpp" + +#include "../HeatPumpAirToWaterHeating.hpp" +#include "../HeatPumpAirToWaterHeating_Impl.hpp" + +#include "../HeatPumpAirToWater.hpp" +#include "../HeatPumpAirToWater_Impl.hpp" +#include "../HeatPumpAirToWaterHeatingSpeedData.hpp" +#include "../HeatPumpAirToWaterHeatingSpeedData_Impl.hpp" +#include "../HeatPumpAirToWaterCooling.hpp" + +#include "../Model.hpp" +#include "../Schedule.hpp" +#include "../ScheduleConstant.hpp" +#include "../Curve.hpp" +#include "../Curve_Impl.hpp" +#include "../CurveCubic.hpp" +#include "../CurveBicubic.hpp" + +#include "../AirLoopHVAC.hpp" +#include "../PlantLoop.hpp" +#include "../Node.hpp" +#include "../Node_Impl.hpp" +#include "../Splitter.hpp" + +#include "../ModelObjectList.hpp" +#include "../ModelObjectList_Impl.hpp" + +#include + +#include +#include +#include + +using namespace openstudio; +using namespace openstudio::model; + +std::string getObjectNames(const auto& rmed) { + std::vector rm_names; + rm_names.reserve(rmed.size()); + std::transform(rmed.cbegin(), rmed.cend(), std::back_inserter(rm_names), [](const auto& idfObjet) { return idfObjet.nameString(); }); + return fmt::format("Removed objects: {}", rm_names); +}; + +TEST_F(ModelFixture, HeatPumpAirToWaterHeating_GettersSetters) { + Model m; + HeatPumpAirToWaterHeating awhp(m); + + awhp.setName("My HeatPumpAirToWaterHeating"); + + // Availability Schedule Name: Required Object + // Ctor default: Always On + EXPECT_EQ(m.alwaysOnDiscreteSchedule(), awhp.availabilitySchedule()); + ScheduleConstant availabilitySchedule(m); + EXPECT_TRUE(awhp.setAvailabilitySchedule(availabilitySchedule)); + EXPECT_EQ(availabilitySchedule, awhp.availabilitySchedule()); + + // Rated Inlet Air Temperature: Required Double + // Ctor default: 8.0 + EXPECT_EQ(8.0, awhp.ratedInletAirTemperature()); + EXPECT_TRUE(awhp.setRatedInletAirTemperature(0.4)); + EXPECT_EQ(0.4, awhp.ratedInletAirTemperature()); + + // Rated Air Flow Rate: Required Double + // Ctor default: Autosize + EXPECT_TRUE(awhp.isRatedAirFlowRateAutosized()); + // Set + EXPECT_TRUE(awhp.setRatedAirFlowRate(0.5)); + ASSERT_TRUE(awhp.ratedAirFlowRate()); + EXPECT_EQ(0.5, awhp.ratedAirFlowRate().get()); + // Bad Value + EXPECT_FALSE(awhp.setRatedAirFlowRate(-10.0)); + ASSERT_TRUE(awhp.ratedAirFlowRate()); + EXPECT_EQ(0.5, awhp.ratedAirFlowRate().get()); + EXPECT_FALSE(awhp.isRatedAirFlowRateAutosized()); + // Autosize + awhp.autosizeRatedAirFlowRate(); + EXPECT_TRUE(awhp.isRatedAirFlowRateAutosized()); + + // Rated Leaving Water Temperature: Required Double + // Ctor default: 40.0 + EXPECT_EQ(40.0, awhp.ratedLeavingWaterTemperature()); + EXPECT_TRUE(awhp.setRatedLeavingWaterTemperature(0.6)); + EXPECT_EQ(0.6, awhp.ratedLeavingWaterTemperature()); + + // Rated Water Flow Rate: Required Double + // Ctor default: Autosize + EXPECT_TRUE(awhp.isRatedWaterFlowRateAutosized()); + // Set + EXPECT_TRUE(awhp.setRatedWaterFlowRate(0.7)); + ASSERT_TRUE(awhp.ratedWaterFlowRate()); + EXPECT_EQ(0.7, awhp.ratedWaterFlowRate().get()); + // Bad Value + EXPECT_FALSE(awhp.setRatedWaterFlowRate(-10.0)); + ASSERT_TRUE(awhp.ratedWaterFlowRate()); + EXPECT_EQ(0.7, awhp.ratedWaterFlowRate().get()); + EXPECT_FALSE(awhp.isRatedWaterFlowRateAutosized()); + // Autosize + awhp.autosizeRatedWaterFlowRate(); + EXPECT_TRUE(awhp.isRatedWaterFlowRateAutosized()); + + // Minimum Outdoor Air Temperature: Required Double + // Ctor default: -30.0 + EXPECT_EQ(-30.0, awhp.minimumOutdoorAirTemperature()); + EXPECT_TRUE(awhp.setMinimumOutdoorAirTemperature(0.8)); + EXPECT_EQ(0.8, awhp.minimumOutdoorAirTemperature()); + + // Maximum Outdoor Air Temperature: Required Double + // Ctor default: 100.0 + EXPECT_EQ(100.0, awhp.maximumOutdoorAirTemperature()); + EXPECT_TRUE(awhp.setMaximumOutdoorAirTemperature(0.9)); + EXPECT_EQ(0.9, awhp.maximumOutdoorAirTemperature()); + + // Minimum Leaving Water Temperature Curve Name: Optional Object + CurveCubic minimumLeavingWaterTemperatureCurve(m); + EXPECT_TRUE(awhp.setMinimumLeavingWaterTemperatureCurve(minimumLeavingWaterTemperatureCurve)); + ASSERT_TRUE(awhp.minimumLeavingWaterTemperatureCurve()); + EXPECT_EQ(minimumLeavingWaterTemperatureCurve, awhp.minimumLeavingWaterTemperatureCurve().get()); + awhp.resetMinimumLeavingWaterTemperatureCurve(); + EXPECT_FALSE(awhp.minimumLeavingWaterTemperatureCurve()); + + // Maximum Leaving Water Temperature Curve Name: Optional Object + CurveCubic maximumLeavingWaterTemperatureCurve(m); + EXPECT_TRUE(awhp.setMaximumLeavingWaterTemperatureCurve(maximumLeavingWaterTemperatureCurve)); + ASSERT_TRUE(awhp.maximumLeavingWaterTemperatureCurve()); + EXPECT_EQ(maximumLeavingWaterTemperatureCurve, awhp.maximumLeavingWaterTemperatureCurve().get()); + + // Sizing Factor: Required Double + // Ctor default: 1.0 + EXPECT_EQ(1.0, awhp.sizingFactor()); + // Set + EXPECT_TRUE(awhp.setSizingFactor(1.2)); + EXPECT_EQ(1.2, awhp.sizingFactor()); + // Bad Value + EXPECT_FALSE(awhp.setSizingFactor(-10.0)); + EXPECT_EQ(1.2, awhp.sizingFactor()); + + // Booster Mode On Speed: Optional Object + HeatPumpAirToWaterHeatingSpeedData boosterModeOnSpeed(m); + EXPECT_TRUE(awhp.setBoosterModeOnSpeed(boosterModeOnSpeed)); + ASSERT_TRUE(awhp.boosterModeOnSpeed()); + EXPECT_EQ(boosterModeOnSpeed, awhp.boosterModeOnSpeed().get()); + awhp.resetBoosterModeOnSpeed(); + EXPECT_FALSE(awhp.boosterModeOnSpeed()); +} + +TEST_F(ModelFixture, HeatPumpAirToWaterHeating_HeatCoolFuelTypes) { + Model m; + HeatPumpAirToWaterHeating awhp(m); + + EXPECT_EQ(ComponentType(ComponentType::Heating), awhp.componentType()); + testFuelTypeEquality({}, awhp.coolingFuelTypes()); + testFuelTypeEquality({FuelType::Electricity}, awhp.heatingFuelTypes()); + testAppGFuelTypeEquality({AppGFuelType::HeatPump}, awhp.appGHeatingFuelTypes()); +} + +TEST_F(ModelFixture, HeatPumpAirToWaterHeating_Speeds) { + + // create a model to use + Model model; + + HeatPumpAirToWaterHeating awhp(model); + + std::vector speeds; + EXPECT_EQ(5, HeatPumpAirToWaterHeating::maximum_number_of_speeds); + for (unsigned i = 1; i <= HeatPumpAirToWaterHeating::maximum_number_of_speeds; ++i) { + HeatPumpAirToWaterHeatingSpeedData speed(model); + speed.setName("Speed " + std::to_string(i)); + speeds.push_back(speed); + EXPECT_TRUE(awhp.addSpeed(speed)); + EXPECT_EQ(i, awhp.numberOfSpeeds()); + EXPECT_EQ(speeds, awhp.speeds()); + } + + // Can't add more than 5 Speeds + HeatPumpAirToWaterHeatingSpeedData anotherSpeed(model); + EXPECT_FALSE(awhp.addSpeed(anotherSpeed)); + EXPECT_EQ(5, awhp.numberOfSpeeds()); + EXPECT_EQ(speeds, awhp.speeds()); + + // Can't remove a speed that's not in there... + EXPECT_FALSE(awhp.removeSpeed(anotherSpeed)); + EXPECT_EQ(5, awhp.numberOfSpeeds()); + EXPECT_EQ(speeds, awhp.speeds()); + + { + int speedIndex = 3; + std::vector thisSpeeds = awhp.speeds(); + // Explicit copy, so we can keep using it after it's been removed + const auto speedAtIndex = thisSpeeds[speedIndex - 1]; + EXPECT_TRUE(std::find(thisSpeeds.begin(), thisSpeeds.end(), speedAtIndex) != thisSpeeds.end()); + auto optIndex = awhp.speedIndex(speedAtIndex); + ASSERT_TRUE(optIndex); + EXPECT_EQ(speedIndex, optIndex.get()); + EXPECT_TRUE(awhp.removeSpeed(speedIndex)); + EXPECT_EQ(4, awhp.numberOfSpeeds()); + thisSpeeds = awhp.speeds(); + EXPECT_FALSE(std::find(thisSpeeds.begin(), thisSpeeds.end(), speedAtIndex) != thisSpeeds.end()); + // Do the same on our vector, so we're up to date... + speeds.erase(speeds.begin() + speedIndex - 1); + EXPECT_EQ(speeds, awhp.speeds()); + } + + { + int speedIndex = 2; + std::vector thisSpeeds = awhp.speeds(); + // Explicit copy, so we can keep using it after it's been removed + const auto speedAtIndex = thisSpeeds[speedIndex - 1]; + EXPECT_TRUE(std::find(thisSpeeds.begin(), thisSpeeds.end(), speedAtIndex) != thisSpeeds.end()); + auto optIndex = awhp.speedIndex(speedAtIndex); + ASSERT_TRUE(optIndex); + EXPECT_EQ(speedIndex, optIndex.get()); + EXPECT_TRUE(awhp.removeSpeed(speedAtIndex)); + EXPECT_EQ(3, awhp.numberOfSpeeds()); + thisSpeeds = awhp.speeds(); + EXPECT_FALSE(std::find(thisSpeeds.begin(), thisSpeeds.end(), speedAtIndex) != thisSpeeds.end()); + // Do the same on our vector, so we're up to date... + speeds.erase(std::find(speeds.begin(), speeds.end(), speedAtIndex)); + EXPECT_EQ(speeds, awhp.speeds()); + } + + { + const auto& speedAtEnd = speeds.back(); + auto optIndex = awhp.speedIndex(speedAtEnd); + ASSERT_TRUE(optIndex); + EXPECT_EQ(awhp.numberOfSpeeds(), optIndex.get()); + + EXPECT_TRUE(awhp.setSpeedIndex(speedAtEnd, 2)); + std::vector thisSpeeds = awhp.speeds(); + optIndex = awhp.speedIndex(speedAtEnd); + ASSERT_TRUE(optIndex); + EXPECT_EQ(2, optIndex.get()); + EXPECT_EQ(3, awhp.numberOfSpeeds()); + for (unsigned i = 1; i <= awhp.numberOfSpeeds(); ++i) { + if (i < optIndex.get()) { + EXPECT_EQ(speeds[i - 1], awhp.speeds()[i - 1]); + } else if (i > optIndex.get()) { + EXPECT_EQ(speeds[i - 2], awhp.speeds()[i - 1]); + } + } + } + + awhp.removeAllSpeeds(); + EXPECT_EQ(0, awhp.numberOfSpeeds()); + + EXPECT_TRUE(awhp.setSpeeds(speeds)); + EXPECT_EQ(3, awhp.numberOfSpeeds()); + EXPECT_EQ(speeds, awhp.speeds()); + + for (unsigned i = 6; i <= 8; ++i) { + HeatPumpAirToWaterHeatingSpeedData speed(model); + awhp.setName("Speed " + std::to_string(i)); + speeds.push_back(speed); + } + EXPECT_EQ(6, speeds.size()); + awhp.removeAllSpeeds(); + EXPECT_TRUE(awhp.addSpeed(anotherSpeed)); + + // This should clear, then assign the first 5, but then return false since the 6th failed + EXPECT_FALSE(awhp.setSpeeds(speeds)); + EXPECT_EQ(5, awhp.numberOfSpeeds()); + { + std::vector thisSpeeds = awhp.speeds(); + for (unsigned i = 0; i < 5; ++i) { + EXPECT_EQ(speeds[i], thisSpeeds[i]); + } + } + + awhp.removeAllSpeeds(); + EXPECT_EQ(0, awhp.numberOfSpeeds()); + EXPECT_EQ(0, awhp.speeds().size()); + + // Test that added a speed from another model will fail but not add a blank extensible group + Model model2; + HeatPumpAirToWaterHeatingSpeedData speedFromAnotherModel(model2); + EXPECT_FALSE(awhp.addSpeed(speedFromAnotherModel)); + EXPECT_EQ(0, awhp.numExtensibleGroups()); + EXPECT_EQ(0, awhp.numberOfSpeeds()); + EXPECT_EQ(0, awhp.speeds().size()); +} + +TEST_F(ModelFixture, HeatPumpAirToWaterHeating_clone) { + + // create a model to use + Model model; + + HeatPumpAirToWaterHeating awhp(model); + + std::vector speeds; + for (unsigned i = 1; i <= HeatPumpAirToWaterHeating::maximum_number_of_speeds; ++i) { + HeatPumpAirToWaterHeatingSpeedData speed(model); + speed.setName("Speed " + std::to_string(i)); + speeds.push_back(speed); + EXPECT_TRUE(awhp.addSpeed(speed)); + } + + EXPECT_EQ(5, awhp.numberOfSpeeds()); + EXPECT_EQ(speeds, awhp.speeds()); + + EXPECT_EQ(1, model.getConcreteModelObjects().size()); + EXPECT_EQ(1, model.getConcreteModelObjects().size()); + EXPECT_EQ(5, model.getConcreteModelObjects().size()); + EXPECT_EQ(5 * 3, model.getModelObjects().size()); + + auto awhpClone = awhp.clone(model).cast(); + EXPECT_EQ(2, model.getConcreteModelObjects().size()); + EXPECT_EQ(2, model.getConcreteModelObjects().size()); + EXPECT_EQ(5, model.getConcreteModelObjects().size()); + EXPECT_EQ(5 * 3, model.getModelObjects().size()); + + EXPECT_EQ(5, awhp.numberOfSpeeds()); + EXPECT_EQ(speeds, awhp.speeds()); + + EXPECT_EQ(5, awhpClone.numberOfSpeeds()); + EXPECT_EQ(speeds, awhpClone.speeds()); + + auto rmed = awhp.remove(); + + EXPECT_EQ(2, rmed.size()) << getObjectNames(rmed); + EXPECT_EQ(IddObjectType::OS_HeatPump_AirToWater_Heating, rmed[0].iddObject().type().value()); + EXPECT_EQ(IddObjectType::OS_ModelObjectList, rmed[1].iddObject().type().value()); + EXPECT_EQ(1, model.getConcreteModelObjects().size()); + EXPECT_EQ(1, model.getConcreteModelObjects().size()); + EXPECT_EQ(5, model.getConcreteModelObjects().size()); + EXPECT_EQ(5 * 3, model.getModelObjects().size()); + EXPECT_EQ(5, awhpClone.numberOfSpeeds()); + EXPECT_EQ(speeds, awhpClone.speeds()); + + rmed = awhpClone.remove(); + + const unsigned expectedRemoved = 1 /* HeatPumpAirToWaterHeating */ + 1 /* ModelObjectList */ + 5 * (1 /* SpeedData */ + 3 /* Curves */); + EXPECT_EQ(expectedRemoved, rmed.size()) << getObjectNames(rmed); + EXPECT_EQ(0, model.getConcreteModelObjects().size()); + EXPECT_EQ(0, model.getConcreteModelObjects().size()); + EXPECT_EQ(0, model.getConcreteModelObjects().size()); + EXPECT_EQ(0, model.getModelObjects().size()); +} + +TEST_F(ModelFixture, HeatPumpAirToWaterHeating_addToNode) { + // create a model to use + Model m; + + HeatPumpAirToWaterHeating awhp(m); + + PlantLoop p(m); + { + Node n = p.demandInletNode(); + EXPECT_FALSE(awhp.addToNode(n)); + } + EXPECT_FALSE(p.addDemandBranchForComponent(awhp)); + + AirLoopHVAC a(m); + { + Node n = a.supplyInletNode(); + EXPECT_FALSE(awhp.addToNode(n)); + } + { + Node n = a.demandInletNode(); + EXPECT_FALSE(awhp.addToNode(n)); + } + + EXPECT_FALSE(awhp.plantLoop()); + EXPECT_FALSE(awhp.inletModelObject()); + EXPECT_FALSE(awhp.outletModelObject()); + + // Plant Side connections + EXPECT_EQ(5, p.demandComponents().size()); // o --- Splitter --- o --- Mixer --- o + EXPECT_EQ(5, p.supplyComponents().size()); // o --- Splitter --- o --- Mixer --- o + + EXPECT_TRUE(p.addSupplyBranchForComponent(awhp)); + EXPECT_EQ(7, p.supplyComponents().size()); // o --- Splitter --- o --- awhp_hc --- Mixer --- o + EXPECT_TRUE(awhp.plantLoop()); + EXPECT_TRUE(awhp.inletModelObject()); + EXPECT_TRUE(awhp.outletModelObject()); + + auto awhpClone = awhp.clone(m).cast(); + EXPECT_FALSE(awhpClone.plantLoop()); + EXPECT_FALSE(awhpClone.inletModelObject()); + EXPECT_FALSE(awhpClone.outletModelObject()); + + EXPECT_EQ(2, m.getConcreteModelObjects().size()); + + awhp.removeFromLoop(); + EXPECT_FALSE(awhp.plantLoop()); + EXPECT_FALSE(awhp.inletModelObject()); + EXPECT_FALSE(awhp.outletModelObject()); + + EXPECT_EQ(2, m.getConcreteModelObjects().size()); + EXPECT_EQ(5, p.supplyComponents().size()); // o --- Splitter --- o --- Mixer --- o + EXPECT_EQ(5, p.demandComponents().size()); // o --- Splitter --- o --- Mixer --- o + + auto supply_inlet_node = p.supplyInletNode(); + EXPECT_TRUE(awhpClone.addToNode(supply_inlet_node)); + + EXPECT_EQ(7, p.supplyComponents().size()); // o --- awhpClone --- Splitter --- o --- Mixer --- o + EXPECT_EQ(5, p.demandComponents().size()); // o --- Splitter --- o --- Mixer --- o + EXPECT_TRUE(awhpClone.plantLoop()); + ASSERT_TRUE(awhpClone.inletModelObject()); + EXPECT_EQ(supply_inlet_node, awhpClone.inletModelObject().get()); + ASSERT_TRUE(awhpClone.outletModelObject()); + auto outlet_ = awhpClone.outletModelObject().get().optionalCast(); + ASSERT_TRUE(outlet_); + ASSERT_TRUE(outlet_->outletModelObject()); + EXPECT_EQ(p.supplySplitter(), outlet_->outletModelObject().get()); + + awhpClone.remove(); + EXPECT_EQ(1, m.getConcreteModelObjects().size()); + EXPECT_EQ(5, p.supplyComponents().size()); // o --- Splitter --- o --- Mixer --- o + EXPECT_EQ(5, p.demandComponents().size()); // o --- Splitter --- o --- Mixer --- o +} + +TEST_F(ModelFixture, HeatPumpAirToWaterHeating_containingHVACComponent) { + + Model m; + + HeatPumpAirToWater awhp(m); + HeatPumpAirToWater awhp2(m); + + HeatPumpAirToWaterHeating awhp_hc(m); + for (unsigned i = 1; i <= HeatPumpAirToWaterHeating::maximum_number_of_speeds; ++i) { + HeatPumpAirToWaterHeatingSpeedData speed(m); + speed.setName("Heating Speed " + std::to_string(i)); + EXPECT_TRUE(awhp_hc.addSpeed(speed)); + } + EXPECT_FALSE(awhp_hc.containingHVACComponent()); + EXPECT_FALSE(awhp_hc.heatPumpAirToWater()); + + EXPECT_TRUE(awhp.setHeatingOperationMode(awhp_hc)); + ASSERT_TRUE(awhp_hc.containingHVACComponent()); + EXPECT_EQ(awhp, awhp_hc.containingHVACComponent().get()); + ASSERT_TRUE(awhp_hc.heatPumpAirToWater()); + EXPECT_EQ(awhp, awhp_hc.heatPumpAirToWater().get()); + EXPECT_EQ(5, m.getConcreteModelObjects().size()); + // Each speed has 3 curves + EXPECT_EQ(5 * 3, m.getModelObjects().size()); + + // Already assigned to another AWHP, not letting it be assigned again + EXPECT_FALSE(awhp2.setHeatingOperationMode(awhp_hc)); + ASSERT_TRUE(awhp_hc.containingHVACComponent()); + ASSERT_TRUE(awhp_hc.heatPumpAirToWater()); + EXPECT_EQ(awhp, awhp_hc.heatPumpAirToWater().get()); + + EXPECT_TRUE(awhp.heatingOperationMode()); + EXPECT_FALSE(awhp2.heatingOperationMode()); + EXPECT_FALSE(awhp.coolingOperationMode()); + EXPECT_FALSE(awhp2.coolingOperationMode()); + + // Is contained so not removable + EXPECT_FALSE(awhp_hc.isRemovable()); + + EXPECT_EQ(2, m.getConcreteModelObjects().size()); + EXPECT_EQ(1, m.getConcreteModelObjects().size()); + EXPECT_EQ(1, m.getConcreteModelObjects().size()); + EXPECT_EQ(5, m.getConcreteModelObjects().size()); + EXPECT_EQ(5 * 3, m.getModelObjects().size()); + + EXPECT_EQ(0, awhp_hc.remove().size()); + EXPECT_EQ(2, m.getConcreteModelObjects().size()); + EXPECT_EQ(1, m.getConcreteModelObjects().size()); + EXPECT_EQ(1, m.getConcreteModelObjects().size()); + EXPECT_EQ(5, m.getConcreteModelObjects().size()); + EXPECT_EQ(5 * 3, m.getModelObjects().size()); + + // Remove the second awhp + auto rmed = awhp2.remove(); + EXPECT_EQ(1, rmed.size()) << getObjectNames(rmed); + EXPECT_TRUE(awhp_hc.heatPumpAirToWater()); + ASSERT_TRUE(awhp_hc.containingHVACComponent()); + EXPECT_EQ(awhp, awhp_hc.containingHVACComponent().get()); + EXPECT_EQ(1, m.getConcreteModelObjects().size()); + EXPECT_EQ(1, m.getConcreteModelObjects().size()); + EXPECT_EQ(1, m.getConcreteModelObjects().size()); + EXPECT_EQ(5, m.getConcreteModelObjects().size()); + EXPECT_EQ(5 * 3, m.getModelObjects().size()); + + // Still not removable + EXPECT_FALSE(awhp_hc.isRemovable()); + rmed = awhp_hc.remove(); + EXPECT_EQ(0, rmed.size()) << getObjectNames(rmed); + EXPECT_EQ(1, m.getConcreteModelObjects().size()); + EXPECT_EQ(1, m.getConcreteModelObjects().size()); + EXPECT_EQ(1, m.getConcreteModelObjects().size()); + EXPECT_EQ(5, m.getConcreteModelObjects().size()); + EXPECT_EQ(5 * 3, m.getModelObjects().size()); + + // Remove the first awhp: this time it's gone! + rmed = awhp.remove(); + const unsigned expectedRemoved = + 1 /* HeatPumpAirToWater */ + 1 /* HeatPumpAirToWaterHeating */ + 1 /* ModelObjectList */ + 5 * (1 /* SpeedData */ + 3 /* Curves */); + EXPECT_EQ(expectedRemoved, rmed.size()) << getObjectNames(rmed); + EXPECT_EQ(0, m.getConcreteModelObjects().size()); + EXPECT_EQ(0, m.getConcreteModelObjects().size()); + EXPECT_EQ(0, m.getConcreteModelObjects().size()); + EXPECT_EQ(0, m.getConcreteModelObjects().size()); + EXPECT_EQ(0, m.getModelObjects().size()); +} diff --git a/src/model/test/HeatPumpAirToWater_GTest.cpp b/src/model/test/HeatPumpAirToWater_GTest.cpp new file mode 100644 index 0000000000..91355e3dd9 --- /dev/null +++ b/src/model/test/HeatPumpAirToWater_GTest.cpp @@ -0,0 +1,323 @@ +/*********************************************************************************************************************** +* OpenStudio(R), Copyright (c) Alliance for Sustainable Energy, LLC. +* See also https://openstudio.net/license +***********************************************************************************************************************/ + +#include "ModelFixture.hpp" + +#include "../HeatPumpAirToWater.hpp" +#include "../HeatPumpAirToWater_Impl.hpp" + +#include "../Schedule.hpp" +#include "../ScheduleConstant.hpp" + +#include "../Curve.hpp" +#include "../Curve_Impl.hpp" +#include "../CurveCubic.hpp" +#include "../CurveBicubic.hpp" + +#include "../AirLoopHVAC.hpp" +#include "../Node.hpp" +#include "../PlantLoop.hpp" + +#include "../HeatPumpAirToWaterHeating.hpp" +#include "../HeatPumpAirToWaterHeating_Impl.hpp" +#include "../HeatPumpAirToWaterHeatingSpeedData.hpp" +#include "../HeatPumpAirToWaterHeatingSpeedData_Impl.hpp" + +#include "../HeatPumpAirToWaterCooling.hpp" +#include "../HeatPumpAirToWaterCooling_Impl.hpp" +#include "../HeatPumpAirToWaterCoolingSpeedData.hpp" +#include "../HeatPumpAirToWaterCoolingSpeedData_Impl.hpp" + +using namespace openstudio; +using namespace openstudio::model; + +TEST_F(ModelFixture, HeatPumpAirToWater_GettersSetters) { + Model m; + HeatPumpAirToWater heatPumpAirToWater(m); + + heatPumpAirToWater.setName("My HeatPumpAirToWater"); + + // Operating Mode Control Method: Required String + // Ctor default + EXPECT_EQ("Load", heatPumpAirToWater.operatingModeControlMethod()); + EXPECT_FALSE(heatPumpAirToWater.setOperatingModeControlMethod("ScheduledModes")); + EXPECT_TRUE(heatPumpAirToWater.setOperatingModeControlMethod("EMSControlled")); + EXPECT_EQ("EMSControlled", heatPumpAirToWater.operatingModeControlMethod()); + // Bad Value + EXPECT_FALSE(heatPumpAirToWater.setOperatingModeControlMethod("BADENUM")); + EXPECT_EQ("EMSControlled", heatPumpAirToWater.operatingModeControlMethod()); + + // Operating Mode Control Option for Multiple Unit: Required String + // Ctor default + EXPECT_EQ("SingleMode", heatPumpAirToWater.operatingModeControlOptionforMultipleUnit()); + EXPECT_TRUE(heatPumpAirToWater.setOperatingModeControlOptionforMultipleUnit("CoolingPriority")); + EXPECT_EQ("CoolingPriority", heatPumpAirToWater.operatingModeControlOptionforMultipleUnit()); + // Bad Value + EXPECT_FALSE(heatPumpAirToWater.setOperatingModeControlOptionforMultipleUnit("BADENUM")); + EXPECT_EQ("CoolingPriority", heatPumpAirToWater.operatingModeControlOptionforMultipleUnit()); + + // Operating Mode Control Schedule Name: Optional Object + EXPECT_FALSE(heatPumpAirToWater.operatingModeControlSchedule()); + EXPECT_EQ("EMSControlled", heatPumpAirToWater.operatingModeControlMethod()); + ScheduleConstant operatingModeControlSchedule(m); + EXPECT_TRUE(heatPumpAirToWater.setOperatingModeControlSchedule(operatingModeControlSchedule)); + ASSERT_TRUE(heatPumpAirToWater.operatingModeControlSchedule()); + EXPECT_EQ(operatingModeControlSchedule, heatPumpAirToWater.operatingModeControlSchedule().get()); + EXPECT_EQ("ScheduledModes", heatPumpAirToWater.operatingModeControlMethod()); + // Reset + heatPumpAirToWater.resetOperatingModeControlSchedule(); + EXPECT_FALSE(heatPumpAirToWater.operatingModeControlSchedule()); + EXPECT_EQ("Load", heatPumpAirToWater.operatingModeControlMethod()); // Back to default + + // Minimum Part Load Ratio: Required Double + // Ctor default + EXPECT_EQ(0.0, heatPumpAirToWater.minimumPartLoadRatio()); + EXPECT_TRUE(heatPumpAirToWater.setMinimumPartLoadRatio(0.6)); + EXPECT_EQ(0.6, heatPumpAirToWater.minimumPartLoadRatio()); + // Bad Value + EXPECT_FALSE(heatPumpAirToWater.setMinimumPartLoadRatio(-10.0)); + EXPECT_EQ(0.6, heatPumpAirToWater.minimumPartLoadRatio()); + + // Air Inlet Node Name: Optional String + EXPECT_FALSE(heatPumpAirToWater.airInletNodeName()); + EXPECT_TRUE(heatPumpAirToWater.setAirInletNodeName("AWHP Air Inlet Node")); + ASSERT_TRUE(heatPumpAirToWater.airInletNodeName()); + EXPECT_EQ("AWHP Air Inlet Node", heatPumpAirToWater.airInletNodeName().get()); + + // Air Outlet Node Name: Optional String + EXPECT_FALSE(heatPumpAirToWater.airOutletNodeName()); + EXPECT_TRUE(heatPumpAirToWater.setAirOutletNodeName("AWHP Air Outlet Node")); + ASSERT_TRUE(heatPumpAirToWater.airOutletNodeName()); + EXPECT_EQ("AWHP Air Outlet Node", heatPumpAirToWater.airOutletNodeName().get()); + + // Maximum Outdoor Dry Bulb Temperature For Defrost Operation: Required Double + // Ctor default + EXPECT_EQ(10.0, heatPumpAirToWater.maximumOutdoorDryBulbTemperatureForDefrostOperation()); + EXPECT_TRUE(heatPumpAirToWater.setMaximumOutdoorDryBulbTemperatureForDefrostOperation(0.9)); + EXPECT_EQ(0.9, heatPumpAirToWater.maximumOutdoorDryBulbTemperatureForDefrostOperation()); + + // Heat Pump Defrost Control: Required String + // Ctor default + EXPECT_EQ("None", heatPumpAirToWater.heatPumpDefrostControl()); + EXPECT_TRUE(heatPumpAirToWater.setHeatPumpDefrostControl("TimedEmpirical")); + EXPECT_EQ("TimedEmpirical", heatPumpAirToWater.heatPumpDefrostControl()); + // Bad Value + EXPECT_FALSE(heatPumpAirToWater.setHeatPumpDefrostControl("BADENUM")); + EXPECT_EQ("TimedEmpirical", heatPumpAirToWater.heatPumpDefrostControl()); + + // Heat Pump Defrost Time Period Fraction: Required Double + // Ctor default + EXPECT_EQ(0.058333, heatPumpAirToWater.heatPumpDefrostTimePeriodFraction()); + EXPECT_TRUE(heatPumpAirToWater.setHeatPumpDefrostTimePeriodFraction(1.1)); + EXPECT_EQ(1.1, heatPumpAirToWater.heatPumpDefrostTimePeriodFraction()); + // Bad Value + EXPECT_FALSE(heatPumpAirToWater.setHeatPumpDefrostTimePeriodFraction(-10.0)); + EXPECT_EQ(1.1, heatPumpAirToWater.heatPumpDefrostTimePeriodFraction()); + + // Resistive Defrost Heater Capacity: Required Double + // Ctor default + EXPECT_EQ(0.0, heatPumpAirToWater.resistiveDefrostHeaterCapacity()); + // Set + EXPECT_TRUE(heatPumpAirToWater.setResistiveDefrostHeaterCapacity(1.2)); + EXPECT_EQ(1.2, heatPumpAirToWater.resistiveDefrostHeaterCapacity()); + // Bad Value + EXPECT_FALSE(heatPumpAirToWater.setResistiveDefrostHeaterCapacity(-10.0)); + EXPECT_EQ(1.2, heatPumpAirToWater.resistiveDefrostHeaterCapacity()); + + // Defrost Energy Input Ratio Function of Temperature Curve Name: Optional Object, BivariateFunctions + CurveBicubic defrostEnergyInputRatioFunctionofTemperatureCurve(m); + EXPECT_TRUE(heatPumpAirToWater.setDefrostEnergyInputRatioFunctionofTemperatureCurve(defrostEnergyInputRatioFunctionofTemperatureCurve)); + ASSERT_TRUE(heatPumpAirToWater.defrostEnergyInputRatioFunctionofTemperatureCurve()); + EXPECT_EQ(defrostEnergyInputRatioFunctionofTemperatureCurve, heatPumpAirToWater.defrostEnergyInputRatioFunctionofTemperatureCurve().get()); + { + // Bad dimensions + CurveCubic badCurve(m); + EXPECT_FALSE(heatPumpAirToWater.setDefrostEnergyInputRatioFunctionofTemperatureCurve(badCurve)); + ASSERT_TRUE(heatPumpAirToWater.defrostEnergyInputRatioFunctionofTemperatureCurve()); + EXPECT_EQ(defrostEnergyInputRatioFunctionofTemperatureCurve, heatPumpAirToWater.defrostEnergyInputRatioFunctionofTemperatureCurve().get()); + } + + // Heat Pump Multiplier: Required Integer + // Ctor default + EXPECT_EQ(1, heatPumpAirToWater.heatPumpMultiplier()); + EXPECT_TRUE(heatPumpAirToWater.setHeatPumpMultiplier(14)); + EXPECT_EQ(14, heatPumpAirToWater.heatPumpMultiplier()); + + // Control Type: Required String + // Ctor default + EXPECT_EQ("VariableSpeed", heatPumpAirToWater.controlType()); + EXPECT_TRUE(heatPumpAirToWater.setControlType("FixedSpeed")); + EXPECT_EQ("FixedSpeed", heatPumpAirToWater.controlType()); + // Bad Value + EXPECT_FALSE(heatPumpAirToWater.setControlType("BADENUM")); + EXPECT_EQ("FixedSpeed", heatPumpAirToWater.controlType()); + + // Crankcase Heater Capacity: Required Double + // Ctor default + EXPECT_EQ(0.0, heatPumpAirToWater.crankcaseHeaterCapacity()); + EXPECT_TRUE(heatPumpAirToWater.setCrankcaseHeaterCapacity(1.6)); + EXPECT_EQ(1.6, heatPumpAirToWater.crankcaseHeaterCapacity()); + // Bad Value + EXPECT_FALSE(heatPumpAirToWater.setCrankcaseHeaterCapacity(-10.0)); + EXPECT_EQ(1.6, heatPumpAirToWater.crankcaseHeaterCapacity()); + + // Crankcase Heater Capacity Function of Temperature Curve Name: Optional Object, UnivariateFunctions + EXPECT_FALSE(heatPumpAirToWater.crankcaseHeaterCapacityFunctionofTemperatureCurve()); + CurveCubic crankcaseHeaterCapacityFunctionofTemperatureCurve(m); + EXPECT_TRUE(heatPumpAirToWater.setCrankcaseHeaterCapacityFunctionofTemperatureCurve(crankcaseHeaterCapacityFunctionofTemperatureCurve)); + ASSERT_TRUE(heatPumpAirToWater.crankcaseHeaterCapacityFunctionofTemperatureCurve()); + EXPECT_EQ(crankcaseHeaterCapacityFunctionofTemperatureCurve, heatPumpAirToWater.crankcaseHeaterCapacityFunctionofTemperatureCurve().get()); + // Bad dimensions + { + CurveBicubic badCurve(m); + EXPECT_FALSE(heatPumpAirToWater.setCrankcaseHeaterCapacityFunctionofTemperatureCurve(badCurve)); + ASSERT_TRUE(heatPumpAirToWater.crankcaseHeaterCapacityFunctionofTemperatureCurve()); + EXPECT_EQ(crankcaseHeaterCapacityFunctionofTemperatureCurve, heatPumpAirToWater.crankcaseHeaterCapacityFunctionofTemperatureCurve().get()); + } + + // Maximum Ambient Temperature for Crankcase Heater Operation: Required Double + // Ctor default + EXPECT_EQ(10.0, heatPumpAirToWater.maximumAmbientTemperatureforCrankcaseHeaterOperation()); + EXPECT_TRUE(heatPumpAirToWater.setMaximumAmbientTemperatureforCrankcaseHeaterOperation(1.8)); + EXPECT_EQ(1.8, heatPumpAirToWater.maximumAmbientTemperatureforCrankcaseHeaterOperation()); + // Bad Value + EXPECT_FALSE(heatPumpAirToWater.setMaximumAmbientTemperatureforCrankcaseHeaterOperation(-10.0)); + EXPECT_EQ(1.8, heatPumpAirToWater.maximumAmbientTemperatureforCrankcaseHeaterOperation()); + + // Heating Operation Mode: Optional Object + HeatPumpAirToWaterHeating heatingOperationMode(m); + EXPECT_TRUE(heatPumpAirToWater.setHeatingOperationMode(heatingOperationMode)); + ASSERT_TRUE(heatPumpAirToWater.heatingOperationMode()); + EXPECT_EQ(heatingOperationMode, heatPumpAirToWater.heatingOperationMode().get()); + + // Cooling Operation Mode: Required Object + HeatPumpAirToWaterCooling coolingOperationMode(m); + EXPECT_TRUE(heatPumpAirToWater.setCoolingOperationMode(coolingOperationMode)); + EXPECT_EQ(coolingOperationMode, heatPumpAirToWater.coolingOperationMode().get()); +} + +TEST_F(ModelFixture, HeatPumpAirToWater_HeatCoolFuelTypes) { + Model m; + HeatPumpAirToWater heatPumpAirToWater(m); + + // No Cooling No Heating yet + EXPECT_EQ(ComponentType(ComponentType::None), heatPumpAirToWater.componentType()); + testFuelTypeEquality({}, heatPumpAirToWater.coolingFuelTypes()); + testFuelTypeEquality({}, heatPumpAirToWater.heatingFuelTypes()); + testAppGFuelTypeEquality({}, heatPumpAirToWater.appGHeatingFuelTypes()); + + HeatPumpAirToWaterCooling coolingOperationMode(m); + EXPECT_TRUE(heatPumpAirToWater.setCoolingOperationMode(coolingOperationMode)); + EXPECT_EQ(ComponentType(ComponentType::Cooling), heatPumpAirToWater.componentType()); + testFuelTypeEquality({FuelType::Electricity}, heatPumpAirToWater.coolingFuelTypes()); + testFuelTypeEquality({}, heatPumpAirToWater.heatingFuelTypes()); + testAppGFuelTypeEquality({}, heatPumpAirToWater.appGHeatingFuelTypes()); + + HeatPumpAirToWaterHeating heatingOperationMode(m); + EXPECT_TRUE(heatPumpAirToWater.setHeatingOperationMode(heatingOperationMode)); + EXPECT_EQ(ComponentType(ComponentType::Both), heatPumpAirToWater.componentType()); + testFuelTypeEquality({FuelType::Electricity}, heatPumpAirToWater.coolingFuelTypes()); + testFuelTypeEquality({FuelType::Electricity}, heatPumpAirToWater.heatingFuelTypes()); + testAppGFuelTypeEquality({AppGFuelType::HeatPump}, heatPumpAirToWater.appGHeatingFuelTypes()); + + heatPumpAirToWater.resetCoolingOperationMode(); + EXPECT_EQ(ComponentType(ComponentType::Heating), heatPumpAirToWater.componentType()); + testFuelTypeEquality({}, heatPumpAirToWater.coolingFuelTypes()); + testFuelTypeEquality({FuelType::Electricity}, heatPumpAirToWater.heatingFuelTypes()); + testAppGFuelTypeEquality({AppGFuelType::HeatPump}, heatPumpAirToWater.appGHeatingFuelTypes()); +} + +TEST_F(ModelFixture, HeatPumpAirToWater_Loop) { + Model m; + HeatPumpAirToWater awhp(m); + HeatPumpAirToWaterCooling awhp_cc(m); + EXPECT_TRUE(awhp.setCoolingOperationMode(awhp_cc)); + HeatPumpAirToWaterHeating awhp_hc(m); + EXPECT_TRUE(awhp.setHeatingOperationMode(awhp_hc)); + + PlantLoop chwLoop(m); + { + Node n = chwLoop.supplyInletNode(); + EXPECT_FALSE(awhp.addToNode(n)); + } + { + Node n = chwLoop.demandInletNode(); + EXPECT_FALSE(awhp.addToNode(n)); + } + EXPECT_FALSE(chwLoop.addSupplyBranchForComponent(awhp)); + EXPECT_FALSE(chwLoop.addDemandBranchForComponent(awhp)); + + AirLoopHVAC a(m); + { + Node n = a.supplyInletNode(); + EXPECT_FALSE(awhp.addToNode(n)); + } + { + Node n = a.demandInletNode(); + EXPECT_FALSE(awhp.addToNode(n)); + } + + EXPECT_FALSE(awhp.coolingLoop()); + EXPECT_FALSE(awhp.heatingLoop()); + + EXPECT_TRUE(chwLoop.addSupplyBranchForComponent(awhp_cc)); + ASSERT_TRUE(awhp.coolingLoop()); + EXPECT_EQ(chwLoop, awhp.coolingLoop().get()); + EXPECT_FALSE(awhp.heatingLoop()); + + PlantLoop hwLoop(m); + EXPECT_TRUE(hwLoop.addSupplyBranchForComponent(awhp_hc)); + ASSERT_TRUE(awhp.coolingLoop()); + EXPECT_EQ(chwLoop, awhp.coolingLoop().get()); + ASSERT_TRUE(awhp.heatingLoop()); + EXPECT_EQ(hwLoop, awhp.heatingLoop().get()); +} + +TEST_F(ModelFixture, HeatPumpAirToWater_Clone) { + Model m; + HeatPumpAirToWater awhp(m); + HeatPumpAirToWaterCooling awhp_cc(m); + EXPECT_TRUE(awhp.setCoolingOperationMode(awhp_cc)); + HeatPumpAirToWaterHeating awhp_hc(m); + EXPECT_TRUE(awhp.setHeatingOperationMode(awhp_hc)); + + for (unsigned i = 1; i <= HeatPumpAirToWaterCooling::maximum_number_of_speeds; ++i) { + HeatPumpAirToWaterCoolingSpeedData speed(m); + speed.setName("Cooling Speed " + std::to_string(i)); + EXPECT_TRUE(awhp_cc.addSpeed(speed)); + } + for (unsigned i = 1; i <= HeatPumpAirToWaterHeating::maximum_number_of_speeds; ++i) { + HeatPumpAirToWaterHeatingSpeedData speed(m); + speed.setName("Heating Speed " + std::to_string(i)); + EXPECT_TRUE(awhp_hc.addSpeed(speed)); + } + + EXPECT_EQ(1, m.getConcreteModelObjects().size()); + EXPECT_EQ(1, m.getConcreteModelObjects().size()); + EXPECT_EQ(5, m.getConcreteModelObjects().size()); + EXPECT_EQ(1, m.getConcreteModelObjects().size()); + EXPECT_EQ(5, m.getConcreteModelObjects().size()); + // Each speed has 3 curves + EXPECT_EQ((5 + 5) * 3, m.getModelObjects().size()); + + auto awhpClone = awhp.clone(m).cast(); + EXPECT_EQ(2, m.getConcreteModelObjects().size()); + EXPECT_EQ(2, m.getConcreteModelObjects().size()); + EXPECT_EQ(5, m.getConcreteModelObjects().size()); + EXPECT_EQ(2, m.getConcreteModelObjects().size()); + EXPECT_EQ(5, m.getConcreteModelObjects().size()); + EXPECT_EQ((5 + 5) * 3, m.getModelObjects().size()); + + { + Model m2; + auto awhpClone2 = awhp.clone(m2).cast(); + EXPECT_EQ(1, m2.getConcreteModelObjects().size()); + EXPECT_EQ(1, m2.getConcreteModelObjects().size()); + EXPECT_EQ(5, m2.getConcreteModelObjects().size()); + EXPECT_EQ(1, m2.getConcreteModelObjects().size()); + EXPECT_EQ(5, m2.getConcreteModelObjects().size()); + EXPECT_EQ((5 + 5) * 3, m.getModelObjects().size()); + } +}