From e1e512c6aede97bd38b8c8d2ff6d8a9192f0b5b1 Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Mon, 6 Oct 2025 13:25:46 +0200 Subject: [PATCH 01/45] Naive add HeatPump:AirToWater IDDs --- resources/energyplus/ProposedEnergy+.idd | 2 +- resources/model/OpenStudio.idd | 560 +++++++++++++++++++++++ 2 files changed, 561 insertions(+), 1 deletion(-) diff --git a/resources/energyplus/ProposedEnergy+.idd b/resources/energyplus/ProposedEnergy+.idd index 994893c233..166b3cf07e 100644 --- a/resources/energyplus/ProposedEnergy+.idd +++ b/resources/energyplus/ProposedEnergy+.idd @@ -27458,7 +27458,7 @@ ZoneHVAC:HighTemperatureRadiant, \default 2.0 A7 , \field Heating Setpoint Temperature Schedule Name \required-field - \note This setpoint is a "mean air temperature", a "mean radiant temperature" or an + \note This setpoint is a "mean air temperature", a "mean radiant temperature" or an \note "operative temperature" setpoint depending on the control type \type object-list \object-list ScheduleNames diff --git a/resources/model/OpenStudio.idd b/resources/model/OpenStudio.idd index 552a82a5ad..2548172ad2 100644 --- a/resources/model/OpenStudio.idd +++ b/resources/model/OpenStudio.idd @@ -14878,6 +14878,566 @@ 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 57 + A1 , \field Handle + \type handle + \required-field + A2 , \field Name + \required-field + \type alpha + \note Enter a unique name for this air-to-water heat pump + A3 , \field Availability Schedule Name Heating + \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 + A4 , \field Availability Schedule Name Cooling + \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 + A5 , \field Operating Mode Control Method + \type choice + \key ScheduledModes + \key EMSControlled + \key Load + \default Load + A6, \field Operating Mode Control Option for Multiple Unit + \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 + \default SingleMode + \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 + A7 , \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 + N2 , \field Rated Inlet Air Temperature in Heating Mode + \type real + \units C + \default 8 + \note inlet air dry-bulb temperature corresponding to rated heat pump performance + \note (capacity, COP). + N3 , \field Rated Air Flow Rate in Heating Mode + \type real + \units m3/s + \minimum> 0.0 + \autosizable + \default autosize + \note air flow rate corresponding to rated heat pump performance + \note (capacity, COP). + N4 , \field Rated Leaving Water Temperature in Heating Mode + \type real + \units C + \default 40 + \note outlet water temperature corresponding to rated heat pump performance + \note (heating capacity, COP). + N5 , \field Rated Water Flow Rate in Heating Mode + \type real + \units m3/s + \ip-units gal/min + \minimum> 0.0 + \autosizable + \default autosize + \note Condenser water flow rate corresponding to rated heat pump performance + \note (capacity, COP). + N6, \field Minimum Outdoor Air Temperature in Heating Mode + \type real + \units C + \default -30.0 + \note Enter the minimum outdoor temperature allowed for heating operation + \note Heating is disabled below this temperature + N7, \field Maximum Outdoor Air Temperature in Heating Mode + \type real + \units C + \default 100 + \note Enter the maximum outdoor temperature allowed for heating operation + \note Heating is disabled above this temperature + A8, \field Minimum Leaving Water Temperature Curve Name in Heating Mode + \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 + A9, \field Maximum Leaving Water Temperature Curve Name in Heating Mode + \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 + N8, \field Minimum Part Load Ratio + \note Below this operating limit compressor cycling will occur + \type real + \minimum 0.0 + \default 0.0 + N9, \field Sizing Factor for Heating + \note Multiplies the autosized capacity and flow rates + \type real + \minimum> 0.0 + \default 1.0 + N10, \field Rated Inlet Air Temperature in Cooling Mode + \type real + \units C + \default 30 + \note inlet air dry-bulb temperature corresponding to rated heat pump performance + \note (capacity, COP). + N11, \field Rated Air Flow Rate in Cooling Mode + \type real + \units m3/s + \minimum> 0.0 + \autosizable + \default autosize + \note air flow rate corresponding to rated heat pump performance + \note (capacity, COP). + N12 , \field Rated Leaving Water Temperature in Cooling Mode + \type real + \units C + \default 8 + \note inlet water temperature corresponding to rated performance + \note (heating capacity, COP). + N13 , \field Rated Water Flow Rate in Cooling Mode + \type real + \units m3/s + \ip-units gal/min + \minimum> 0.0 + \autosizable + \default autosize + \note Condenser water flow rate corresponding to rated heat pump performance + \note (capacity, COP). + N14 , \field Minimum Outdoor Air Temperature in Cooling Mode + \type real + \units C + \default -30.0 + \note Enter the minimum outdoor temperature allowed for cooling operation + \note Cooling is disabled below this temperature + N15, \field Maximum Outdoor Air Temperature in Cooling Mode + \type real + \units C + \default 100.0 + \note Enter the maximum outdoor temperature allowed for cooling operation + \note Cooling is disabled above this temperature + A10, \field Minimum Leaving Water Temperature Curve Name in Cooling Mode + \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 + A11, \field Maximum Leaving Water Temperature Curve Name in Cooling Mode + \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 + N16, \field Sizing Factor for Cooling + \note Multiplies the autosized capacity and flow rates + \type real + \minimum> 0.0 + \default 1.0 + A12, \field Air Inlet Node Name + \required-field + \type node + \note The node from which the heat pump draws its inlet air. + A13, \field Air Outlet Node Name + \required-field + \type node + \note The node to which the heat pump sends its outlet air. + A14, \field Hot Water Inlet Node Name + \type node + \note The node connects to the hot water loop + A15, \field Hot Water Outlet Node Name + \type node + \note The node connects to the hot water loop + A16, \field Chilled Water Inlet Node Name + \type node + \note The node connects to the chilled water loop + A17, \field Chilled Water Outlet Node Name + \type node + \note The node connects to the chilled water loop + N17, \field Maximum Outdoor Dry Bulb Temperature For Defrost Operation + \type real + \default 10.0 + \note defrost operation will not be active above this outdoor temperature + A18, \field Heat Pump Defrost Control + \type choice + \key None + \key Timed + \key OnDemand + \key TimedEmpirical + \note A blank field is the same as None. + N18, \field Heat Pump Defrost Time Period Fraction + \type real + \minimum 0.0 + \default 0.058333 + \note Fraction of time in defrost mode + \note only applicable if timed defrost control is specified + N19, \field Resistive Defrost Heater Capacity + \type real + \minimum 0.0 + \default 0.0 + \autosizable + \units W + \note only applicable if resistive defrost strategy is specified + \ip-units W + A19, \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 + N20, \field Heat Pump Multiplier + \type integer + \default 1 + \note intend to model modular heat pumps + A20, \field Control Type + \type choice + \key FixedSpeed + \key VariableSpeed + \default VariableSpeed + N21, \field Crankcase Heater Capacity + \type real + \minimum 0.0 + \default 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. + A21, \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 + N22, \field Maximum Ambient Temperature for Crankcase Heater Operation + \type real + \minimum 0.0 + \default 10.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. + N25, \field Number of Speeds for Heating + \type integer + \note the number of speed levels in heating mode + \maximum 5 + \default 1 + \note if there's only cooling component, set this field to 0 + N26, \field Rated Heating Capacity at Speed 1 + \type real + \units W + \minimum> 0.0 + \autosizable + \default autosize + N27, \field Rated COP for Heating at Speed 1 + \type real + \minimum> 0.0 + \units W/W + \default 3.0 + A23, \field Normalized Heating Capacity Function of Temperature Curve Name at Speed 1 + \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. + A24, \field Heating Energy Input Ratio Function of Temperature Curve Name at Speed 1 + \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. + A25, \field Heating Energy Input Ratio Function of PLR Curve Name at Speed 1 + \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. + N28, \field Rated Heating Capacity at Speed 2 + \type real + \units W + \minimum> 0.0 + N29, \field Rated COP for Heating at Speed 2 + \type real + \minimum> 0.0 + \units W/W + A26, \field Normalized Heating Capacity Function of Temperature Curve Name at Speed 2 + \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. + A27, \field Heating Energy Input Ratio Function of Temperature Curve Name at Speed 2 + \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. + A28, \field Heating Energy Input Ratio Function of PLR Curve Name at Speed 2 + \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. + N30, \field Rated Heating Capacity at Speed 3 + \type real + \units W + \minimum> 0.0 + N31, \field Rated COP for Heating at Speed 3 + \type real + \minimum> 0.0 + \units W/W + A29, \field Normalized Heating Capacity Function of Temperature Curve Name at Speed 3 + \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. + A30, \field Heating Energy Input Ratio Function of Temperature Curve Name at Speed 3 + \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. + A31, \field Heating Energy Input Ratio Function of PLR Curve Name at Speed 3 + \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. + N32, \field Rated Heating Capacity at Speed 4 + \type real + \units W + \minimum> 0.0 + N33,\field Rated COP for Heating at Speed 4 + \type real + \minimum> 0.0 + \units W/W + A32, \field Normalized Heating Capacity Function of Temperature Curve Name at Speed 4 + \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. + A33, \field Heating Energy Input Ratio Function of Temperature Curve Name at Speed 4 + \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. + A34, \field Heating Energy Input Ratio Function of PLR Curve Name at Speed 4 + \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. + N34, \field Rated Heating Capacity at Speed 5 + \type real + \units W + \minimum> 0.0 + N35, \field Rated COP for Heating at Speed 5 + \type real + \minimum> 0.0 + \units W/W + A35, \field Normalized Heating Capacity Function of Temperature Curve Name at Speed 5 + \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. + A36, \field Heating Energy Input Ratio Function of Temperature Curve Name at Speed 5 + \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. + A37, \field Heating Energy Input Ratio Function of PLR Curve Name at Speed 5 + \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. + A38, \field Booster Mode On Heating + \type choice + \key Yes + \key No + \default No + N36, \field Rated Heating Capacity in Booster Mode + \type real + \units W + \minimum> 0.0 + N37, \field Rated Heating COP in Booster Mode + \type real + \minimum> 0.0 + \units W/W + \default 3.0 + A39, \field Normalized Heating Capacity Function of Temperature Curve Name in Booster Mode + \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. + A40, \field Heating Energy Input Ratio Function of Temperature Curve Name in Booster Mode + \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. + A41, \field Heating Energy Input Ratio Function of PLR Curve Name in Booster Mode + \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. + N38, \field Number of Speeds for Cooling + \type integer + \maximum 5 + \default 1 + \note the number of speed levels in cooling mode + \note if there's only heating component, set this field to 0 + N39, \field Rated Cooling Capacity at Speed 1 + \type real + \units W + \minimum> 0.0 + \autosizable + \default autosize + N40, \field Rated COP for Cooling at Speed 1 + \type real + \minimum> 0.0 + \units W/W + \default 3.0 + A42, \field Normalized Cooling Capacity Function of Temperature Curve Name at Speed 1 + \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. + A43, \field Cooling Energy Input Ratio Function of Temperature Curve Name at Speed 1 + \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. + A44, \field Cooling Energy Input Ratio Function of PLR Curve Name at Speed 1 + \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. + N41, \field Rated Cooling Capacity at Speed 2 + \type real + \units W + \minimum> 0.0 + N42, \field Rated COP for Cooling at Speed 2 + \type real + \minimum> 0.0 + \units W/W + A45, \field Normalized Cooling Capacity Function of Temperature Curve Name at Speed 2 + \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. + A46, \field Cooling Energy Input Ratio Function of Temperature Curve Name at Speed 2 + \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. + A47, \field Cooling Energy Input Ratio Function of PLR Curve Name at Speed 2 + \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. + N43, \field Rated Cooling Capacity at Speed 3 + \type real + \units W + \minimum> 0.0 + N44, \field Rated COP for Cooling at Speed 3 + \type real + \minimum> 0.0 + \units W/W + A48, \field Normalized Cooling Capacity Function of Temperature Curve Name at Speed 3 + \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. + A49, \field Cooling Energy Input Ratio Function of Temperature Curve Name at Speed 3 + \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. + A50, \field Cooling Energy Input Ratio Function of PLR Curve Name at Speed 3 + \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. + N45, \field Rated Cooling Capacity at Speed 4 + \type real + \units W + \minimum> 0.0 + N46, \field Rated COP for Cooling at Speed 4 + \type real + \minimum> 0.0 + \units W/W + A51, \field Normalized Cooling Capacity Function of Temperature Curve Name at Speed 4 + \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. + A52, \field Cooling Energy Input Ratio Function of Temperature Curve Name at Speed 4 + \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. + A53, \field Cooling Energy Input Ratio Function of PLR Curve Name at Speed 4 + \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. + N47, \field Rated Cooling Capacity at Speed 5 + \type real + \units W + \minimum> 0.0 + N48, \field Rated COP for Cooling at Speed 5 + \type real + \minimum> 0.0 + \units W/W + A54, \field Normalized Cooling Capacity Function of Temperature Curve Name at Speed 5 + \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. + A55, \field Cooling Energy Input Ratio Function of Temperature Curve Name at Speed 5 + \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. + A56, \field Cooling Energy Input Ratio Function of PLR Curve Name at Speed 5 + \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. + A57, \field Booster Mode On Cooling + \type choice + \key Yes + \key No + \default No + N49, \field Rated Cooling Capacity in Booster Mode + \type real + \units W + \minimum> 0.0 + N50, \field Rated Cooling COP in Booster Mode + \type real + \minimum> 0.0 + \units W/W + \default 3.0 + A58, \field Normalized Cooling Capacity Function of Temperature Curve Name in Booster Mode + \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. + A59, \field Cooling Energy Input Ratio Function of Temperature Curve Name in Booster Mode + \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. + A60; \field Cooling Energy Input Ratio Function of PLR Curve Name in Booster Mode + \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 From 10bb8c446c518302845d1d1f1994e8f50d6d5457 Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Mon, 6 Oct 2025 15:28:07 +0200 Subject: [PATCH 02/45] Make required-field + break speed data into subobjects I'm considering breaking it further into subobjects, but I need to start somewhere while I wait on stakeholder feedback --- resources/model/OpenStudio.idd | 409 +++++++++------------------------ 1 file changed, 103 insertions(+), 306 deletions(-) diff --git a/resources/model/OpenStudio.idd b/resources/model/OpenStudio.idd index 2548172ad2..3cd38e1295 100644 --- a/resources/model/OpenStudio.idd +++ b/resources/model/OpenStudio.idd @@ -14880,33 +14880,36 @@ OS:HeatPump:AirToWater:FuelFired:Cooling, 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 57 + \min-fields 47 A1 , \field Handle - \type 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 Availability Schedule Name Heating + \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 A4 , \field Availability Schedule Name Cooling + \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 A5 , \field Operating Mode Control Method + \required-field \type choice \key ScheduledModes \key EMSControlled \key Load - \default Load A6, \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 @@ -14914,7 +14917,6 @@ OS:HeatPump:AirToWater, \key CoolingPriority \key HeatingPriority \key Balanced - \default SingleMode \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. @@ -14931,44 +14933,44 @@ OS:HeatPump:AirToWater, \type object-list \object-list ScheduleNames N2 , \field Rated Inlet Air Temperature in Heating Mode + \required-field \type real \units C - \default 8 \note inlet air dry-bulb temperature corresponding to rated heat pump performance \note (capacity, COP). N3 , \field Rated Air Flow Rate in Heating Mode + \required-field \type real \units m3/s \minimum> 0.0 \autosizable - \default autosize \note air flow rate corresponding to rated heat pump performance \note (capacity, COP). N4 , \field Rated Leaving Water Temperature in Heating Mode + \required-field \type real \units C - \default 40 \note outlet water temperature corresponding to rated heat pump performance \note (heating capacity, COP). N5 , \field Rated Water Flow Rate in Heating Mode + \required-field \type real \units m3/s \ip-units gal/min \minimum> 0.0 \autosizable - \default autosize \note Condenser water flow rate corresponding to rated heat pump performance \note (capacity, COP). N6, \field Minimum Outdoor Air Temperature in Heating Mode + \required-field \type real \units C - \default -30.0 \note Enter the minimum outdoor temperature allowed for heating operation \note Heating is disabled below this temperature N7, \field Maximum Outdoor Air Temperature in Heating Mode + \required-field \type real \units C - \default 100 \note Enter the maximum outdoor temperature allowed for heating operation \note Heating is disabled above this temperature A8, \field Minimum Leaving Water Temperature Curve Name in Heating Mode @@ -14982,57 +14984,57 @@ OS:HeatPump:AirToWater, \note quadratic curve = a + b*OAT is typical, other univariate curves may be used \note OAT = Outdoor Dry-Bulb Temperature N8, \field Minimum Part Load Ratio + \required-field \note Below this operating limit compressor cycling will occur \type real \minimum 0.0 - \default 0.0 - N9, \field Sizing Factor for Heating + N9, \field Sizing Factor for Heating + \required-field \note Multiplies the autosized capacity and flow rates \type real \minimum> 0.0 - \default 1.0 N10, \field Rated Inlet Air Temperature in Cooling Mode + \required-field \type real \units C - \default 30 \note inlet air dry-bulb temperature corresponding to rated heat pump performance \note (capacity, COP). N11, \field Rated Air Flow Rate in Cooling Mode + \required-field \type real \units m3/s \minimum> 0.0 \autosizable - \default autosize \note air flow rate corresponding to rated heat pump performance \note (capacity, COP). - N12 , \field Rated Leaving Water Temperature in Cooling Mode + N12, \field Rated Leaving Water Temperature in Cooling Mode + \required-field \type real \units C - \default 8 \note inlet water temperature corresponding to rated performance \note (heating capacity, COP). - N13 , \field Rated Water Flow Rate in Cooling Mode + N13, \field Rated Water Flow Rate in Cooling Mode + \required-field \type real \units m3/s \ip-units gal/min \minimum> 0.0 \autosizable - \default autosize \note Condenser water flow rate corresponding to rated heat pump performance \note (capacity, COP). - N14 , \field Minimum Outdoor Air Temperature in Cooling Mode + N14, \field Minimum Outdoor Air Temperature in Cooling Mode + \required-field \type real \units C - \default -30.0 \note Enter the minimum outdoor temperature allowed for cooling operation \note Cooling is disabled below this temperature N15, \field Maximum Outdoor Air Temperature in Cooling Mode + \required-field \type real \units C - \default 100.0 \note Enter the maximum outdoor temperature allowed for cooling operation \note Cooling is disabled above this temperature - A10, \field Minimum Leaving Water Temperature Curve Name in Cooling Mode + A10, \field Minimum Leaving Water Temperature Curve Name in Cooling Mode \type object-list \object-list UniVariateFunctions \note quadratic curve = a + b*OAT is typical, other univariate curves may be used @@ -15043,51 +15045,52 @@ OS:HeatPump:AirToWater, \note quadratic curve = a + b*OAT is typical, other univariate curves may be used \note OAT = Outdoor Dry-Bulb Temperature N16, \field Sizing Factor for Cooling + \required-field \note Multiplies the autosized capacity and flow rates \type real \minimum> 0.0 - \default 1.0 A12, \field Air Inlet Node Name - \required-field - \type node + \type alpha \note The node from which the heat pump draws its inlet air. A13, \field Air Outlet Node Name - \required-field - \type node + \type alpha \note The node to which the heat pump sends its outlet air. A14, \field Hot Water Inlet Node Name - \type node + \type object-list + \object-list ConnectionNames \note The node connects to the hot water loop A15, \field Hot Water Outlet Node Name - \type node + \type object-list + \object-list ConnectionNames \note The node connects to the hot water loop A16, \field Chilled Water Inlet Node Name - \type node + \type object-list + \object-list ConnectionNames \note The node connects to the chilled water loop A17, \field Chilled Water Outlet Node Name - \type node + \type object-list + \object-list ConnectionNames \note The node connects to the chilled water loop N17, \field Maximum Outdoor Dry Bulb Temperature For Defrost Operation - \type real - \default 10.0 + \required-field \note defrost operation will not be active above this outdoor temperature A18, \field Heat Pump Defrost Control + \required-field \type choice \key None \key Timed \key OnDemand \key TimedEmpirical - \note A blank field is the same as None. N18, \field Heat Pump Defrost Time Period Fraction + \required-field \type real \minimum 0.0 - \default 0.058333 \note Fraction of time in defrost mode \note only applicable if timed defrost control is specified N19, \field Resistive Defrost Heater Capacity + \required-field \type real \minimum 0.0 - \default 0.0 \autosizable \units W \note only applicable if resistive defrost strategy is specified @@ -15101,18 +15104,18 @@ OS:HeatPump:AirToWater, \note WB = wet-bulb temperature (C) of air entering the indoor coil \note only required if Timed or OnDemand defrost strategy is specified N20, \field Heat Pump Multiplier + \required-field \type integer - \default 1 \note intend to model modular heat pumps A20, \field Control Type + \required-field \type choice \key FixedSpeed \key VariableSpeed - \default VariableSpeed N21, \field Crankcase Heater Capacity + \required-field \type real \minimum 0.0 - \default 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 @@ -15124,315 +15127,109 @@ OS:HeatPump:AirToWater, \type object-list \object-list UnivariateFunctions N22, \field Maximum Ambient Temperature for Crankcase Heater Operation + \required-field \type real \minimum 0.0 - \default 10.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. - N25, \field Number of Speeds for Heating - \type integer - \note the number of speed levels in heating mode - \maximum 5 - \default 1 - \note if there's only cooling component, set this field to 0 - N26, \field Rated Heating Capacity at Speed 1 - \type real - \units W - \minimum> 0.0 - \autosizable - \default autosize - N27, \field Rated COP for Heating at Speed 1 - \type real - \minimum> 0.0 - \units W/W - \default 3.0 - A23, \field Normalized Heating Capacity Function of Temperature Curve Name at Speed 1 - \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. - A24, \field Heating Energy Input Ratio Function of Temperature Curve Name at Speed 1 - \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. - A25, \field Heating Energy Input Ratio Function of PLR Curve Name at Speed 1 - \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. - N28, \field Rated Heating Capacity at Speed 2 - \type real - \units W - \minimum> 0.0 - N29, \field Rated COP for Heating at Speed 2 - \type real - \minimum> 0.0 - \units W/W - A26, \field Normalized Heating Capacity Function of Temperature Curve Name at Speed 2 - \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. - A27, \field Heating Energy Input Ratio Function of Temperature Curve Name at Speed 2 - \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. - A28, \field Heating Energy Input Ratio Function of PLR Curve Name at Speed 2 - \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. - N30, \field Rated Heating Capacity at Speed 3 - \type real - \units W - \minimum> 0.0 - N31, \field Rated COP for Heating at Speed 3 - \type real - \minimum> 0.0 - \units W/W - A29, \field Normalized Heating Capacity Function of Temperature Curve Name at Speed 3 - \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. - A30, \field Heating Energy Input Ratio Function of Temperature Curve Name at Speed 3 - \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. - A31, \field Heating Energy Input Ratio Function of PLR Curve Name at Speed 3 - \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. - N32, \field Rated Heating Capacity at Speed 4 - \type real - \units W - \minimum> 0.0 - N33,\field Rated COP for Heating at Speed 4 - \type real - \minimum> 0.0 - \units W/W - A32, \field Normalized Heating Capacity Function of Temperature Curve Name at Speed 4 - \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. - A33, \field Heating Energy Input Ratio Function of Temperature Curve Name at Speed 4 - \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. - A34, \field Heating Energy Input Ratio Function of PLR Curve Name at Speed 4 - \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. - N34, \field Rated Heating Capacity at Speed 5 - \type real - \units W - \minimum> 0.0 - N35, \field Rated COP for Heating at Speed 5 - \type real - \minimum> 0.0 - \units W/W - A35, \field Normalized Heating Capacity Function of Temperature Curve Name at Speed 5 - \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. - A36, \field Heating Energy Input Ratio Function of Temperature Curve Name at Speed 5 - \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. - A37, \field Heating Energy Input Ratio Function of PLR Curve Name at Speed 5 + A22, \field Heating Speed Data List + \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. - A38, \field Booster Mode On Heating - \type choice - \key Yes - \key No - \default No - N36, \field Rated Heating Capacity in Booster Mode - \type real - \units W - \minimum> 0.0 - N37, \field Rated Heating COP in Booster Mode - \type real - \minimum> 0.0 - \units W/W - \default 3.0 - A39, \field Normalized Heating Capacity Function of Temperature Curve Name in Booster Mode + \object-list ModelObjectLists + \note The minimum number of speeds is 0 (no heating) and the maximum is 5. + A23, \field Booster Mode On Heating Speed Data + \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 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. - A40, \field Heating Energy Input Ratio Function of Temperature Curve Name in Booster Mode + \object-list HeatPumpAirToWaterHeatingSpeedData + A24, \field Cooling Speed Data List + \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. - A41, \field Heating Energy Input Ratio Function of PLR Curve Name in Booster Mode + \object-list ModelObjectLists + \note The minimum number of speeds is 0 (no cooling) and the maximum is 5. + A25; \field Booster Mode On Cooling Speed Data + \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 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. - N38, \field Number of Speeds for Cooling - \type integer - \maximum 5 - \default 1 - \note the number of speed levels in cooling mode - \note if there's only heating component, set this field to 0 - N39, \field Rated Cooling Capacity at Speed 1 + \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 - \default autosize - N40, \field Rated COP for Cooling at Speed 1 - \type real - \minimum> 0.0 - \units W/W - \default 3.0 - A42, \field Normalized Cooling Capacity Function of Temperature Curve Name at Speed 1 - \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. - A43, \field Cooling Energy Input Ratio Function of Temperature Curve Name at Speed 1 - \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. - A44, \field Cooling Energy Input Ratio Function of PLR Curve Name at Speed 1 - \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. - N41, \field Rated Cooling Capacity at Speed 2 - \type real - \units W - \minimum> 0.0 - N42, \field Rated COP for Cooling at Speed 2 - \type real - \minimum> 0.0 - \units W/W - A45, \field Normalized Cooling Capacity Function of Temperature Curve Name at Speed 2 - \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. - A46, \field Cooling Energy Input Ratio Function of Temperature Curve Name at Speed 2 - \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. - A47, \field Cooling Energy Input Ratio Function of PLR Curve Name at Speed 2 - \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. - N43, \field Rated Cooling Capacity at Speed 3 - \type real - \units W - \minimum> 0.0 - N44, \field Rated COP for Cooling at Speed 3 - \type real - \minimum> 0.0 - \units W/W - A48, \field Normalized Cooling Capacity Function of Temperature Curve Name at Speed 3 - \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. - A49, \field Cooling Energy Input Ratio Function of Temperature Curve Name at Speed 3 - \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. - A50, \field Cooling Energy Input Ratio Function of PLR Curve Name at Speed 3 - \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. - N45, \field Rated Cooling Capacity at Speed 4 - \type real - \units W - \minimum> 0.0 - N46, \field Rated COP for Cooling at Speed 4 - \type real - \minimum> 0.0 - \units W/W - A51, \field Normalized Cooling Capacity Function of Temperature Curve Name at Speed 4 - \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. - A52, \field Cooling Energy Input Ratio Function of Temperature Curve Name at Speed 4 - \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. - A53, \field Cooling Energy Input Ratio Function of PLR Curve Name at Speed 4 - \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. - N47, \field Rated Cooling Capacity at Speed 5 - \type real - \units W - \minimum> 0.0 - N48, \field Rated COP for Cooling at Speed 5 + N2, \field Rated COP for Heating + \required-field \type real \minimum> 0.0 \units W/W - A54, \field Normalized Cooling Capacity Function of Temperature Curve Name at Speed 5 + 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. - A55, \field Cooling Energy Input Ratio Function of Temperature Curve Name at Speed 5 + 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. - A56, \field Cooling Energy Input Ratio Function of PLR Curve Name at Speed 5 + 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. - A57, \field Booster Mode On Cooling - \type choice - \key Yes - \key No - \default No - N49, \field Rated Cooling Capacity in Booster Mode + +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 - N50, \field Rated Cooling COP in Booster Mode + \autosizable + N2, \field Rated COP for Cooling + \required-field \type real \minimum> 0.0 \units W/W - \default 3.0 - A58, \field Normalized Cooling Capacity Function of Temperature Curve Name in Booster Mode + 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. - A59, \field Cooling Energy Input Ratio Function of Temperature Curve Name in Booster Mode + 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. - A60; \field Cooling Energy Input Ratio Function of PLR Curve Name in Booster Mode + 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, From 68e51d1aa9848329d314c65e8c06f3238b0d167b Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Mon, 6 Oct 2025 17:34:49 +0200 Subject: [PATCH 03/45] WIP wrapping HeatPumpAirToWaterHeatingSpeedData --- src/model/CMakeLists.txt | 4 + src/model/ConcreteModelObjects.hpp | 2 + .../HeatPumpAirToWaterHeatingSpeedData.cpp | 334 ++++++++++++++++++ .../HeatPumpAirToWaterHeatingSpeedData.hpp | 115 ++++++ ...eatPumpAirToWaterHeatingSpeedData_Impl.hpp | 107 ++++++ ...atPumpAirToWaterHeatingSpeedData_GTest.cpp | 151 ++++++++ 6 files changed, 713 insertions(+) create mode 100644 src/model/HeatPumpAirToWaterHeatingSpeedData.cpp create mode 100644 src/model/HeatPumpAirToWaterHeatingSpeedData.hpp create mode 100644 src/model/HeatPumpAirToWaterHeatingSpeedData_Impl.hpp create mode 100644 src/model/test/HeatPumpAirToWaterHeatingSpeedData_GTest.cpp diff --git a/src/model/CMakeLists.txt b/src/model/CMakeLists.txt index aa91ad5add..2172a51ec0 100644 --- a/src/model/CMakeLists.txt +++ b/src/model/CMakeLists.txt @@ -920,6 +920,9 @@ set(${target_name}_src HeatPumpAirToWaterFuelFiredCooling.hpp HeatPumpAirToWaterFuelFiredCooling_Impl.hpp HeatPumpAirToWaterFuelFiredCooling.cpp + HeatPumpAirToWaterHeatingSpeedData.hpp + HeatPumpAirToWaterHeatingSpeedData_Impl.hpp + HeatPumpAirToWaterHeatingSpeedData.cpp HeatPumpWaterToWaterEquationFitCooling.hpp HeatPumpWaterToWaterEquationFitCooling_Impl.hpp HeatPumpWaterToWaterEquationFitCooling.cpp @@ -2163,6 +2166,7 @@ set(${target_name}_test_src test/HeatExchangerFluidToFluid_GTest.cpp test/HeatPumpAirToWaterFuelFiredHeating_GTest.cpp test/HeatPumpAirToWaterFuelFiredCooling_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..a35eeacb84 100644 --- a/src/model/ConcreteModelObjects.hpp +++ b/src/model/ConcreteModelObjects.hpp @@ -290,6 +290,7 @@ #include "HeatExchangerFluidToFluid.hpp" #include "HeatPumpAirToWaterFuelFiredHeating.hpp" #include "HeatPumpAirToWaterFuelFiredCooling.hpp" +#include "HeatPumpAirToWaterHeatingSpeedData.hpp" #include "HeatPumpWaterToWaterEquationFitCooling.hpp" #include "HeatPumpWaterToWaterEquationFitHeating.hpp" #include "HeatPumpPlantLoopEIRCooling.hpp" @@ -849,6 +850,7 @@ #include "HeatExchangerFluidToFluid_Impl.hpp" #include "HeatPumpAirToWaterFuelFiredHeating_Impl.hpp" #include "HeatPumpAirToWaterFuelFiredCooling_Impl.hpp" +#include "HeatPumpAirToWaterHeatingSpeedData_Impl.hpp" #include "HeatPumpWaterToWaterEquationFitCooling_Impl.hpp" #include "HeatPumpWaterToWaterEquationFitHeating_Impl.hpp" #include "HeatPumpPlantLoopEIRCooling_Impl.hpp" diff --git a/src/model/HeatPumpAirToWaterHeatingSpeedData.cpp b/src/model/HeatPumpAirToWaterHeatingSpeedData.cpp new file mode 100644 index 0000000000..cd71a7db39 --- /dev/null +++ b/src/model/HeatPumpAirToWaterHeatingSpeedData.cpp @@ -0,0 +1,334 @@ +/*********************************************************************************************************************** +* OpenStudio(R), Copyright (c) Alliance for Sustainable Energy, LLC. +* See also https://openstudio.net/license +***********************************************************************************************************************/ + +#include "HeatPumpAirToWaterHeatingSpeedData.hpp" +#include "HeatPumpAirToWaterHeatingSpeedData_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) + : ParentObject_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) + : ParentObject_Impl(other, model, keepHandle) { + OS_ASSERT(other.iddObject().type() == HeatPumpAirToWaterHeatingSpeedData::iddObjectType()); + } + + HeatPumpAirToWaterHeatingSpeedData_Impl::HeatPumpAirToWaterHeatingSpeedData_Impl(const HeatPumpAirToWaterHeatingSpeedData_Impl& other, + Model_Impl* model, bool keepHandle) + : ParentObject_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(); + } + + 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() { + return getAutosizedValue("TODO_CHECK_SQL Rated Heating Capacity", "W"); + } + + 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); + } + + } // namespace detail + + HeatPumpAirToWaterHeatingSpeedData::HeatPumpAirToWaterHeatingSpeedData(const Model& model) + : ParentObject(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) + : ParentObject(HeatPumpAirToWaterHeatingSpeedData::iddObjectType(), model) { + + OS_ASSERT(getImpl()); + + autosizeRatedHeatingCapacity(); + setRatedCOPforHeating(3.0); + + bool ok = setNormalizedHeatingCapacityFunctionofTemperatureCurve(normalizedHeatingCapacityFunctionofTemperatureCurve); + if (!ok) { + remove(); + LOG_AND_THROW("Unable to set " << briefDescription() << "'s Capacity Curve to " + << normalizedHeatingCapacityFunctionofTemperatureCurve.briefDescription() << "."); + } + ok = setHeatingEnergyInputRatioFunctionofTemperatureCurve(heatingEnergyInputRatioFunctionofTemperatureCurve); + if (!ok) { + remove(); + LOG_AND_THROW("Unable to set " << briefDescription() << "'s EIRfT Curve to " + << heatingEnergyInputRatioFunctionofTemperatureCurve.briefDescription() << "."); + } + ok = setHeatingEnergyInputRatioFunctionofPLRCurve(heatingEnergyInputRatioFunctionofPLRCurve); + if (!ok) { + 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) + : ParentObject(std::move(impl)) {} + /// @endcond + + void HeatPumpAirToWaterHeatingSpeedData::autosize() { + getImpl()->autosizeRatedHeatingCapacity(); + } + + // TODO: needed? + // void HeatPumpAirToWaterHeatingSpeedData::applySizingValues() { + // getImpl()->applySizingValues(); + // } + +} // namespace model +} // namespace openstudio diff --git a/src/model/HeatPumpAirToWaterHeatingSpeedData.hpp b/src/model/HeatPumpAirToWaterHeatingSpeedData.hpp new file mode 100644 index 0000000000..c4a7744e88 --- /dev/null +++ b/src/model/HeatPumpAirToWaterHeatingSpeedData.hpp @@ -0,0 +1,115 @@ +/*********************************************************************************************************************** +* 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 "ParentObject.hpp" + +namespace openstudio { + +namespace model { + + class Curve; + + namespace detail { + + class HeatPumpAirToWaterHeatingSpeedData_Impl; + + } // namespace detail + + /** HeatPumpAirToWaterHeatingSpeedData is a ParentObject that wraps the OpenStudio IDD object 'OS:HeatPump:AirToWater:Heating:SpeedData'. */ + class MODEL_API HeatPumpAirToWaterHeatingSpeedData : public ParentObject + { + 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 */ + //@{ + + // returns a vector of HeatPumpAirToWater that use this as their Speed Data + // TODO: std::vector heatPumpsAirToWater() 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..345d9a5263 --- /dev/null +++ b/src/model/HeatPumpAirToWaterHeatingSpeedData_Impl.hpp @@ -0,0 +1,107 @@ +/*********************************************************************************************************************** +* 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 "ParentObject_Impl.hpp" + +namespace openstudio { +namespace model { + + class Curve; + + namespace detail { + + /** HeatPumpAirToWaterHeatingSpeedData_Impl is a ParentObject_Impl that is the implementation class for HeatPumpAirToWaterHeatingSpeedData.*/ + class MODEL_API HeatPumpAirToWaterHeatingSpeedData_Impl : public ParentObject_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; + + // TODO: clone, children, remove? + + //@} + /** @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 */ + //@{ + + // returns a vector of HeatPumpAirToWater that use this as their Speed Data + // TODO: std::vector heatPumpsAirToWater() 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/test/HeatPumpAirToWaterHeatingSpeedData_GTest.cpp b/src/model/test/HeatPumpAirToWaterHeatingSpeedData_GTest.cpp new file mode 100644 index 0000000000..10bfda2c3d --- /dev/null +++ b/src/model/test/HeatPumpAirToWaterHeatingSpeedData_GTest.cpp @@ -0,0 +1,151 @@ +/*********************************************************************************************************************** +* 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 "../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; + CurveBiquadratic bivariate(m); + CurveQuadratic univariate(m); + + // 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()); + awhpHCSpeed.remove(); + 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), Exception); + EXPECT_THROW(HeatPumpAirToWaterHeatingSpeedData(m, univariate, bivariate, univariate), Exception); + EXPECT_THROW(HeatPumpAirToWaterHeatingSpeedData(m, bivariate, bivariate, bivariate), Exception); + EXPECT_EQ(0, m.getConcreteModelObjects().size()); + 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()); + + 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) { + GTEST_SKIP() << "Need to implement ModelObjectList support in HeatPumpAirToWater and check that removing a speed data is either prevented or " + "cleans up the ModelObjectList properly."; +} From 029bcc3db6fbf61780b79c516ceda180baed10b0 Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Mon, 6 Oct 2025 17:35:42 +0200 Subject: [PATCH 04/45] GenerateClass: allow generating stubs even if didn't build openstudio yet --- .../ModelClassGenerator.rb | 59 +++++++++++-------- .../SubProjectClassGenerator.rb | 8 +++ .../TranslatorGenerator.rb | 23 +++++++- 3 files changed, 63 insertions(+), 27 deletions(-) diff --git a/developer/ruby/SubProjectClassGenerators/ModelClassGenerator.rb b/developer/ruby/SubProjectClassGenerators/ModelClassGenerator.rb index 016f785e0a..35df688f64 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 @@ -406,7 +407,6 @@ class ModelClassGenerator < SubProjectClassGenerator def initialize(className, baseClassName, pImpl, qobject, iddObjectType) super(className, baseClassName, pImpl, qobject) - @iddObjectType = iddObjectType @hasRealFields = false @hasScheduleFields = false @@ -421,14 +421,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 +474,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 @@ -528,7 +539,7 @@ 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| result << preamble @@ -542,7 +553,7 @@ def hppSubProjectForwardDeclarations def implHppSubProjectForwardDeclarations result = String.new - if @idfObject + if @iddObject result = hppSubProjectForwardDeclarations end return result @@ -551,7 +562,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 +576,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 +592,7 @@ def hppConstructors() def implHppConstructors() result = String.new - if @idfObject + if @iddObject implConstructorStart = String.new implConstructorStart << " " << @className << "_Impl(" @@ -608,7 +619,7 @@ def implHppConstructors() def cppConstructors() result = String.new - if @idfObject + if @iddObject implConstructorStart = String.new @@ -641,7 +652,7 @@ def cppConstructors() def cppPublicClassConstructors() result = String.new - if @idfObject + if @iddObject if (not iddObject.properties.unique) @@ -686,7 +697,7 @@ def cppPublicClassConstructors() def hppPublicMethods() result = String.new - if @idfObject + if @iddObject result << " static IddObjectType iddObjectType();\n\n" @@ -798,7 +809,7 @@ def hppPublicMethods() def implHppPublicMethods() result = String.new - if @idfObject + if @iddObject result << " /** @name Virtual Methods */\n" @@ -929,7 +940,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" @@ -1212,7 +1223,7 @@ def cppPublicMethods() def cppPublicClassPublicMethods() result = String.new - if @idfObject + if @iddObject result << " IddObjectType " << @className << "::iddObjectType() {\n" result << " return {IddObjectType::" << @iddObjectType.valueName << "};\n" @@ -1306,7 +1317,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 +1329,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 +1345,7 @@ def hppProtectedMethods() def implHppProtectedMethods() result = String.new - if @idfObject + if @iddObject else result = super @@ -1346,7 +1357,7 @@ def implHppProtectedMethods() def cppProtectedMethods() result = String.new - if @idfObject + if @iddObject else result = super @@ -1358,7 +1369,7 @@ def cppProtectedMethods() def cppPublicClassProtectedMethods() result = String.new - if @idfObject + if @iddObject if (iddObject.properties.unique) result << " " << @className << "::" << @className << "(Model& model)\n" @@ -1375,7 +1386,7 @@ def cppPublicClassProtectedMethods() def implHppPrivateMethods() result = String.new - if @idfObject + if @iddObject # Optional getters for required objects any = false @@ -1403,7 +1414,7 @@ def implHppPrivateMethods() def cppPrivateMethods() result = String.new - if @idfObject + if @iddObject # Optional getters for required objects @nonextensibleFields.each { |field| 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 = [] From d22527a39d94e6d8d67243edb8b84cefb2c00c3c Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Mon, 6 Oct 2025 17:48:15 +0200 Subject: [PATCH 05/45] register ctor and swig --- src/model/Model.cpp | 2 ++ src/model/ModelHVAC.i | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/model/Model.cpp b/src/model/Model.cpp index 657761bf56..a77f6aee93 100644 --- a/src/model/Model.cpp +++ b/src/model/Model.cpp @@ -4188,6 +4188,7 @@ namespace model { REGISTER_CONSTRUCTOR(HeatExchangerFluidToFluid); REGISTER_CONSTRUCTOR(HeatPumpAirToWaterFuelFiredHeating); REGISTER_CONSTRUCTOR(HeatPumpAirToWaterFuelFiredCooling); + REGISTER_CONSTRUCTOR(HeatPumpAirToWaterHeatingSpeedData); REGISTER_CONSTRUCTOR(HeatPumpWaterToWaterEquationFitCooling); REGISTER_CONSTRUCTOR(HeatPumpWaterToWaterEquationFitHeating); REGISTER_CONSTRUCTOR(HeatPumpPlantLoopEIRCooling); @@ -4767,6 +4768,7 @@ namespace model { REGISTER_COPYCONSTRUCTORS(HeatExchangerFluidToFluid); REGISTER_COPYCONSTRUCTORS(HeatPumpAirToWaterFuelFiredHeating); REGISTER_COPYCONSTRUCTORS(HeatPumpAirToWaterFuelFiredCooling); + 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..6f7b345619 100644 --- a/src/model/ModelHVAC.i +++ b/src/model/ModelHVAC.i @@ -224,6 +224,7 @@ MODELOBJECT_TEMPLATES(HeatPumpWaterToWaterEquationFitCooling); MODELOBJECT_TEMPLATES(HeatPumpWaterToWaterEquationFitHeating); MODELOBJECT_TEMPLATES(HeatPumpPlantLoopEIRCooling); MODELOBJECT_TEMPLATES(HeatPumpPlantLoopEIRHeating); +MODELOBJECT_TEMPLATES(HeatPumpAirToWaterHeatingSpeedData); MODELOBJECT_TEMPLATES(ThermalStorageChilledWaterStratified); MODELOBJECT_TEMPLATES(ChillerAbsorptionIndirect); MODELOBJECT_TEMPLATES(ChillerAbsorption); @@ -354,6 +355,7 @@ SWIG_MODELOBJECT(HeatPumpWaterToWaterEquationFitCooling,1); SWIG_MODELOBJECT(HeatPumpWaterToWaterEquationFitHeating,1); SWIG_MODELOBJECT(HeatPumpPlantLoopEIRCooling,1); SWIG_MODELOBJECT(HeatPumpPlantLoopEIRHeating,1); +SWIG_MODELOBJECT(HeatPumpAirToWaterHeatingSpeedData, 1); SWIG_MODELOBJECT(ThermalStorageChilledWaterStratified, 1); SWIG_MODELOBJECT(ChillerAbsorptionIndirect, 1); SWIG_MODELOBJECT(ChillerAbsorption, 1); From 59ccbcec28ba5102fe8c70c8a4d7fe17e1bef24e Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Tue, 7 Oct 2025 09:59:54 +0200 Subject: [PATCH 06/45] Tweak for clone/remove etc --- .../HeatPumpAirToWaterHeatingSpeedData.cpp | 20 ++++++ ...eatPumpAirToWaterHeatingSpeedData_Impl.hpp | 6 +- ...atPumpAirToWaterHeatingSpeedData_GTest.cpp | 68 ++++++++++++++----- 3 files changed, 77 insertions(+), 17 deletions(-) diff --git a/src/model/HeatPumpAirToWaterHeatingSpeedData.cpp b/src/model/HeatPumpAirToWaterHeatingSpeedData.cpp index cd71a7db39..348fd1e1d7 100644 --- a/src/model/HeatPumpAirToWaterHeatingSpeedData.cpp +++ b/src/model/HeatPumpAirToWaterHeatingSpeedData.cpp @@ -51,6 +51,26 @@ namespace model { 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); } diff --git a/src/model/HeatPumpAirToWaterHeatingSpeedData_Impl.hpp b/src/model/HeatPumpAirToWaterHeatingSpeedData_Impl.hpp index 345d9a5263..9b1d29eba1 100644 --- a/src/model/HeatPumpAirToWaterHeatingSpeedData_Impl.hpp +++ b/src/model/HeatPumpAirToWaterHeatingSpeedData_Impl.hpp @@ -39,7 +39,11 @@ namespace model { virtual IddObjectType iddObjectType() const override; - // TODO: clone, children, remove? + // 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 ParentObject_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 */ diff --git a/src/model/test/HeatPumpAirToWaterHeatingSpeedData_GTest.cpp b/src/model/test/HeatPumpAirToWaterHeatingSpeedData_GTest.cpp index 10bfda2c3d..090dd4b3fd 100644 --- a/src/model/test/HeatPumpAirToWaterHeatingSpeedData_GTest.cpp +++ b/src/model/test/HeatPumpAirToWaterHeatingSpeedData_GTest.cpp @@ -73,29 +73,46 @@ TEST_F(ModelFixture, HeatPumpAirToWaterHeatingSpeedData_GettersSetters) { } TEST_F(ModelFixture, HeatPumpAirToWaterHeatingSpeedData_CreateInvalid) { - Model m; - CurveBiquadratic bivariate(m); - CurveQuadratic univariate(m); - - // Valid { - HeatPumpAirToWaterHeatingSpeedData awhpHCSpeed(m, bivariate, bivariate, univariate); + 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()); - awhpHCSpeed.remove(); - 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()); } - - // Invalid: wrong curve type - EXPECT_THROW(HeatPumpAirToWaterHeatingSpeedData(m, bivariate, univariate, univariate), Exception); - EXPECT_THROW(HeatPumpAirToWaterHeatingSpeedData(m, univariate, bivariate, univariate), Exception); - EXPECT_THROW(HeatPumpAirToWaterHeatingSpeedData(m, bivariate, bivariate, bivariate), Exception); - EXPECT_EQ(0, m.getConcreteModelObjects().size()); - EXPECT_EQ(1, m.getConcreteModelObjects().size()); - EXPECT_EQ(1, m.getConcreteModelObjects().size()); } TEST_F(ModelFixture, HeatPumpAirToWaterHeatingSpeedData_clone_remove) { @@ -127,6 +144,25 @@ TEST_F(ModelFixture, HeatPumpAirToWaterHeatingSpeedData_clone_remove) { 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()); From 5d3fdbdf601a8f801f2876303959217b7be9f167 Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Tue, 7 Oct 2025 10:00:16 +0200 Subject: [PATCH 07/45] Fixup nasty side effect of removing passed curves in ctor --- src/model/HeatPumpAirToWaterHeatingSpeedData.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/model/HeatPumpAirToWaterHeatingSpeedData.cpp b/src/model/HeatPumpAirToWaterHeatingSpeedData.cpp index 348fd1e1d7..451c15bea1 100644 --- a/src/model/HeatPumpAirToWaterHeatingSpeedData.cpp +++ b/src/model/HeatPumpAirToWaterHeatingSpeedData.cpp @@ -255,26 +255,29 @@ namespace model { const Curve& heatingEnergyInputRatioFunctionofPLRCurve) : ParentObject(HeatPumpAirToWaterHeatingSpeedData::iddObjectType(), model) { - OS_ASSERT(getImpl()); + auto impl = getImpl(); + OS_ASSERT(impl); autosizeRatedHeatingCapacity(); setRatedCOPforHeating(3.0); bool ok = setNormalizedHeatingCapacityFunctionofTemperatureCurve(normalizedHeatingCapacityFunctionofTemperatureCurve); if (!ok) { - remove(); + // We don't call remove() (which would do ParentObject::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) { - remove(); + impl->detail::ModelObject_Impl::remove(); LOG_AND_THROW("Unable to set " << briefDescription() << "'s EIRfT Curve to " << heatingEnergyInputRatioFunctionofTemperatureCurve.briefDescription() << "."); } ok = setHeatingEnergyInputRatioFunctionofPLRCurve(heatingEnergyInputRatioFunctionofPLRCurve); if (!ok) { - remove(); + impl->detail::ModelObject_Impl::remove(); LOG_AND_THROW("Unable to set " << briefDescription() << "'s EIRfPLR Curve to " << heatingEnergyInputRatioFunctionofPLRCurve.briefDescription() << "."); } From 9941697914217712779a19d962975d53dd78549a Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Tue, 7 Oct 2025 10:18:09 +0200 Subject: [PATCH 08/45] Implement HeatPumpAirToWaterCoolingSpeedData the same way --- src/model/CMakeLists.txt | 4 + src/model/ConcreteModelObjects.hpp | 2 + .../HeatPumpAirToWaterCoolingSpeedData.cpp | 357 ++++++++++++++++++ .../HeatPumpAirToWaterCoolingSpeedData.hpp | 115 ++++++ ...eatPumpAirToWaterCoolingSpeedData_Impl.hpp | 111 ++++++ src/model/Model.cpp | 2 + src/model/ModelHVAC.i | 2 + ...atPumpAirToWaterCoolingSpeedData_GTest.cpp | 187 +++++++++ 8 files changed, 780 insertions(+) create mode 100644 src/model/HeatPumpAirToWaterCoolingSpeedData.cpp create mode 100644 src/model/HeatPumpAirToWaterCoolingSpeedData.hpp create mode 100644 src/model/HeatPumpAirToWaterCoolingSpeedData_Impl.hpp create mode 100644 src/model/test/HeatPumpAirToWaterCoolingSpeedData_GTest.cpp diff --git a/src/model/CMakeLists.txt b/src/model/CMakeLists.txt index 2172a51ec0..49921df843 100644 --- a/src/model/CMakeLists.txt +++ b/src/model/CMakeLists.txt @@ -923,6 +923,9 @@ set(${target_name}_src HeatPumpAirToWaterHeatingSpeedData.hpp HeatPumpAirToWaterHeatingSpeedData_Impl.hpp HeatPumpAirToWaterHeatingSpeedData.cpp + HeatPumpAirToWaterCoolingSpeedData.hpp + HeatPumpAirToWaterCoolingSpeedData_Impl.hpp + HeatPumpAirToWaterCoolingSpeedData.cpp HeatPumpWaterToWaterEquationFitCooling.hpp HeatPumpWaterToWaterEquationFitCooling_Impl.hpp HeatPumpWaterToWaterEquationFitCooling.cpp @@ -2167,6 +2170,7 @@ set(${target_name}_test_src test/HeatPumpAirToWaterFuelFiredHeating_GTest.cpp test/HeatPumpAirToWaterFuelFiredCooling_GTest.cpp test/HeatPumpAirToWaterHeatingSpeedData_GTest.cpp + test/HeatPumpAirToWaterCoolingSpeedData_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 a35eeacb84..cdc58fd143 100644 --- a/src/model/ConcreteModelObjects.hpp +++ b/src/model/ConcreteModelObjects.hpp @@ -290,6 +290,7 @@ #include "HeatExchangerFluidToFluid.hpp" #include "HeatPumpAirToWaterFuelFiredHeating.hpp" #include "HeatPumpAirToWaterFuelFiredCooling.hpp" +#include "HeatPumpAirToWaterCoolingSpeedData.hpp" #include "HeatPumpAirToWaterHeatingSpeedData.hpp" #include "HeatPumpWaterToWaterEquationFitCooling.hpp" #include "HeatPumpWaterToWaterEquationFitHeating.hpp" @@ -851,6 +852,7 @@ #include "HeatPumpAirToWaterFuelFiredHeating_Impl.hpp" #include "HeatPumpAirToWaterFuelFiredCooling_Impl.hpp" #include "HeatPumpAirToWaterHeatingSpeedData_Impl.hpp" +#include "HeatPumpAirToWaterCoolingSpeedData_Impl.hpp" #include "HeatPumpWaterToWaterEquationFitCooling_Impl.hpp" #include "HeatPumpWaterToWaterEquationFitHeating_Impl.hpp" #include "HeatPumpPlantLoopEIRCooling_Impl.hpp" diff --git a/src/model/HeatPumpAirToWaterCoolingSpeedData.cpp b/src/model/HeatPumpAirToWaterCoolingSpeedData.cpp new file mode 100644 index 0000000000..1fac8b4c51 --- /dev/null +++ b/src/model/HeatPumpAirToWaterCoolingSpeedData.cpp @@ -0,0 +1,357 @@ +/*********************************************************************************************************************** +* OpenStudio(R), Copyright (c) Alliance for Sustainable Energy, LLC. +* See also https://openstudio.net/license +***********************************************************************************************************************/ + +#include "HeatPumpAirToWaterCoolingSpeedData.hpp" +#include "HeatPumpAirToWaterCoolingSpeedData_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) + : ParentObject_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) + : ParentObject_Impl(other, model, keepHandle) { + OS_ASSERT(other.iddObject().type() == HeatPumpAirToWaterCoolingSpeedData::iddObjectType()); + } + + HeatPumpAirToWaterCoolingSpeedData_Impl::HeatPumpAirToWaterCoolingSpeedData_Impl(const HeatPumpAirToWaterCoolingSpeedData_Impl& other, + Model_Impl* model, bool keepHandle) + : ParentObject_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() { + return getAutosizedValue("TODO_CHECK_SQL Rated Cooling Capacity", "W"); + } + + 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); + } + + } // namespace detail + + HeatPumpAirToWaterCoolingSpeedData::HeatPumpAirToWaterCoolingSpeedData(const Model& model) + : ParentObject(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) + : ParentObject(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 ParentObject::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) + : ParentObject(std::move(impl)) {} + /// @endcond + + void HeatPumpAirToWaterCoolingSpeedData::autosize() { + getImpl()->autosizeRatedCoolingCapacity(); + } + + // TODO: needed? + // void HeatPumpAirToWaterCoolingSpeedData::applySizingValues() { + // getImpl()->applySizingValues(); + // } + +} // namespace model +} // namespace openstudio diff --git a/src/model/HeatPumpAirToWaterCoolingSpeedData.hpp b/src/model/HeatPumpAirToWaterCoolingSpeedData.hpp new file mode 100644 index 0000000000..47a7f812ff --- /dev/null +++ b/src/model/HeatPumpAirToWaterCoolingSpeedData.hpp @@ -0,0 +1,115 @@ +/*********************************************************************************************************************** +* 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 "ParentObject.hpp" + +namespace openstudio { + +namespace model { + + class Curve; + + namespace detail { + + class HeatPumpAirToWaterCoolingSpeedData_Impl; + + } // namespace detail + + /** HeatPumpAirToWaterCoolingSpeedData is a ParentObject that wraps the OpenStudio IDD object 'OS:HeatPump:AirToWater:Cooling:SpeedData'. */ + class MODEL_API HeatPumpAirToWaterCoolingSpeedData : public ParentObject + { + 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 */ + //@{ + + // returns a vector of HeatPumpAirToWater that use this as their Speed Data + // TODO: std::vector heatPumpsAirToWater() 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..8a3c59acea --- /dev/null +++ b/src/model/HeatPumpAirToWaterCoolingSpeedData_Impl.hpp @@ -0,0 +1,111 @@ +/*********************************************************************************************************************** +* 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 "ParentObject_Impl.hpp" + +namespace openstudio { +namespace model { + + class Curve; + + namespace detail { + + /** HeatPumpAirToWaterCoolingSpeedData_Impl is a ParentObject_Impl that is the implementation class for HeatPumpAirToWaterCoolingSpeedData.*/ + class MODEL_API HeatPumpAirToWaterCoolingSpeedData_Impl : public ParentObject_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 ParentObject_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 */ + //@{ + + // returns a vector of HeatPumpAirToWater that use this as their Speed Data + // TODO: std::vector heatPumpsAirToWater() 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/Model.cpp b/src/model/Model.cpp index a77f6aee93..ce9d074c38 100644 --- a/src/model/Model.cpp +++ b/src/model/Model.cpp @@ -4189,6 +4189,7 @@ namespace model { REGISTER_CONSTRUCTOR(HeatPumpAirToWaterFuelFiredHeating); REGISTER_CONSTRUCTOR(HeatPumpAirToWaterFuelFiredCooling); REGISTER_CONSTRUCTOR(HeatPumpAirToWaterHeatingSpeedData); + REGISTER_CONSTRUCTOR(HeatPumpAirToWaterCoolingSpeedData); REGISTER_CONSTRUCTOR(HeatPumpWaterToWaterEquationFitCooling); REGISTER_CONSTRUCTOR(HeatPumpWaterToWaterEquationFitHeating); REGISTER_CONSTRUCTOR(HeatPumpPlantLoopEIRCooling); @@ -4769,6 +4770,7 @@ namespace model { REGISTER_COPYCONSTRUCTORS(HeatPumpAirToWaterFuelFiredHeating); REGISTER_COPYCONSTRUCTORS(HeatPumpAirToWaterFuelFiredCooling); REGISTER_COPYCONSTRUCTORS(HeatPumpAirToWaterHeatingSpeedData); + REGISTER_COPYCONSTRUCTORS(HeatPumpAirToWaterCoolingSpeedData); REGISTER_COPYCONSTRUCTORS(HeatPumpWaterToWaterEquationFitCooling); REGISTER_COPYCONSTRUCTORS(HeatPumpWaterToWaterEquationFitHeating); REGISTER_COPYCONSTRUCTORS(HeatPumpPlantLoopEIRCooling); diff --git a/src/model/ModelHVAC.i b/src/model/ModelHVAC.i index 6f7b345619..4802c3b14a 100644 --- a/src/model/ModelHVAC.i +++ b/src/model/ModelHVAC.i @@ -225,6 +225,7 @@ MODELOBJECT_TEMPLATES(HeatPumpWaterToWaterEquationFitHeating); MODELOBJECT_TEMPLATES(HeatPumpPlantLoopEIRCooling); MODELOBJECT_TEMPLATES(HeatPumpPlantLoopEIRHeating); MODELOBJECT_TEMPLATES(HeatPumpAirToWaterHeatingSpeedData); +MODELOBJECT_TEMPLATES(HeatPumpAirToWaterCoolingSpeedData); MODELOBJECT_TEMPLATES(ThermalStorageChilledWaterStratified); MODELOBJECT_TEMPLATES(ChillerAbsorptionIndirect); MODELOBJECT_TEMPLATES(ChillerAbsorption); @@ -356,6 +357,7 @@ SWIG_MODELOBJECT(HeatPumpWaterToWaterEquationFitHeating,1); SWIG_MODELOBJECT(HeatPumpPlantLoopEIRCooling,1); SWIG_MODELOBJECT(HeatPumpPlantLoopEIRHeating,1); SWIG_MODELOBJECT(HeatPumpAirToWaterHeatingSpeedData, 1); +SWIG_MODELOBJECT(HeatPumpAirToWaterCoolingSpeedData, 1); SWIG_MODELOBJECT(ThermalStorageChilledWaterStratified, 1); SWIG_MODELOBJECT(ChillerAbsorptionIndirect, 1); SWIG_MODELOBJECT(ChillerAbsorption, 1); diff --git a/src/model/test/HeatPumpAirToWaterCoolingSpeedData_GTest.cpp b/src/model/test/HeatPumpAirToWaterCoolingSpeedData_GTest.cpp new file mode 100644 index 0000000000..b7bb98bdba --- /dev/null +++ b/src/model/test/HeatPumpAirToWaterCoolingSpeedData_GTest.cpp @@ -0,0 +1,187 @@ +/*********************************************************************************************************************** +* 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 "../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) { + GTEST_SKIP() << "Need to implement ModelObjectList support in HeatPumpAirToWater and check that removing a speed data is either prevented or " + "cleans up the ModelObjectList properly."; +} From 65aa931552bc40c18b5314059551975ef25da167 Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Tue, 7 Oct 2025 10:41:37 +0200 Subject: [PATCH 09/45] Move Min PLR field outside of heating input --- resources/model/OpenStudio.idd | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/resources/model/OpenStudio.idd b/resources/model/OpenStudio.idd index 3cd38e1295..eb5b6a1f42 100644 --- a/resources/model/OpenStudio.idd +++ b/resources/model/OpenStudio.idd @@ -14932,13 +14932,18 @@ OS:HeatPump:AirToWater, \note Schedule values control operating mode: 0=off, 1=cooling, 2=heating \type object-list \object-list ScheduleNames - N2 , \field Rated Inlet Air Temperature in Heating Mode + N2, \field Minimum Part Load Ratio + \required-field + \note Below this operating limit compressor cycling will occur + \type real + \minimum 0.0 + N3 , \field Rated Inlet Air Temperature in Heating Mode \required-field \type real \units C \note inlet air dry-bulb temperature corresponding to rated heat pump performance \note (capacity, COP). - N3 , \field Rated Air Flow Rate in Heating Mode + N4 , \field Rated Air Flow Rate in Heating Mode \required-field \type real \units m3/s @@ -14946,13 +14951,13 @@ OS:HeatPump:AirToWater, \autosizable \note air flow rate corresponding to rated heat pump performance \note (capacity, COP). - N4 , \field Rated Leaving Water Temperature in Heating Mode + N5 , \field Rated Leaving Water Temperature in Heating Mode \required-field \type real \units C \note outlet water temperature corresponding to rated heat pump performance \note (heating capacity, COP). - N5 , \field Rated Water Flow Rate in Heating Mode + N6 , \field Rated Water Flow Rate in Heating Mode \required-field \type real \units m3/s @@ -14961,13 +14966,13 @@ OS:HeatPump:AirToWater, \autosizable \note Condenser water flow rate corresponding to rated heat pump performance \note (capacity, COP). - N6, \field Minimum Outdoor Air Temperature in Heating Mode + N7, \field Minimum Outdoor Air Temperature in Heating Mode \required-field \type real \units C \note Enter the minimum outdoor temperature allowed for heating operation \note Heating is disabled below this temperature - N7, \field Maximum Outdoor Air Temperature in Heating Mode + N8, \field Maximum Outdoor Air Temperature in Heating Mode \required-field \type real \units C @@ -14983,11 +14988,6 @@ OS:HeatPump:AirToWater, \object-list UniVariateFunctions \note quadratic curve = a + b*OAT is typical, other univariate curves may be used \note OAT = Outdoor Dry-Bulb Temperature - N8, \field Minimum Part Load Ratio - \required-field - \note Below this operating limit compressor cycling will occur - \type real - \minimum 0.0 N9, \field Sizing Factor for Heating \required-field \note Multiplies the autosized capacity and flow rates From dfa12e3ba65412de1a6ca8c21b1499f487746e48 Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Tue, 7 Oct 2025 11:10:40 +0200 Subject: [PATCH 10/45] Prototype subobjects: OS:HeatPump:AirToWater:Heating/Cooling --- resources/model/OpenStudio.idd | 192 +++++++++++++++++++++++++++++++++ 1 file changed, 192 insertions(+) diff --git a/resources/model/OpenStudio.idd b/resources/model/OpenStudio.idd index eb5b6a1f42..cd94c3900a 100644 --- a/resources/model/OpenStudio.idd +++ b/resources/model/OpenStudio.idd @@ -14878,6 +14878,198 @@ 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: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 + A3 , \field Availability Schedule Name Heating + \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 in Heating Mode + \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 in Heating Mode + \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 in Heating Mode + \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 in Heating Mode + \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 in Heating Mode + \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 in Heating Mode + \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 in Heating Mode + \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 in Heating Mode + \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 for Heating + \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 Heating 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 + A3 , \field Availability Schedule Name Cooling + \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 in Cooling Mode + \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 in Cooling Mode + \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 in Cooling Mode + \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 in Cooling Mode + \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 in Cooling Mode + \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 in Cooling Mode + \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 in Cooling Mode + \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 in Cooling Mode + \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 for Cooling + \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 cooling) and the maximum is 5. + A9; \field Booster Mode On Cooling 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, \memo air-to-water heat pump system which provides either chilled or hot water with single- or variable speed compressors. \min-fields 47 From a477ed9808dd36b1b871e7ad9c8c583f78c63d81 Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Tue, 7 Oct 2025 11:18:15 +0200 Subject: [PATCH 11/45] Remove the " in Heating Mode" (/Cooling) suffix from the objects, it's redundant --- resources/model/OpenStudio.idd | 48 +++++++++++++++++----------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/resources/model/OpenStudio.idd b/resources/model/OpenStudio.idd index cd94c3900a..3601ae9d21 100644 --- a/resources/model/OpenStudio.idd +++ b/resources/model/OpenStudio.idd @@ -14889,20 +14889,20 @@ OS:HeatPump:AirToWater:Heating, \type alpha \note Enter a unique name for this air-to-water heat pump \reference HeatPumpAirToWaterHeating - A3 , \field Availability Schedule Name Heating + 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 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 in Heating Mode + 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 in Heating Mode + N2 , \field Rated Air Flow Rate \required-field \type real \units m3/s @@ -14910,13 +14910,13 @@ OS:HeatPump:AirToWater:Heating, \autosizable \note air flow rate corresponding to rated heat pump performance \note (capacity, COP). - N3 , \field Rated Leaving Water Temperature in Heating Mode + 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 in Heating Mode + N4 , \field Rated Water Flow Rate \required-field \type real \units m3/s @@ -14925,29 +14925,29 @@ OS:HeatPump:AirToWater:Heating, \autosizable \note Condenser water flow rate corresponding to rated heat pump performance \note (capacity, COP). - N5, \field Minimum Outdoor Air Temperature in Heating Mode + 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 in Heating Mode + 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 in Heating Mode + 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 in Heating Mode + 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 for Heating + N7, \field Sizing Factor \required-field \note Multiplies the autosized capacity and flow rates \type real @@ -14962,12 +14962,12 @@ OS:HeatPump:AirToWater:Heating, \type object-list \object-list ConnectionNames \note The node connects to the hot water loop - A8, \field Speed Data List + 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 Heating Speed + 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. @@ -14985,20 +14985,20 @@ OS:HeatPump:AirToWater:Cooling, \type alpha \note Enter a unique name for this air-to-water heat pump \reference HeatPumpAirToWaterCooling - A3 , \field Availability Schedule Name Cooling + 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 in Cooling Mode + 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 in Cooling Mode + N2 , \field Rated Air Flow Rate \required-field \type real \units m3/s @@ -15006,13 +15006,13 @@ OS:HeatPump:AirToWater:Cooling, \autosizable \note air flow rate corresponding to rated heat pump performance \note (capacity, COP). - N3 , \field Rated Leaving Water Temperature in Cooling Mode + 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 in Cooling Mode + N4 , \field Rated Water Flow Rate \required-field \type real \units m3/s @@ -15021,24 +15021,24 @@ OS:HeatPump:AirToWater:Cooling, \autosizable \note Condenser water flow rate corresponding to rated heat pump performance \note (capacity, COP). - N5, \field Minimum Outdoor Air Temperature in Cooling Mode + 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 in Cooling Mode + 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 in Cooling Mode + 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 in Cooling Mode + 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 @@ -15058,12 +15058,12 @@ OS:HeatPump:AirToWater:Cooling, \type object-list \object-list ConnectionNames \note The node connects to the hot water loop - A8, \field Speed Data List + 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 Cooling Speed + 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. From 6643ef2fd811840fd938c07df6772834385595cd Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Tue, 7 Oct 2025 14:10:16 +0200 Subject: [PATCH 12/45] GenerateClass: improve to add the virtual overrides needed for HVACComps and handle Curves properly --- developer/ruby/GenerateClass.rb | 12 +- .../ModelClassGenerator.rb | 142 +++++++++++++++--- 2 files changed, 132 insertions(+), 22 deletions(-) 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 35df688f64..d9d5150fdd 100644 --- a/developer/ruby/SubProjectClassGenerators/ModelClassGenerator.rb +++ b/developer/ruby/SubProjectClassGenerators/ModelClassGenerator.rb @@ -192,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 @@ -238,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 @@ -317,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 @@ -487,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" @@ -542,6 +557,11 @@ def hppSubProjectForwardDeclarations 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 = "" @@ -731,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? @@ -797,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 @@ -824,6 +851,78 @@ def implHppPublicMethods() result << " virtual std::vector getScheduleTypeKeys(const Schedule& schedule) const override;\n\n" end + if @derivesHVACComponent || @baseClassName == 'ParentObject' + 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" @@ -854,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? @@ -910,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 @@ -928,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 @@ -1065,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" @@ -1206,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" @@ -1263,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 @@ -1442,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" From 92164d1179ca18dab4b9c7fd49b97c752b21445b Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Tue, 7 Oct 2025 14:23:54 +0200 Subject: [PATCH 13/45] Initial implementation for HeatPumpAirToWaterHeating Some notes: * I'm considering skipping the ModelObjectList and use extensible groups directly * I'm not sure if I want the SpeedData objects to be unique or shared (I need to switch to ResourceObject in that case I think) * The clone/remove/children need some work --- src/model/CMakeLists.txt | 3 + src/model/ConcreteModelObjects.hpp | 4 +- src/model/HeatPumpAirToWaterHeating.cpp | 692 +++++++++++++++++++ src/model/HeatPumpAirToWaterHeating.hpp | 197 ++++++ src/model/HeatPumpAirToWaterHeating_Impl.hpp | 182 +++++ src/model/Model.cpp | 6 +- src/model/ModelHVAC.i | 6 +- src/model/ScheduleTypeRegistry.cpp | 1 + 8 files changed, 1086 insertions(+), 5 deletions(-) create mode 100644 src/model/HeatPumpAirToWaterHeating.cpp create mode 100644 src/model/HeatPumpAirToWaterHeating.hpp create mode 100644 src/model/HeatPumpAirToWaterHeating_Impl.hpp diff --git a/src/model/CMakeLists.txt b/src/model/CMakeLists.txt index 49921df843..1e41362a8e 100644 --- a/src/model/CMakeLists.txt +++ b/src/model/CMakeLists.txt @@ -920,6 +920,9 @@ set(${target_name}_src HeatPumpAirToWaterFuelFiredCooling.hpp HeatPumpAirToWaterFuelFiredCooling_Impl.hpp HeatPumpAirToWaterFuelFiredCooling.cpp + HeatPumpAirToWaterHeating.hpp + HeatPumpAirToWaterHeating_Impl.hpp + HeatPumpAirToWaterHeating.cpp HeatPumpAirToWaterHeatingSpeedData.hpp HeatPumpAirToWaterHeatingSpeedData_Impl.hpp HeatPumpAirToWaterHeatingSpeedData.cpp diff --git a/src/model/ConcreteModelObjects.hpp b/src/model/ConcreteModelObjects.hpp index cdc58fd143..245c01cf2e 100644 --- a/src/model/ConcreteModelObjects.hpp +++ b/src/model/ConcreteModelObjects.hpp @@ -291,6 +291,7 @@ #include "HeatPumpAirToWaterFuelFiredHeating.hpp" #include "HeatPumpAirToWaterFuelFiredCooling.hpp" #include "HeatPumpAirToWaterCoolingSpeedData.hpp" +#include "HeatPumpAirToWaterHeating.hpp" #include "HeatPumpAirToWaterHeatingSpeedData.hpp" #include "HeatPumpWaterToWaterEquationFitCooling.hpp" #include "HeatPumpWaterToWaterEquationFitHeating.hpp" @@ -851,8 +852,9 @@ #include "HeatExchangerFluidToFluid_Impl.hpp" #include "HeatPumpAirToWaterFuelFiredHeating_Impl.hpp" #include "HeatPumpAirToWaterFuelFiredCooling_Impl.hpp" -#include "HeatPumpAirToWaterHeatingSpeedData_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/HeatPumpAirToWaterHeating.cpp b/src/model/HeatPumpAirToWaterHeating.cpp new file mode 100644 index 0000000000..3990aab133 --- /dev/null +++ b/src/model/HeatPumpAirToWaterHeating.cpp @@ -0,0 +1,692 @@ +/*********************************************************************************************************************** +* OpenStudio(R), Copyright (c) Alliance for Sustainable Energy, LLC. +* See also https://openstudio.net/license +***********************************************************************************************************************/ + +#include "HeatPumpAirToWaterHeating.hpp" +#include "HeatPumpAirToWaterHeating_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 { + LOG_AND_THROW("TODO: implement HeatPumpAirToWaterHeating_Impl::containingHVACComponent"); + } + + ModelObject HeatPumpAirToWaterHeating_Impl::clone(Model model) const { + // This handles resetting the ports, and bypassing ParentObject::clone so it doesn't clone children + auto t_clone = StraightComponent_Impl::clone(model).cast(); + + // TODO: Cloning a ModelObjectList clones the underlying objects too, I don't know if want that + // TODO: if we do not want that, we need to make the Speed Data a ResourceObject instead and not just a ParentObject + // auto speedDataListClone = speedDataList().clone(model).cast(); + // t_clone.getImpl()->setSpeedDataList(speedDataListClone); + + // 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()) { + ok = t_clone.addSpeed(speed); + 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() { + auto speedList = speedDataList(); + std::vector result = StraightComponent_Impl::remove(); + if (!result.empty()) { + 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; + } + + boost::optional HeatPumpAirToWaterHeating_Impl::autosizedRatedAirFlowRate() const { + LOG_AND_THROW("TODO: need to use the name of the WRAPPER object"); + return getAutosizedValue("Design Size Source Side Volume Flow Rate", "m3/s"); + } + + 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; + } + + boost::optional HeatPumpAirToWaterHeating_Impl::autosizedRatedWaterFlowRate() const { + LOG_AND_THROW("TODO: need to use the name of the WRAPPER object"); + return getAutosizedValue("Design Size Load Side Volume Flow Rate", "m3/s"); + } + + 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); + } + + void HeatPumpAirToWaterHeating_Impl::autosize() { + autosizeRatedAirFlowRate(); + autosizeRatedWaterFlowRate(); + } + + void HeatPumpAirToWaterHeating_Impl::applySizingValues() { + if (boost::optional val_ = autosizedRatedAirFlowRate()) { + setRatedAirFlowRate(*val_); + } + + if (boost::optional val_ = autosizedRatedWaterFlowRate()) { + setRatedWaterFlowRate(*val_); + } + } + + 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 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 stages (=" << 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)) { + 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); + } + + /// @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..7011b8efaf --- /dev/null +++ b/src/model/HeatPumpAirToWaterHeating.hpp @@ -0,0 +1,197 @@ +/*********************************************************************************************************************** +* 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 Schedule; + class Curve; + class HeatPumpAirToWaterHeatingSpeedData; + + 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); + + // Autosize methods + boost::optional autosizedRatedAirFlowRate() const; + boost::optional autosizedRatedWaterFlowRate() const; + + //@} + 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/HeatPumpAirToWaterHeating_Impl.hpp b/src/model/HeatPumpAirToWaterHeating_Impl.hpp new file mode 100644 index 0000000000..882a4c7293 --- /dev/null +++ b/src/model/HeatPumpAirToWaterHeating_Impl.hpp @@ -0,0 +1,182 @@ +/*********************************************************************************************************************** +* 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 Schedule; + class Curve; + class Node; + class ModelObjectList; + class HeatPumpAirToWaterHeatingSpeedData; + + 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(); + + // Autosize methods + + boost::optional autosizedRatedAirFlowRate() const; + boost::optional autosizedRatedWaterFlowRate() const; + //@} + 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/Model.cpp b/src/model/Model.cpp index ce9d074c38..61a8592966 100644 --- a/src/model/Model.cpp +++ b/src/model/Model.cpp @@ -4188,8 +4188,9 @@ namespace model { REGISTER_CONSTRUCTOR(HeatExchangerFluidToFluid); REGISTER_CONSTRUCTOR(HeatPumpAirToWaterFuelFiredHeating); REGISTER_CONSTRUCTOR(HeatPumpAirToWaterFuelFiredCooling); - REGISTER_CONSTRUCTOR(HeatPumpAirToWaterHeatingSpeedData); REGISTER_CONSTRUCTOR(HeatPumpAirToWaterCoolingSpeedData); + REGISTER_CONSTRUCTOR(HeatPumpAirToWaterHeating); + REGISTER_CONSTRUCTOR(HeatPumpAirToWaterHeatingSpeedData); REGISTER_CONSTRUCTOR(HeatPumpWaterToWaterEquationFitCooling); REGISTER_CONSTRUCTOR(HeatPumpWaterToWaterEquationFitHeating); REGISTER_CONSTRUCTOR(HeatPumpPlantLoopEIRCooling); @@ -4769,8 +4770,9 @@ namespace model { REGISTER_COPYCONSTRUCTORS(HeatExchangerFluidToFluid); REGISTER_COPYCONSTRUCTORS(HeatPumpAirToWaterFuelFiredHeating); REGISTER_COPYCONSTRUCTORS(HeatPumpAirToWaterFuelFiredCooling); - REGISTER_COPYCONSTRUCTORS(HeatPumpAirToWaterHeatingSpeedData); 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 4802c3b14a..569ffa7130 100644 --- a/src/model/ModelHVAC.i +++ b/src/model/ModelHVAC.i @@ -224,8 +224,9 @@ MODELOBJECT_TEMPLATES(HeatPumpWaterToWaterEquationFitCooling); MODELOBJECT_TEMPLATES(HeatPumpWaterToWaterEquationFitHeating); MODELOBJECT_TEMPLATES(HeatPumpPlantLoopEIRCooling); MODELOBJECT_TEMPLATES(HeatPumpPlantLoopEIRHeating); -MODELOBJECT_TEMPLATES(HeatPumpAirToWaterHeatingSpeedData); MODELOBJECT_TEMPLATES(HeatPumpAirToWaterCoolingSpeedData); +MODELOBJECT_TEMPLATES(HeatPumpAirToWaterHeating); +MODELOBJECT_TEMPLATES(HeatPumpAirToWaterHeatingSpeedData); MODELOBJECT_TEMPLATES(ThermalStorageChilledWaterStratified); MODELOBJECT_TEMPLATES(ChillerAbsorptionIndirect); MODELOBJECT_TEMPLATES(ChillerAbsorption); @@ -356,8 +357,9 @@ SWIG_MODELOBJECT(HeatPumpWaterToWaterEquationFitCooling,1); SWIG_MODELOBJECT(HeatPumpWaterToWaterEquationFitHeating,1); SWIG_MODELOBJECT(HeatPumpPlantLoopEIRCooling,1); SWIG_MODELOBJECT(HeatPumpPlantLoopEIRHeating,1); -SWIG_MODELOBJECT(HeatPumpAirToWaterHeatingSpeedData, 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 6ede93052b..bff03d12f2 100644 --- a/src/model/ScheduleTypeRegistry.cpp +++ b/src/model/ScheduleTypeRegistry.cpp @@ -328,6 +328,7 @@ 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}, + {"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}, From 71873fdd2b673a5c3c267f827326b648c288619f Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Tue, 7 Oct 2025 16:12:34 +0200 Subject: [PATCH 14/45] Start testing --- src/model/CMakeLists.txt | 9 +- .../test/HeatPumpAirToWaterHeating_GTest.cpp | 143 ++++++++++++++++++ 2 files changed, 148 insertions(+), 4 deletions(-) create mode 100644 src/model/test/HeatPumpAirToWaterHeating_GTest.cpp diff --git a/src/model/CMakeLists.txt b/src/model/CMakeLists.txt index 1e41362a8e..767c18bca5 100644 --- a/src/model/CMakeLists.txt +++ b/src/model/CMakeLists.txt @@ -920,15 +920,15 @@ set(${target_name}_src HeatPumpAirToWaterFuelFiredCooling.hpp HeatPumpAirToWaterFuelFiredCooling_Impl.hpp HeatPumpAirToWaterFuelFiredCooling.cpp + HeatPumpAirToWaterCoolingSpeedData.hpp + HeatPumpAirToWaterCoolingSpeedData_Impl.hpp + HeatPumpAirToWaterCoolingSpeedData.cpp HeatPumpAirToWaterHeating.hpp HeatPumpAirToWaterHeating_Impl.hpp HeatPumpAirToWaterHeating.cpp HeatPumpAirToWaterHeatingSpeedData.hpp HeatPumpAirToWaterHeatingSpeedData_Impl.hpp HeatPumpAirToWaterHeatingSpeedData.cpp - HeatPumpAirToWaterCoolingSpeedData.hpp - HeatPumpAirToWaterCoolingSpeedData_Impl.hpp - HeatPumpAirToWaterCoolingSpeedData.cpp HeatPumpWaterToWaterEquationFitCooling.hpp HeatPumpWaterToWaterEquationFitCooling_Impl.hpp HeatPumpWaterToWaterEquationFitCooling.cpp @@ -2172,8 +2172,9 @@ set(${target_name}_test_src test/HeatExchangerFluidToFluid_GTest.cpp test/HeatPumpAirToWaterFuelFiredHeating_GTest.cpp test/HeatPumpAirToWaterFuelFiredCooling_GTest.cpp - test/HeatPumpAirToWaterHeatingSpeedData_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/test/HeatPumpAirToWaterHeating_GTest.cpp b/src/model/test/HeatPumpAirToWaterHeating_GTest.cpp new file mode 100644 index 0000000000..6e2b6cb03f --- /dev/null +++ b/src/model/test/HeatPumpAirToWaterHeating_GTest.cpp @@ -0,0 +1,143 @@ +/*********************************************************************************************************************** +* 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 "../HeatPumpAirToWaterHeatingSpeedData.hpp" +#include "../HeatPumpAirToWaterHeatingSpeedData_Impl.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 "../ModelObjectList.hpp" +#include "../ModelObjectList_Impl.hpp" + +using namespace openstudio; +using namespace openstudio::model; + +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, FuelType::Propane}, awhp.heatingFuelTypes()); + testAppGFuelTypeEquality({AppGFuelType::HeatPump}, awhp.appGHeatingFuelTypes()); +} From 3b25f898af077993f9f4b6695d820015f2ee8583 Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Thu, 9 Oct 2025 09:33:14 +0200 Subject: [PATCH 15/45] Correct casing of UnivariateFunctions --- resources/model/OpenStudio.idd | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/resources/model/OpenStudio.idd b/resources/model/OpenStudio.idd index 3601ae9d21..e1e071e374 100644 --- a/resources/model/OpenStudio.idd +++ b/resources/model/OpenStudio.idd @@ -14939,12 +14939,12 @@ OS:HeatPump:AirToWater:Heating, \note Heating is disabled above this temperature A4, \field Minimum Leaving Water Temperature Curve Name \type object-list - \object-list UniVariateFunctions + \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 + \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 @@ -15035,12 +15035,12 @@ OS:HeatPump:AirToWater:Cooling, \note Cooling is disabled above this temperature A4, \field Minimum Leaving Water Temperature Curve Name \type object-list - \object-list UniVariateFunctions + \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 + \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 for Cooling @@ -15172,12 +15172,12 @@ OS:HeatPump:AirToWater, \note Heating is disabled above this temperature A8, \field Minimum Leaving Water Temperature Curve Name in Heating Mode \type object-list - \object-list UniVariateFunctions + \object-list UnivariateFunctions \note quadratic curve = a + b*OAT is typical, other univariate curves may be used \note OAT = Outdoor Dry-Bulb Temperature A9, \field Maximum Leaving Water Temperature Curve Name in Heating Mode \type object-list - \object-list UniVariateFunctions + \object-list UnivariateFunctions \note quadratic curve = a + b*OAT is typical, other univariate curves may be used \note OAT = Outdoor Dry-Bulb Temperature N9, \field Sizing Factor for Heating @@ -15228,12 +15228,12 @@ OS:HeatPump:AirToWater, \note Cooling is disabled above this temperature A10, \field Minimum Leaving Water Temperature Curve Name in Cooling Mode \type object-list - \object-list UniVariateFunctions + \object-list UnivariateFunctions \note quadratic curve = a + b*OAT is typical, other univariate curves may be used \note OAT = Outdoor Dry-Bulb Temperature A11, \field Maximum Leaving Water Temperature Curve Name in Cooling Mode \type object-list - \object-list UniVariateFunctions + \object-list UnivariateFunctions \note quadratic curve = a + b*OAT is typical, other univariate curves may be used \note OAT = Outdoor Dry-Bulb Temperature N16, \field Sizing Factor for Cooling From 3eb7cb0e7caa99600d03ed22e132f7bc3840524e Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Thu, 9 Oct 2025 15:17:27 +0200 Subject: [PATCH 16/45] Wrap HeatPump:AirToWater, add missing ConnectionObject reference for the Heating/Cooling side --- .../ModelClassGenerator.rb | 10 +- resources/model/OpenStudio.idd | 436 ++++------- src/model/CMakeLists.txt | 8 + src/model/ConcreteModelObjects.hpp | 4 + src/model/HeatPumpAirToWater.cpp | 686 +++++++++++++++++ src/model/HeatPumpAirToWater.hpp | 190 +++++ src/model/HeatPumpAirToWaterCooling.cpp | 694 ++++++++++++++++++ src/model/HeatPumpAirToWaterCooling.hpp | 197 +++++ src/model/HeatPumpAirToWaterCooling_Impl.hpp | 182 +++++ src/model/HeatPumpAirToWaterHeating.cpp | 6 +- src/model/HeatPumpAirToWater_Impl.hpp | 194 +++++ src/model/Model.cpp | 4 + src/model/ModelHVAC.i | 2 + src/model/ScheduleTypeRegistry.cpp | 2 + .../test/HeatPumpAirToWaterCooling_GTest.cpp | 323 ++++++++ .../test/HeatPumpAirToWaterHeating_GTest.cpp | 260 ++++++- src/model/test/HeatPumpAirToWater_GTest.cpp | 271 +++++++ 17 files changed, 3165 insertions(+), 304 deletions(-) create mode 100644 src/model/HeatPumpAirToWater.cpp create mode 100644 src/model/HeatPumpAirToWater.hpp create mode 100644 src/model/HeatPumpAirToWaterCooling.cpp create mode 100644 src/model/HeatPumpAirToWaterCooling.hpp create mode 100644 src/model/HeatPumpAirToWaterCooling_Impl.hpp create mode 100644 src/model/HeatPumpAirToWater_Impl.hpp create mode 100644 src/model/test/HeatPumpAirToWaterCooling_GTest.cpp create mode 100644 src/model/test/HeatPumpAirToWater_GTest.cpp diff --git a/developer/ruby/SubProjectClassGenerators/ModelClassGenerator.rb b/developer/ruby/SubProjectClassGenerators/ModelClassGenerator.rb index d9d5150fdd..4b2fd2a751 100644 --- a/developer/ruby/SubProjectClassGenerators/ModelClassGenerator.rb +++ b/developer/ruby/SubProjectClassGenerators/ModelClassGenerator.rb @@ -238,7 +238,7 @@ def getterReturnType(forceOptional=nil) result = "std::string" end elsif isObjectList? - if ["UniVariateFunctions", "BiVariateFunctions"].include?(objectListClassName) + if ["UnivariateFunctions", "BivariateFunctions"].include?(objectListClassName) result = "Curve" elsif objectListClassName == "Connection" result = "Node" @@ -323,7 +323,7 @@ def publicClassSetterType elsif isObjectList? if isSchedule? result = objectListClassName + "&" - elsif ["UniVariateFunctions", "BiVariateFunctions"].include?(objectListClassName) + elsif ["UnivariateFunctions", "BivariateFunctions"].include?(objectListClassName) result = "const Curve&" elsif objectListClassName == "Connection" result = "const Node&" @@ -497,7 +497,7 @@ def cppIncludes() preamble = "// TODO: Check the following class names against object getters and setters.\n" @objectListClassNames.each { |className| - if ["UniVariateFunctions", "BiVariateFunctions"].include?(className) + if ["UnivariateFunctions", "BivariateFunctions"].include?(className) className = "Curve" elsif className == "Connection" className = "Node" @@ -557,7 +557,7 @@ def hppSubProjectForwardDeclarations if @iddObject preamble = " // TODO: Check the following class names against object getters and setters.\n" @objectListClassNames.each { |className| - if ["UniVariateFunctions", "BiVariateFunctions"].include?(className) + if ["UnivariateFunctions", "BivariateFunctions"].include?(className) className = "Curve" elsif className == "Connection" className = "Node" @@ -1539,7 +1539,7 @@ def gtestIncludes() # Check for ObjectList fields, to see which we need to include @objectListClassNames.each { |className| - if ["UniVariateFunctions", "BiVariateFunctions"].include?(className) + if ["UnivariateFunctions", "BivariateFunctions"].include?(className) className = "Curve" elsif className == "Connection" className = "Node" diff --git a/resources/model/OpenStudio.idd b/resources/model/OpenStudio.idd index e1e071e374..27fe4f6772 100644 --- a/resources/model/OpenStudio.idd +++ b/resources/model/OpenStudio.idd @@ -14878,127 +14878,155 @@ 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:Heating, - \memo air-to-water heat pump system which provides hot water with single or variable speed compressors. - \min-fields 16 - A1 , \field Handle +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 47 + A1, \field Handle \required-field \type handle - A2 , \field Name + A2, \field Name \required-field \type alpha \note Enter a unique name for this air-to-water heat pump - \reference HeatPumpAirToWaterHeating - A3 , \field Availability Schedule Name + 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 - \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 + N1, \field Minimum Part Load Ratio \required-field + \note Below this operating limit compressor cycling will occur \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 + \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 - \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 + \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 - \units C - \note outlet water temperature corresponding to rated heat pump performance - \note (heating capacity, COP). - N4 , \field Rated Water Flow Rate + \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 - \units m3/s - \ip-units gal/min - \minimum> 0.0 + \minimum 0.0 \autosizable - \note Condenser water flow rate corresponding to rated heat pump performance - \note (capacity, COP). - N5, \field Minimum Outdoor Air Temperature + \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 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 + \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 - \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 + \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 - \note quadratic curve = a + b*OAT is typical, other univariate curves may be used - \note OAT = Outdoor Dry-Bulb Temperature - N7, \field Sizing Factor + N7, \field Maximum Ambient Temperature for Crankcase Heater Operation \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 + \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 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. + \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 HeatPumpAirToWaterHeatingSpeedData + \object-list HeatPumpAirToWaterCooling + \note A HeatPump:AirToWater:Cooling object. If not specified, cooling is disabled. -OS:HeatPump:AirToWater:Cooling, +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 + A1, \field Handle \required-field \type handle - A2 , \field Name + A2, \field Name \required-field \type alpha \note Enter a unique name for this air-to-water heat pump - \reference HeatPumpAirToWaterCooling - A3 , \field Availability Schedule Name + \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 cooling mode + \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 + 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 + N2, \field Rated Air Flow Rate \required-field \type real \units m3/s @@ -15006,12 +15034,12 @@ OS:HeatPump:AirToWater:Cooling, \autosizable \note air flow rate corresponding to rated heat pump performance \note (capacity, COP). - N3 , \field Rated Leaving Water Temperature + 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). + \note (heating capacity, COP). N4 , \field Rated Water Flow Rate \required-field \type real @@ -15025,14 +15053,14 @@ OS:HeatPump:AirToWater:Cooling, \required-field \type real \units C - \note Enter the minimum outdoor temperature allowed for cooling operation - \note Cooling is disabled below this temperature + \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 cooling operation - \note Cooling is disabled above this temperature + \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 @@ -15043,7 +15071,7 @@ OS:HeatPump:AirToWater:Cooling, \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 for Cooling + N7, \field Sizing Factor \required-field \note Multiplies the autosized capacity and flow rates \type real @@ -15062,80 +15090,40 @@ OS:HeatPump:AirToWater:Cooling, \required-field \type object-list \object-list ModelObjectLists - \note The minimum number of speeds is 0 (no cooling) and the maximum is 5. + \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 cooling operation. - \note The heat pump will operate at a higher capacity during cooling operation. + \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 HeatPumpAirToWaterCoolingSpeedData + \object-list HeatPumpAirToWaterHeatingSpeedData -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 47 - A1 , \field Handle +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 + A2, \field Name \required-field \type alpha \note Enter a unique name for this air-to-water heat pump - A3 , \field Availability Schedule Name Heating - \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 - A4 , \field Availability Schedule Name Cooling + \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 - A5 , \field Operating Mode Control Method - \required-field - \type choice - \key ScheduledModes - \key EMSControlled - \key Load - A6, \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 - A7 , \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 - N2, \field Minimum Part Load Ratio - \required-field - \note Below this operating limit compressor cycling will occur - \type real - \minimum 0.0 - N3 , \field Rated Inlet Air Temperature in Heating Mode + 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). - N4 , \field Rated Air Flow Rate in Heating Mode + N2, \field Rated Air Flow Rate \required-field \type real \units m3/s @@ -15143,69 +15131,13 @@ OS:HeatPump:AirToWater, \autosizable \note air flow rate corresponding to rated heat pump performance \note (capacity, COP). - N5 , \field Rated Leaving Water Temperature in Heating Mode + 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). - N6 , \field Rated Water Flow Rate in Heating Mode - \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). - N7, \field Minimum Outdoor Air Temperature in Heating Mode - \required-field - \type real - \units C - \note Enter the minimum outdoor temperature allowed for heating operation - \note Heating is disabled below this temperature - N8, \field Maximum Outdoor Air Temperature in Heating Mode - \required-field - \type real - \units C - \note Enter the maximum outdoor temperature allowed for heating operation - \note Heating is disabled above this temperature - A8, \field Minimum Leaving Water Temperature Curve Name in Heating Mode - \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 - A9, \field Maximum Leaving Water Temperature Curve Name in Heating Mode - \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 - N9, \field Sizing Factor for Heating - \required-field - \note Multiplies the autosized capacity and flow rates - \type real - \minimum> 0.0 - N10, \field Rated Inlet Air Temperature in Cooling Mode - \required-field - \type real - \units C - \note inlet air dry-bulb temperature corresponding to rated heat pump performance - \note (capacity, COP). - N11, \field Rated Air Flow Rate in Cooling Mode - \required-field - \type real - \units m3/s - \minimum> 0.0 - \autosizable - \note air flow rate corresponding to rated heat pump performance - \note (capacity, COP). - N12, \field Rated Leaving Water Temperature in Cooling Mode - \required-field - \type real - \units C - \note inlet water temperature corresponding to rated performance - \note (heating capacity, COP). - N13, \field Rated Water Flow Rate in Cooling Mode + \note (cooling capacity, COP). + N4, \field Rated Water Flow Rate \required-field \type real \units m3/s @@ -15214,135 +15146,49 @@ OS:HeatPump:AirToWater, \autosizable \note Condenser water flow rate corresponding to rated heat pump performance \note (capacity, COP). - N14, \field Minimum Outdoor Air Temperature in Cooling Mode + 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 - N15, \field Maximum Outdoor Air Temperature in Cooling Mode + 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 - A10, \field Minimum Leaving Water Temperature Curve Name in Cooling Mode + 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 - A11, \field Maximum Leaving Water Temperature Curve Name in Cooling Mode + 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 - N16, \field Sizing Factor for Cooling + N7, \field Sizing Factor \required-field \note Multiplies the autosized capacity and flow rates \type real \minimum> 0.0 - A12, \field Air Inlet Node Name - \type alpha - \note The node from which the heat pump draws its inlet air. - A13, \field Air Outlet Node Name - \type alpha - \note The node to which the heat pump sends its outlet air. - A14, \field Hot Water Inlet Node Name + A6, \field Chilled Water Inlet Node Name + \required-field \type object-list \object-list ConnectionNames \note The node connects to the hot water loop - A15, \field Hot Water Outlet Node Name + A7, \field Chilled Water Outlet Node Name + \required-field \type object-list \object-list ConnectionNames \note The node connects to the hot water loop - A16, \field Chilled Water Inlet Node Name - \type object-list - \object-list ConnectionNames - \note The node connects to the chilled water loop - A17, \field Chilled Water Outlet Node Name - \type object-list - \object-list ConnectionNames - \note The node connects to the chilled water loop - N17, \field Maximum Outdoor Dry Bulb Temperature For Defrost Operation - \required-field - \note defrost operation will not be active above this outdoor temperature - A18, \field Heat Pump Defrost Control - \required-field - \type choice - \key None - \key Timed - \key OnDemand - \key TimedEmpirical - N18, \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 - N19, \field Resistive Defrost Heater Capacity - \required-field - \type real - \minimum 0.0 - \autosizable - \units W - \note only applicable if resistive defrost strategy is specified - \ip-units W - A19, \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 - N20, \field Heat Pump Multiplier - \required-field - \type integer - \note intend to model modular heat pumps - A20, \field Control Type - \required-field - \type choice - \key FixedSpeed - \key VariableSpeed - N21, \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. - A21, \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 - N22, \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. - A22, \field Heating 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. - A23, \field Booster Mode On Heating Speed Data - \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 - A24, \field Cooling Speed Data List + 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. - A25; \field Booster Mode On Cooling Speed Data + 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. diff --git a/src/model/CMakeLists.txt b/src/model/CMakeLists.txt index 767c18bca5..ed16f521b0 100644 --- a/src/model/CMakeLists.txt +++ b/src/model/CMakeLists.txt @@ -920,6 +920,12 @@ 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 @@ -2172,6 +2178,8 @@ 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 diff --git a/src/model/ConcreteModelObjects.hpp b/src/model/ConcreteModelObjects.hpp index 245c01cf2e..97b16b6232 100644 --- a/src/model/ConcreteModelObjects.hpp +++ b/src/model/ConcreteModelObjects.hpp @@ -290,6 +290,8 @@ #include "HeatExchangerFluidToFluid.hpp" #include "HeatPumpAirToWaterFuelFiredHeating.hpp" #include "HeatPumpAirToWaterFuelFiredCooling.hpp" +#include "HeatPumpAirToWater.hpp" +#include "HeatPumpAirToWaterCooling.hpp" #include "HeatPumpAirToWaterCoolingSpeedData.hpp" #include "HeatPumpAirToWaterHeating.hpp" #include "HeatPumpAirToWaterHeatingSpeedData.hpp" @@ -852,6 +854,8 @@ #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" diff --git a/src/model/HeatPumpAirToWater.cpp b/src/model/HeatPumpAirToWater.cpp new file mode 100644 index 0000000000..2747282fb4 --- /dev/null +++ b/src/model/HeatPumpAirToWater.cpp @@ -0,0 +1,686 @@ +/*********************************************************************************************************************** +* OpenStudio(R), Copyright (c) Alliance for Sustainable Energy, LLC. +* See also https://openstudio.net/license +***********************************************************************************************************************/ + +#include "HeatPumpAirToWater.hpp" +#include "HeatPumpAirToWater_Impl.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/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; + } + + 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(); + } + + boost::optional HeatPumpAirToWater_Impl::resistiveDefrostHeaterCapacity() const { + return getDouble(OS_HeatPump_AirToWaterFields::ResistiveDefrostHeaterCapacity, true); + } + + bool HeatPumpAirToWater_Impl::isResistiveDefrostHeaterCapacityAutosized() const { + bool result = false; + boost::optional value = getString(OS_HeatPump_AirToWaterFields::ResistiveDefrostHeaterCapacity, true); + if (value) { + result = openstudio::istringEqual(value.get(), "autosize"); + } + return result; + } + + boost::optional HeatPumpAirToWater_Impl::autosizedResistiveDefrostHeaterCapacity() const { + return getAutosizedValue("TODO_CHECK_SQL Resistive Defrost Heater Capacity", "W"); + } + + 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) { + 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) { + const bool result = setSchedule(OS_HeatPump_AirToWaterFields::OperatingModeControlScheduleName, "HeatPumpAirToWater", "Operating Mode Control", + operatingModeControlSchedule); + return result; + } + + void HeatPumpAirToWater_Impl::resetOperatingModeControlSchedule() { + const bool result = setString(OS_HeatPump_AirToWaterFields::OperatingModeControlScheduleName, ""); + OS_ASSERT(result); + } + + 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; + } + + void HeatPumpAirToWater_Impl::autosizeResistiveDefrostHeaterCapacity() { + const bool result = setString(OS_HeatPump_AirToWaterFields::ResistiveDefrostHeaterCapacity, "autosize"); + OS_ASSERT(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) { + 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) { + 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); + } + + void HeatPumpAirToWater_Impl::autosize() { + autosizeResistiveDefrostHeaterCapacity(); + } + + void HeatPumpAirToWater_Impl::applySizingValues() { + if (boost::optional val_ = autosizedResistiveDefrostHeaterCapacity()) { + setResistiveDefrostHeaterCapacity(*val_); + } + } + + 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; + } + + } // 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(); + } + + boost::optional HeatPumpAirToWater::resistiveDefrostHeaterCapacity() const { + return getImpl()->resistiveDefrostHeaterCapacity(); + } + + bool HeatPumpAirToWater::isResistiveDefrostHeaterCapacityAutosized() const { + return getImpl()->isResistiveDefrostHeaterCapacityAutosized(); + } + + boost::optional HeatPumpAirToWater::autosizedResistiveDefrostHeaterCapacity() const { + return getImpl()->autosizedResistiveDefrostHeaterCapacity(); + } + + 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); + } + + void HeatPumpAirToWater::autosizeResistiveDefrostHeaterCapacity() { + getImpl()->autosizeResistiveDefrostHeaterCapacity(); + } + + 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(); + } + + /// @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..7286367bc2 --- /dev/null +++ b/src/model/HeatPumpAirToWater.hpp @@ -0,0 +1,190 @@ +/*********************************************************************************************************************** +* 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; + + boost::optional resistiveDefrostHeaterCapacity() const; + + bool isResistiveDefrostHeaterCapacityAutosized() 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); + + void autosizeResistiveDefrostHeaterCapacity(); + + 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 autosizedResistiveDefrostHeaterCapacity() 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..00586d0211 --- /dev/null +++ b/src/model/HeatPumpAirToWaterCooling.cpp @@ -0,0 +1,694 @@ +/*********************************************************************************************************************** +* OpenStudio(R), Copyright (c) Alliance for Sustainable Energy, LLC. +* See also https://openstudio.net/license +***********************************************************************************************************************/ + +#include "HeatPumpAirToWaterCooling.hpp" +#include "HeatPumpAirToWaterCooling_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 { + // TODO: implement HeatPumpAirToWaterCooling_Impl::containingHVACComponent" + return boost::none; + } + + ModelObject HeatPumpAirToWaterCooling_Impl::clone(Model model) const { + // This handles resetting the ports, and bypassing ParentObject::clone so it doesn't clone children + auto t_clone = StraightComponent_Impl::clone(model).cast(); + + // TODO: Cloning a ModelObjectList clones the underlying objects too, I don't know if want that + // TODO: if we do not want that, we need to make the Speed Data a ResourceObject instead and not just a ParentObject + // auto speedDataListClone = speedDataList().clone(model).cast(); + // t_clone.getImpl()->setSpeedDataList(speedDataListClone); + + // 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()) { + ok = t_clone.addSpeed(speed); + 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() { + auto speedList = speedDataList(); + std::vector result = StraightComponent_Impl::remove(); + if (!result.empty()) { + 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; + } + + boost::optional HeatPumpAirToWaterCooling_Impl::autosizedRatedAirFlowRate() const { + LOG_AND_THROW("TODO: need to use the name of the WRAPPER object"); + return getAutosizedValue("Design Size Source Side Volume Flow Rate", "m3/s"); + } + + 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; + } + + boost::optional HeatPumpAirToWaterCooling_Impl::autosizedRatedWaterFlowRate() const { + LOG_AND_THROW("TODO: need to use the name of the WRAPPER object"); + return getAutosizedValue("Design Size Load Side Volume Flow Rate", "m3/s"); + } + + 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); + } + + void HeatPumpAirToWaterCooling_Impl::autosize() { + autosizeRatedAirFlowRate(); + autosizeRatedWaterFlowRate(); + } + + void HeatPumpAirToWaterCooling_Impl::applySizingValues() { + if (boost::optional val_ = autosizedRatedAirFlowRate()) { + setRatedAirFlowRate(*val_); + } + + if (boost::optional val_ = autosizedRatedWaterFlowRate()) { + setRatedWaterFlowRate(*val_); + } + } + + 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 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) + // 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 + + 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); + } + + /// @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..5d7ceb7097 --- /dev/null +++ b/src/model/HeatPumpAirToWaterCooling.hpp @@ -0,0 +1,197 @@ +/*********************************************************************************************************************** +* 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 Schedule; + class Curve; + class HeatPumpAirToWaterCoolingSpeedData; + + 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); + + // Autosize methods + boost::optional autosizedRatedAirFlowRate() const; + boost::optional autosizedRatedWaterFlowRate() const; + + //@} + 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/HeatPumpAirToWaterCooling_Impl.hpp b/src/model/HeatPumpAirToWaterCooling_Impl.hpp new file mode 100644 index 0000000000..662bf00d88 --- /dev/null +++ b/src/model/HeatPumpAirToWaterCooling_Impl.hpp @@ -0,0 +1,182 @@ +/*********************************************************************************************************************** +* 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 Schedule; + class Curve; + class Node; + class ModelObjectList; + class HeatPumpAirToWaterCoolingSpeedData; + + 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(); + + // Autosize methods + + boost::optional autosizedRatedAirFlowRate() const; + boost::optional autosizedRatedWaterFlowRate() const; + //@} + 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 index 3990aab133..702e29961e 100644 --- a/src/model/HeatPumpAirToWaterHeating.cpp +++ b/src/model/HeatPumpAirToWaterHeating.cpp @@ -93,7 +93,8 @@ namespace model { } boost::optional HeatPumpAirToWaterHeating_Impl::containingHVACComponent() const { - LOG_AND_THROW("TODO: implement HeatPumpAirToWaterHeating_Impl::containingHVACComponent"); + // TODO: implement HeatPumpAirToWaterHeating_Impl::containingHVACComponent" + return boost::none; } ModelObject HeatPumpAirToWaterHeating_Impl::clone(Model model) const { @@ -408,7 +409,7 @@ namespace model { bool HeatPumpAirToWaterHeating_Impl::addSpeed(const HeatPumpAirToWaterHeatingSpeedData& speed) { if (numberOfSpeeds() >= HeatPumpAirToWaterHeating::maximum_number_of_speeds) { - LOG(Error, "You have reached the maximum number of stages (=" << HeatPumpAirToWaterHeating::maximum_number_of_speeds << "), occurred for " + LOG(Error, "You have reached the maximum number of speeds (=" << HeatPumpAirToWaterHeating::maximum_number_of_speeds << "), occurred for " << briefDescription() << "."); return false; // too many speeds } @@ -465,6 +466,7 @@ namespace model { 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); diff --git a/src/model/HeatPumpAirToWater_Impl.hpp b/src/model/HeatPumpAirToWater_Impl.hpp new file mode 100644 index 0000000000..bec440b6b8 --- /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; + + // TODO: You may need to override these since 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; + + // TODO: If your component can be contained, override these + // 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 */ + //@{ + + 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; + + boost::optional resistiveDefrostHeaterCapacity() const; + + bool isResistiveDefrostHeaterCapacityAutosized() 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); + + void autosizeResistiveDefrostHeaterCapacity(); + + 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 autosizedResistiveDefrostHeaterCapacity() 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 61a8592966..780643fdc2 100644 --- a/src/model/Model.cpp +++ b/src/model/Model.cpp @@ -4188,6 +4188,8 @@ 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); @@ -4770,6 +4772,8 @@ 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); diff --git a/src/model/ModelHVAC.i b/src/model/ModelHVAC.i index 569ffa7130..27dc2b719b 100644 --- a/src/model/ModelHVAC.i +++ b/src/model/ModelHVAC.i @@ -224,6 +224,7 @@ MODELOBJECT_TEMPLATES(HeatPumpWaterToWaterEquationFitCooling); MODELOBJECT_TEMPLATES(HeatPumpWaterToWaterEquationFitHeating); MODELOBJECT_TEMPLATES(HeatPumpPlantLoopEIRCooling); MODELOBJECT_TEMPLATES(HeatPumpPlantLoopEIRHeating); +MODELOBJECT_TEMPLATES(HeatPumpAirToWater) MODELOBJECT_TEMPLATES(HeatPumpAirToWaterCoolingSpeedData); MODELOBJECT_TEMPLATES(HeatPumpAirToWaterHeating); MODELOBJECT_TEMPLATES(HeatPumpAirToWaterHeatingSpeedData); @@ -357,6 +358,7 @@ SWIG_MODELOBJECT(HeatPumpWaterToWaterEquationFitCooling,1); SWIG_MODELOBJECT(HeatPumpWaterToWaterEquationFitHeating,1); SWIG_MODELOBJECT(HeatPumpPlantLoopEIRCooling,1); SWIG_MODELOBJECT(HeatPumpPlantLoopEIRHeating,1); +SWIG_MODELOBJECT(HeatPumpAirToWater, 1); SWIG_MODELOBJECT(HeatPumpAirToWaterCoolingSpeedData, 1); SWIG_MODELOBJECT(HeatPumpAirToWaterHeating,1); SWIG_MODELOBJECT(HeatPumpAirToWaterHeatingSpeedData, 1); diff --git a/src/model/ScheduleTypeRegistry.cpp b/src/model/ScheduleTypeRegistry.cpp index bff03d12f2..cfc5a08502 100644 --- a/src/model/ScheduleTypeRegistry.cpp +++ b/src/model/ScheduleTypeRegistry.cpp @@ -328,6 +328,8 @@ 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}, diff --git a/src/model/test/HeatPumpAirToWaterCooling_GTest.cpp b/src/model/test/HeatPumpAirToWaterCooling_GTest.cpp new file mode 100644 index 0000000000..ee89573329 --- /dev/null +++ b/src/model/test/HeatPumpAirToWaterCooling_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 "../HeatPumpAirToWaterCooling.hpp" +#include "../HeatPumpAirToWaterCooling_Impl.hpp" + +#include "../HeatPumpAirToWaterCoolingSpeedData.hpp" +#include "../HeatPumpAirToWaterCoolingSpeedData_Impl.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 "../ModelObjectList.hpp" +#include "../ModelObjectList_Impl.hpp" + +using namespace openstudio; +using namespace openstudio::model; + +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()); + + 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, awhp.numberOfSpeeds()); + EXPECT_EQ(speeds, awhp.speeds()); + + EXPECT_EQ(5, awhpClone.numberOfSpeeds()); + EXPECT_EQ(speeds, awhpClone.speeds()); + + // This shouldn't work, it's going to put the CoilCoolingDXCurveFitPerformance in a broken state + auto rmed = awhp.remove(); + EXPECT_EQ(0, rmed.size()); + HeatPumpAirToWaterCoolingSpeedData boosterModeOnSpeed(model); + EXPECT_TRUE(awhp.setBoosterModeOnSpeed(boosterModeOnSpeed)); + ASSERT_TRUE(awhp.boosterModeOnSpeed()); + EXPECT_EQ(3, model.getConcreteModelObjects().size()); + EXPECT_EQ(10, model.getConcreteModelObjects().size()); + + rmed = awhp.remove(); + EXPECT_EQ(1, rmed.size()); + EXPECT_EQ(2, model.getConcreteModelObjects().size()); + EXPECT_EQ(10, model.getConcreteModelObjects().size()); + EXPECT_EQ(10, awhpClone.numberOfSpeeds()); + EXPECT_EQ(speeds, awhpClone.speeds()); + + rmed = awhpClone.remove(); + EXPECT_EQ(11, rmed.size()); + EXPECT_EQ(1, model.getConcreteModelObjects().size()); + EXPECT_EQ(0, model.getConcreteModelObjects().size()); +} diff --git a/src/model/test/HeatPumpAirToWaterHeating_GTest.cpp b/src/model/test/HeatPumpAirToWaterHeating_GTest.cpp index 6e2b6cb03f..18fc08683c 100644 --- a/src/model/test/HeatPumpAirToWaterHeating_GTest.cpp +++ b/src/model/test/HeatPumpAirToWaterHeating_GTest.cpp @@ -15,7 +15,6 @@ #include "../Schedule.hpp" #include "../ScheduleConstant.hpp" #include "../Curve.hpp" -#include "../Curve_Impl.hpp" #include "../CurveCubic.hpp" #include "../CurveBicubic.hpp" @@ -23,10 +22,15 @@ #include "../PlantLoop.hpp" #include "../Node.hpp" #include "../Node_Impl.hpp" +#include "../Splitter.hpp" #include "../ModelObjectList.hpp" #include "../ModelObjectList_Impl.hpp" +#include +#include +#include + using namespace openstudio; using namespace openstudio::model; @@ -138,6 +142,258 @@ TEST_F(ModelFixture, HeatPumpAirToWaterHeating_HeatCoolFuelTypes) { EXPECT_EQ(ComponentType(ComponentType::Heating), awhp.componentType()); testFuelTypeEquality({}, awhp.coolingFuelTypes()); - testFuelTypeEquality({FuelType::Electricity, FuelType::Propane}, awhp.heatingFuelTypes()); + 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()); + + 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, awhp.numberOfSpeeds()); + EXPECT_EQ(speeds, awhp.speeds()); + + EXPECT_EQ(5, awhpClone.numberOfSpeeds()); + EXPECT_EQ(speeds, awhpClone.speeds()); + + auto rmed = awhp.remove(); + + auto 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); + }; + + EXPECT_EQ(2, rmed.size()) << getObjectNames(rmed); + EXPECT_EQ(1, model.getConcreteModelObjects().size()); + EXPECT_EQ(5, model.getConcreteModelObjects().size()); + EXPECT_EQ(5, awhpClone.numberOfSpeeds()); + EXPECT_EQ(speeds, awhpClone.speeds()); + + rmed = awhpClone.remove(); + EXPECT_EQ(11, rmed.size()) << getObjectNames(rmed); + EXPECT_EQ(1, model.getConcreteModelObjects().size()); + EXPECT_EQ(0, model.getConcreteModelObjects().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 +} diff --git a/src/model/test/HeatPumpAirToWater_GTest.cpp b/src/model/test/HeatPumpAirToWater_GTest.cpp new file mode 100644 index 0000000000..ba2fc3d600 --- /dev/null +++ b/src/model/test/HeatPumpAirToWater_GTest.cpp @@ -0,0 +1,271 @@ +/*********************************************************************************************************************** +* 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 "../CurveCubic.hpp" +#include "../CurveBicubic.hpp" + +#include "../AirLoopHVAC.hpp" +#include "../Node.hpp" +#include "../PlantLoop.hpp" + +#include "../HeatPumpAirToWaterHeating.hpp" +#include "../HeatPumpAirToWaterHeatingSpeedData.hpp" + +#include "../HeatPumpAirToWaterCooling.hpp" +#include "../HeatPumpAirToWaterCoolingSpeedData.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_TRUE(heatPumpAirToWater.setOperatingModeControlMethod("ScheduledModes")); + EXPECT_EQ("ScheduledModes", heatPumpAirToWater.operatingModeControlMethod()); + // Bad Value + EXPECT_FALSE(heatPumpAirToWater.setOperatingModeControlMethod("BADENUM")); + EXPECT_EQ("ScheduledModes", 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()); + ScheduleConstant operatingModeControlSchedule(m); + EXPECT_TRUE(heatPumpAirToWater.setOperatingModeControlSchedule(operatingModeControlSchedule)); + ASSERT_TRUE(heatPumpAirToWater.operatingModeControlSchedule()); + EXPECT_EQ(operatingModeControlSchedule, heatPumpAirToWater.operatingModeControlSchedule().get()); + + // 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 + ASSERT_TRUE(heatPumpAirToWater.resistiveDefrostHeaterCapacity()); + EXPECT_EQ(0.0, heatPumpAirToWater.resistiveDefrostHeaterCapacity().get()); + // Autosize + heatPumpAirToWater.autosizeResistiveDefrostHeaterCapacity(); + EXPECT_TRUE(heatPumpAirToWater.isResistiveDefrostHeaterCapacityAutosized()); + // Set + EXPECT_TRUE(heatPumpAirToWater.setResistiveDefrostHeaterCapacity(1.2)); + ASSERT_TRUE(heatPumpAirToWater.resistiveDefrostHeaterCapacity()); + EXPECT_EQ(1.2, heatPumpAirToWater.resistiveDefrostHeaterCapacity().get()); + // Bad Value + EXPECT_FALSE(heatPumpAirToWater.setResistiveDefrostHeaterCapacity(-10.0)); + ASSERT_TRUE(heatPumpAirToWater.resistiveDefrostHeaterCapacity()); + EXPECT_EQ(1.2, heatPumpAirToWater.resistiveDefrostHeaterCapacity().get()); + EXPECT_FALSE(heatPumpAirToWater.isResistiveDefrostHeaterCapacityAutosized()); + + // 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()); +} From aef90f05c2bcbf07f52265f75aabfb20183864ef Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Thu, 9 Oct 2025 16:10:49 +0200 Subject: [PATCH 17/45] Make SpeedData be ResourceObject --- src/model/HeatPumpAirToWaterCooling.cpp | 9 +- .../HeatPumpAirToWaterCoolingSpeedData.cpp | 14 +-- .../HeatPumpAirToWaterCoolingSpeedData.hpp | 6 +- ...eatPumpAirToWaterCoolingSpeedData_Impl.hpp | 8 +- src/model/HeatPumpAirToWaterHeating.cpp | 9 +- .../HeatPumpAirToWaterHeatingSpeedData.cpp | 14 +-- .../HeatPumpAirToWaterHeatingSpeedData.hpp | 6 +- ...eatPumpAirToWaterHeatingSpeedData_Impl.hpp | 8 +- ...atPumpAirToWaterCoolingSpeedData_GTest.cpp | 29 ++++- .../test/HeatPumpAirToWaterCooling_GTest.cpp | 116 +++++++++++++++--- ...atPumpAirToWaterHeatingSpeedData_GTest.cpp | 29 ++++- .../test/HeatPumpAirToWaterHeating_GTest.cpp | 17 ++- 12 files changed, 211 insertions(+), 54 deletions(-) diff --git a/src/model/HeatPumpAirToWaterCooling.cpp b/src/model/HeatPumpAirToWaterCooling.cpp index 00586d0211..df2ddde564 100644 --- a/src/model/HeatPumpAirToWaterCooling.cpp +++ b/src/model/HeatPumpAirToWaterCooling.cpp @@ -139,10 +139,13 @@ namespace model { // std::vector allowableChildTypes() const override; std::vector HeatPumpAirToWaterCooling_Impl::remove() { - auto speedList = speedDataList(); + // 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()) { - openstudio::detail::concat_helper(result, speedList.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; diff --git a/src/model/HeatPumpAirToWaterCoolingSpeedData.cpp b/src/model/HeatPumpAirToWaterCoolingSpeedData.cpp index 1fac8b4c51..44cec8fee8 100644 --- a/src/model/HeatPumpAirToWaterCoolingSpeedData.cpp +++ b/src/model/HeatPumpAirToWaterCoolingSpeedData.cpp @@ -26,19 +26,19 @@ namespace model { namespace detail { HeatPumpAirToWaterCoolingSpeedData_Impl::HeatPumpAirToWaterCoolingSpeedData_Impl(const IdfObject& idfObject, Model_Impl* model, bool keepHandle) - : ParentObject_Impl(idfObject, model, 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) - : ParentObject_Impl(other, model, 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) - : ParentObject_Impl(other, model, keepHandle) {} + : ResourceObject_Impl(other, model, keepHandle) {} const std::vector& HeatPumpAirToWaterCoolingSpeedData_Impl::outputVariableNames() const { static std::vector result; @@ -179,7 +179,7 @@ namespace model { } // namespace detail HeatPumpAirToWaterCoolingSpeedData::HeatPumpAirToWaterCoolingSpeedData(const Model& model) - : ParentObject(HeatPumpAirToWaterCoolingSpeedData::iddObjectType(), model) { + : ResourceObject(HeatPumpAirToWaterCoolingSpeedData::iddObjectType(), model) { OS_ASSERT(getImpl()); autosizeRatedCoolingCapacity(); @@ -253,7 +253,7 @@ namespace model { const Curve& normalizedCoolingCapacityFunctionofTemperatureCurve, const Curve& coolingEnergyInputRatioFunctionofTemperatureCurve, const Curve& coolingEnergyInputRatioFunctionofPLRCurve) - : ParentObject(HeatPumpAirToWaterCoolingSpeedData::iddObjectType(), model) { + : ResourceObject(HeatPumpAirToWaterCoolingSpeedData::iddObjectType(), model) { auto impl = getImpl(); OS_ASSERT(impl); @@ -263,7 +263,7 @@ namespace model { bool ok = setNormalizedCoolingCapacityFunctionofTemperatureCurve(normalizedCoolingCapacityFunctionofTemperatureCurve); if (!ok) { - // We don't call remove() (which would do ParentObject::remove()) because that would try to remove the curves too if they aren't being used, + // 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 " @@ -341,7 +341,7 @@ namespace model { /// @cond HeatPumpAirToWaterCoolingSpeedData::HeatPumpAirToWaterCoolingSpeedData(std::shared_ptr impl) - : ParentObject(std::move(impl)) {} + : ResourceObject(std::move(impl)) {} /// @endcond void HeatPumpAirToWaterCoolingSpeedData::autosize() { diff --git a/src/model/HeatPumpAirToWaterCoolingSpeedData.hpp b/src/model/HeatPumpAirToWaterCoolingSpeedData.hpp index 47a7f812ff..b0114c10c9 100644 --- a/src/model/HeatPumpAirToWaterCoolingSpeedData.hpp +++ b/src/model/HeatPumpAirToWaterCoolingSpeedData.hpp @@ -7,7 +7,7 @@ #define MODEL_HEATPUMPAIRTOWATERCOOLINGSPEEDDATA_HPP #include "ModelAPI.hpp" -#include "ParentObject.hpp" +#include "ResourceObject.hpp" namespace openstudio { @@ -21,8 +21,8 @@ namespace model { } // namespace detail - /** HeatPumpAirToWaterCoolingSpeedData is a ParentObject that wraps the OpenStudio IDD object 'OS:HeatPump:AirToWater:Cooling:SpeedData'. */ - class MODEL_API HeatPumpAirToWaterCoolingSpeedData : public ParentObject + /** 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 */ diff --git a/src/model/HeatPumpAirToWaterCoolingSpeedData_Impl.hpp b/src/model/HeatPumpAirToWaterCoolingSpeedData_Impl.hpp index 8a3c59acea..581f0f61a3 100644 --- a/src/model/HeatPumpAirToWaterCoolingSpeedData_Impl.hpp +++ b/src/model/HeatPumpAirToWaterCoolingSpeedData_Impl.hpp @@ -7,7 +7,7 @@ #define MODEL_HEATPUMPAIRTOWATERCOOLINGSPEEDDATA_IMPL_HPP #include "ModelAPI.hpp" -#include "ParentObject_Impl.hpp" +#include "ResourceObject_Impl.hpp" namespace openstudio { namespace model { @@ -16,8 +16,8 @@ namespace model { namespace detail { - /** HeatPumpAirToWaterCoolingSpeedData_Impl is a ParentObject_Impl that is the implementation class for HeatPumpAirToWaterCoolingSpeedData.*/ - class MODEL_API HeatPumpAirToWaterCoolingSpeedData_Impl : public ParentObject_Impl + /** 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 */ @@ -40,7 +40,7 @@ namespace model { 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 ParentObject_Impl::clone would also clone them, so we override clone to call ModelObject_Impl::clone + // 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; diff --git a/src/model/HeatPumpAirToWaterHeating.cpp b/src/model/HeatPumpAirToWaterHeating.cpp index 702e29961e..d0ea433dae 100644 --- a/src/model/HeatPumpAirToWaterHeating.cpp +++ b/src/model/HeatPumpAirToWaterHeating.cpp @@ -139,10 +139,13 @@ namespace model { // std::vector allowableChildTypes() const override; std::vector HeatPumpAirToWaterHeating_Impl::remove() { - auto speedList = speedDataList(); + // 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()) { - openstudio::detail::concat_helper(result, speedList.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; diff --git a/src/model/HeatPumpAirToWaterHeatingSpeedData.cpp b/src/model/HeatPumpAirToWaterHeatingSpeedData.cpp index 451c15bea1..c2c9ee5327 100644 --- a/src/model/HeatPumpAirToWaterHeatingSpeedData.cpp +++ b/src/model/HeatPumpAirToWaterHeatingSpeedData.cpp @@ -26,19 +26,19 @@ namespace model { namespace detail { HeatPumpAirToWaterHeatingSpeedData_Impl::HeatPumpAirToWaterHeatingSpeedData_Impl(const IdfObject& idfObject, Model_Impl* model, bool keepHandle) - : ParentObject_Impl(idfObject, model, 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) - : ParentObject_Impl(other, model, 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) - : ParentObject_Impl(other, model, keepHandle) {} + : ResourceObject_Impl(other, model, keepHandle) {} const std::vector& HeatPumpAirToWaterHeatingSpeedData_Impl::outputVariableNames() const { static std::vector result; @@ -179,7 +179,7 @@ namespace model { } // namespace detail HeatPumpAirToWaterHeatingSpeedData::HeatPumpAirToWaterHeatingSpeedData(const Model& model) - : ParentObject(HeatPumpAirToWaterHeatingSpeedData::iddObjectType(), model) { + : ResourceObject(HeatPumpAirToWaterHeatingSpeedData::iddObjectType(), model) { OS_ASSERT(getImpl()); autosizeRatedHeatingCapacity(); @@ -253,7 +253,7 @@ namespace model { const Curve& normalizedHeatingCapacityFunctionofTemperatureCurve, const Curve& heatingEnergyInputRatioFunctionofTemperatureCurve, const Curve& heatingEnergyInputRatioFunctionofPLRCurve) - : ParentObject(HeatPumpAirToWaterHeatingSpeedData::iddObjectType(), model) { + : ResourceObject(HeatPumpAirToWaterHeatingSpeedData::iddObjectType(), model) { auto impl = getImpl(); OS_ASSERT(impl); @@ -263,7 +263,7 @@ namespace model { bool ok = setNormalizedHeatingCapacityFunctionofTemperatureCurve(normalizedHeatingCapacityFunctionofTemperatureCurve); if (!ok) { - // We don't call remove() (which would do ParentObject::remove()) because that would try to remove the curves too if they aren't being used, + // 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 " @@ -341,7 +341,7 @@ namespace model { /// @cond HeatPumpAirToWaterHeatingSpeedData::HeatPumpAirToWaterHeatingSpeedData(std::shared_ptr impl) - : ParentObject(std::move(impl)) {} + : ResourceObject(std::move(impl)) {} /// @endcond void HeatPumpAirToWaterHeatingSpeedData::autosize() { diff --git a/src/model/HeatPumpAirToWaterHeatingSpeedData.hpp b/src/model/HeatPumpAirToWaterHeatingSpeedData.hpp index c4a7744e88..761ca320a9 100644 --- a/src/model/HeatPumpAirToWaterHeatingSpeedData.hpp +++ b/src/model/HeatPumpAirToWaterHeatingSpeedData.hpp @@ -7,7 +7,7 @@ #define MODEL_HEATPUMPAIRTOWATERHEATINGSPEEDDATA_HPP #include "ModelAPI.hpp" -#include "ParentObject.hpp" +#include "ResourceObject.hpp" namespace openstudio { @@ -21,8 +21,8 @@ namespace model { } // namespace detail - /** HeatPumpAirToWaterHeatingSpeedData is a ParentObject that wraps the OpenStudio IDD object 'OS:HeatPump:AirToWater:Heating:SpeedData'. */ - class MODEL_API HeatPumpAirToWaterHeatingSpeedData : public ParentObject + /** 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 */ diff --git a/src/model/HeatPumpAirToWaterHeatingSpeedData_Impl.hpp b/src/model/HeatPumpAirToWaterHeatingSpeedData_Impl.hpp index 9b1d29eba1..bc220b89f0 100644 --- a/src/model/HeatPumpAirToWaterHeatingSpeedData_Impl.hpp +++ b/src/model/HeatPumpAirToWaterHeatingSpeedData_Impl.hpp @@ -7,7 +7,7 @@ #define MODEL_HEATPUMPAIRTOWATERHEATINGSPEEDDATA_IMPL_HPP #include "ModelAPI.hpp" -#include "ParentObject_Impl.hpp" +#include "ResourceObject_Impl.hpp" namespace openstudio { namespace model { @@ -16,8 +16,8 @@ namespace model { namespace detail { - /** HeatPumpAirToWaterHeatingSpeedData_Impl is a ParentObject_Impl that is the implementation class for HeatPumpAirToWaterHeatingSpeedData.*/ - class MODEL_API HeatPumpAirToWaterHeatingSpeedData_Impl : public ParentObject_Impl + /** 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 */ @@ -40,7 +40,7 @@ namespace model { 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 ParentObject_Impl::clone would also clone them, so we override clone to call ModelObject_Impl::clone + // 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; diff --git a/src/model/test/HeatPumpAirToWaterCoolingSpeedData_GTest.cpp b/src/model/test/HeatPumpAirToWaterCoolingSpeedData_GTest.cpp index b7bb98bdba..5a05e3afa4 100644 --- a/src/model/test/HeatPumpAirToWaterCoolingSpeedData_GTest.cpp +++ b/src/model/test/HeatPumpAirToWaterCoolingSpeedData_GTest.cpp @@ -8,6 +8,8 @@ #include "../HeatPumpAirToWaterCoolingSpeedData.hpp" #include "../HeatPumpAirToWaterCoolingSpeedData_Impl.hpp" +#include "../HeatPumpAirToWaterCooling.hpp" + #include "../Model.hpp" #include "../Curve.hpp" #include "../Curve_Impl.hpp" @@ -182,6 +184,29 @@ TEST_F(ModelFixture, HeatPumpAirToWaterCoolingSpeedData_clone_remove) { } TEST_F(ModelFixture, HeatPumpAirToWaterCoolingSpeedData_RemoveParentModelObjectList) { - GTEST_SKIP() << "Need to implement ModelObjectList support in HeatPumpAirToWater and check that removing a speed data is either prevented or " - "cleans up the ModelObjectList properly."; + Model m; + + HeatPumpAirToWaterCooling awhp(m); + HeatPumpAirToWaterCooling awhp2(m); + + HeatPumpAirToWaterCoolingSpeedData speed1(m); + HeatPumpAirToWaterCoolingSpeedData speed2(m); + + EXPECT_TRUE(awhp.addSpeed(speed1)); + EXPECT_TRUE(awhp2.addSpeed(speed1)); + EXPECT_TRUE(awhp.addSpeed(speed2)); + EXPECT_TRUE(awhp2.addSpeed(speed2)); + + { + 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()); + } } diff --git a/src/model/test/HeatPumpAirToWaterCooling_GTest.cpp b/src/model/test/HeatPumpAirToWaterCooling_GTest.cpp index ee89573329..6e6f371c66 100644 --- a/src/model/test/HeatPumpAirToWaterCooling_GTest.cpp +++ b/src/model/test/HeatPumpAirToWaterCooling_GTest.cpp @@ -23,10 +23,16 @@ #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; @@ -288,11 +294,13 @@ TEST_F(ModelFixture, HeatPumpAirToWaterCooling_clone) { 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()); @@ -300,24 +308,104 @@ TEST_F(ModelFixture, HeatPumpAirToWaterCooling_clone) { EXPECT_EQ(5, awhpClone.numberOfSpeeds()); EXPECT_EQ(speeds, awhpClone.speeds()); - // This shouldn't work, it's going to put the CoilCoolingDXCurveFitPerformance in a broken state auto rmed = awhp.remove(); - EXPECT_EQ(0, rmed.size()); - HeatPumpAirToWaterCoolingSpeedData boosterModeOnSpeed(model); - EXPECT_TRUE(awhp.setBoosterModeOnSpeed(boosterModeOnSpeed)); - ASSERT_TRUE(awhp.boosterModeOnSpeed()); - EXPECT_EQ(3, model.getConcreteModelObjects().size()); - EXPECT_EQ(10, model.getConcreteModelObjects().size()); - rmed = awhp.remove(); - EXPECT_EQ(1, rmed.size()); - EXPECT_EQ(2, model.getConcreteModelObjects().size()); - EXPECT_EQ(10, model.getConcreteModelObjects().size()); - EXPECT_EQ(10, awhpClone.numberOfSpeeds()); + auto 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); + }; + + 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(); - EXPECT_EQ(11, rmed.size()); - EXPECT_EQ(1, model.getConcreteModelObjects().size()); + + 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 } diff --git a/src/model/test/HeatPumpAirToWaterHeatingSpeedData_GTest.cpp b/src/model/test/HeatPumpAirToWaterHeatingSpeedData_GTest.cpp index 090dd4b3fd..5de99d57f2 100644 --- a/src/model/test/HeatPumpAirToWaterHeatingSpeedData_GTest.cpp +++ b/src/model/test/HeatPumpAirToWaterHeatingSpeedData_GTest.cpp @@ -8,6 +8,8 @@ #include "../HeatPumpAirToWaterHeatingSpeedData.hpp" #include "../HeatPumpAirToWaterHeatingSpeedData_Impl.hpp" +#include "../HeatPumpAirToWaterHeating.hpp" + #include "../Model.hpp" #include "../Curve.hpp" #include "../Curve_Impl.hpp" @@ -182,6 +184,29 @@ TEST_F(ModelFixture, HeatPumpAirToWaterHeatingSpeedData_clone_remove) { } TEST_F(ModelFixture, HeatPumpAirToWaterHeatingSpeedData_RemoveParentModelObjectList) { - GTEST_SKIP() << "Need to implement ModelObjectList support in HeatPumpAirToWater and check that removing a speed data is either prevented or " - "cleans up the ModelObjectList properly."; + Model m; + + HeatPumpAirToWaterHeating awhp(m); + HeatPumpAirToWaterHeating awhp2(m); + + HeatPumpAirToWaterHeatingSpeedData speed1(m); + HeatPumpAirToWaterHeatingSpeedData speed2(m); + + EXPECT_TRUE(awhp.addSpeed(speed1)); + EXPECT_TRUE(awhp2.addSpeed(speed1)); + EXPECT_TRUE(awhp.addSpeed(speed2)); + EXPECT_TRUE(awhp2.addSpeed(speed2)); + + { + 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()); + } } diff --git a/src/model/test/HeatPumpAirToWaterHeating_GTest.cpp b/src/model/test/HeatPumpAirToWaterHeating_GTest.cpp index 18fc08683c..698974a359 100644 --- a/src/model/test/HeatPumpAirToWaterHeating_GTest.cpp +++ b/src/model/test/HeatPumpAirToWaterHeating_GTest.cpp @@ -15,6 +15,7 @@ #include "../Schedule.hpp" #include "../ScheduleConstant.hpp" #include "../Curve.hpp" +#include "../Curve_Impl.hpp" #include "../CurveCubic.hpp" #include "../CurveBicubic.hpp" @@ -27,6 +28,8 @@ #include "../ModelObjectList.hpp" #include "../ModelObjectList_Impl.hpp" +#include + #include #include #include @@ -292,11 +295,13 @@ TEST_F(ModelFixture, HeatPumpAirToWaterHeating_clone) { 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()); @@ -314,15 +319,23 @@ TEST_F(ModelFixture, HeatPumpAirToWaterHeating_clone) { }; 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(); - EXPECT_EQ(11, rmed.size()) << getObjectNames(rmed); - EXPECT_EQ(1, model.getConcreteModelObjects().size()); + + 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) { From 303af7c71364e3c9151e1320eba32a635499d12e Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Thu, 9 Oct 2025 17:16:20 +0200 Subject: [PATCH 18/45] Implement containingHVACComponent + convenience methods to go from child to parent(s) --- src/model/HeatPumpAirToWaterCooling.cpp | 19 +++++- src/model/HeatPumpAirToWaterCooling.hpp | 50 +++++++------- .../HeatPumpAirToWaterCoolingSpeedData.cpp | 29 ++++++++- .../HeatPumpAirToWaterCoolingSpeedData.hpp | 5 +- ...eatPumpAirToWaterCoolingSpeedData_Impl.hpp | 5 +- src/model/HeatPumpAirToWaterCooling_Impl.hpp | 10 ++- src/model/HeatPumpAirToWaterHeating.cpp | 19 +++++- src/model/HeatPumpAirToWaterHeating.hpp | 6 +- .../HeatPumpAirToWaterHeatingSpeedData.cpp | 27 ++++++++ .../HeatPumpAirToWaterHeatingSpeedData.hpp | 5 +- ...eatPumpAirToWaterHeatingSpeedData_Impl.hpp | 5 +- src/model/HeatPumpAirToWaterHeating_Impl.hpp | 10 ++- ...atPumpAirToWaterCoolingSpeedData_GTest.cpp | 18 +++++ .../test/HeatPumpAirToWaterCooling_GTest.cpp | 65 +++++++++++++++++++ ...atPumpAirToWaterHeatingSpeedData_GTest.cpp | 18 +++++ .../test/HeatPumpAirToWaterHeating_GTest.cpp | 65 +++++++++++++++++++ 16 files changed, 315 insertions(+), 41 deletions(-) diff --git a/src/model/HeatPumpAirToWaterCooling.cpp b/src/model/HeatPumpAirToWaterCooling.cpp index df2ddde564..3b8bc88416 100644 --- a/src/model/HeatPumpAirToWaterCooling.cpp +++ b/src/model/HeatPumpAirToWaterCooling.cpp @@ -6,6 +6,8 @@ #include "HeatPumpAirToWaterCooling.hpp" #include "HeatPumpAirToWaterCooling_Impl.hpp" +#include "HeatPumpAirToWater.hpp" +#include "HeatPumpAirToWater_Impl.hpp" #include "HeatPumpAirToWaterCoolingSpeedData.hpp" #include "HeatPumpAirToWaterCoolingSpeedData_Impl.hpp" @@ -93,10 +95,21 @@ namespace model { } boost::optional HeatPumpAirToWaterCooling_Impl::containingHVACComponent() const { - // TODO: implement HeatPumpAirToWaterCooling_Impl::containingHVACComponent" + auto awhps = getObject().getModelObjectSources(HeatPumpAirToWater::iddObjectType()); + auto count = awhps.size(); + if (count == 1) { + return awhps[0]; + } else if (count > 1) { + LOG(Warn, briefDescription() << " is referenced by more than one CoilCoolingElectricMultiStage, returning the first"); + return awhps[0]; + } return boost::none; } + std::vector HeatPumpAirToWaterCooling_Impl::heatPumpAirToWaters() const { + return getObject().getModelObjectSources(HeatPumpAirToWater::iddObjectType()); + } + ModelObject HeatPumpAirToWaterCooling_Impl::clone(Model model) const { // This handles resetting the ports, and bypassing ParentObject::clone so it doesn't clone children auto t_clone = StraightComponent_Impl::clone(model).cast(); @@ -688,6 +701,10 @@ namespace model { return getImpl()->removeSpeed(index); } + std::vector HeatPumpAirToWaterCooling::heatPumpAirToWaters() const { + return getImpl()->heatPumpAirToWaters(); + } + /// @cond HeatPumpAirToWaterCooling::HeatPumpAirToWaterCooling(std::shared_ptr impl) : StraightComponent(std::move(impl)) {} diff --git a/src/model/HeatPumpAirToWaterCooling.hpp b/src/model/HeatPumpAirToWaterCooling.hpp index 5d7ceb7097..34cb4827e9 100644 --- a/src/model/HeatPumpAirToWaterCooling.hpp +++ b/src/model/HeatPumpAirToWaterCooling.hpp @@ -13,9 +13,10 @@ namespace openstudio { namespace model { - class Schedule; class Curve; + class HeatPumpAirToWater; class HeatPumpAirToWaterCoolingSpeedData; + class Schedule; namespace detail { @@ -120,51 +121,54 @@ namespace model { unsigned numberOfSpeeds() const; /* - * Get the index of a given HeatPumpAirToWaterCoolingSpeedData (1-indexed) - */ + * 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. - */ + * 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 - */ + * 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 - */ + * 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) - */ + * 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 - */ + * Removes all HeatPumpAirToWaterCoolingSpeedDatas in this object + */ void removeAllSpeeds(); /* - * Remove the given HeatPumpAirToWaterCoolingSpeedData from this object's speeds - */ + * Remove the given HeatPumpAirToWaterCoolingSpeedData from this object's speeds + */ bool removeSpeed(const HeatPumpAirToWaterCoolingSpeedData& speed); /* - * Remove the HeatPumpAirToWaterCoolingSpeedData at the given index (1-indexed) - */ + * Remove the HeatPumpAirToWaterCoolingSpeedData at the given index (1-indexed) + */ bool removeSpeed(unsigned index); + // Convenience function to return all HeatPumpAirToWater objects that reference this cooling coil + std::vector heatPumpAirToWaters() const; + // Autosize methods boost::optional autosizedRatedAirFlowRate() const; boost::optional autosizedRatedWaterFlowRate() const; diff --git a/src/model/HeatPumpAirToWaterCoolingSpeedData.cpp b/src/model/HeatPumpAirToWaterCoolingSpeedData.cpp index 44cec8fee8..14b0b0b875 100644 --- a/src/model/HeatPumpAirToWaterCoolingSpeedData.cpp +++ b/src/model/HeatPumpAirToWaterCoolingSpeedData.cpp @@ -6,6 +6,9 @@ #include "HeatPumpAirToWaterCoolingSpeedData.hpp" #include "HeatPumpAirToWaterCoolingSpeedData_Impl.hpp" +#include "HeatPumpAirToWaterCooling.hpp" +#include "HeatPumpAirToWaterCooling_Impl.hpp" + #include "Model.hpp" #include "Model_Impl.hpp" #include "Curve.hpp" @@ -176,6 +179,26 @@ namespace model { 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) @@ -345,7 +368,7 @@ namespace model { /// @endcond void HeatPumpAirToWaterCoolingSpeedData::autosize() { - getImpl()->autosizeRatedCoolingCapacity(); + getImpl()->autosize(); } // TODO: needed? @@ -353,5 +376,9 @@ namespace model { // 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 index b0114c10c9..0060fdd562 100644 --- a/src/model/HeatPumpAirToWaterCoolingSpeedData.hpp +++ b/src/model/HeatPumpAirToWaterCoolingSpeedData.hpp @@ -14,6 +14,7 @@ namespace openstudio { namespace model { class Curve; + class HeatPumpAirToWaterCooling; namespace detail { @@ -80,8 +81,8 @@ namespace model { /** @name Other */ //@{ - // returns a vector of HeatPumpAirToWater that use this as their Speed Data - // TODO: std::vector heatPumpsAirToWater() const; + // 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(); diff --git a/src/model/HeatPumpAirToWaterCoolingSpeedData_Impl.hpp b/src/model/HeatPumpAirToWaterCoolingSpeedData_Impl.hpp index 581f0f61a3..c056797123 100644 --- a/src/model/HeatPumpAirToWaterCoolingSpeedData_Impl.hpp +++ b/src/model/HeatPumpAirToWaterCoolingSpeedData_Impl.hpp @@ -13,6 +13,7 @@ namespace openstudio { namespace model { class Curve; + class HeatPumpAirToWaterCooling; namespace detail { @@ -83,8 +84,8 @@ namespace model { /** @name Other */ //@{ - // returns a vector of HeatPumpAirToWater that use this as their Speed Data - // TODO: std::vector heatPumpsAirToWater() const; + // 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(); diff --git a/src/model/HeatPumpAirToWaterCooling_Impl.hpp b/src/model/HeatPumpAirToWaterCooling_Impl.hpp index 662bf00d88..d4734ac22a 100644 --- a/src/model/HeatPumpAirToWaterCooling_Impl.hpp +++ b/src/model/HeatPumpAirToWaterCooling_Impl.hpp @@ -12,11 +12,12 @@ namespace openstudio { namespace model { - class Schedule; class Curve; - class Node; - class ModelObjectList; + class HeatPumpAirToWater; class HeatPumpAirToWaterCoolingSpeedData; + class ModelObjectList; + class Node; + class Schedule; namespace detail { @@ -157,6 +158,9 @@ namespace model { bool removeSpeed(unsigned index); void removeAllSpeeds(); + // Convenience function to return all HeatPumpAirToWater objects that reference this cooling coil + std::vector heatPumpAirToWaters() const; + // Autosize methods boost::optional autosizedRatedAirFlowRate() const; diff --git a/src/model/HeatPumpAirToWaterHeating.cpp b/src/model/HeatPumpAirToWaterHeating.cpp index d0ea433dae..5daa866dfa 100644 --- a/src/model/HeatPumpAirToWaterHeating.cpp +++ b/src/model/HeatPumpAirToWaterHeating.cpp @@ -6,6 +6,8 @@ #include "HeatPumpAirToWaterHeating.hpp" #include "HeatPumpAirToWaterHeating_Impl.hpp" +#include "HeatPumpAirToWater.hpp" +#include "HeatPumpAirToWater_Impl.hpp" #include "HeatPumpAirToWaterHeatingSpeedData.hpp" #include "HeatPumpAirToWaterHeatingSpeedData_Impl.hpp" @@ -93,10 +95,21 @@ namespace model { } boost::optional HeatPumpAirToWaterHeating_Impl::containingHVACComponent() const { - // TODO: implement HeatPumpAirToWaterHeating_Impl::containingHVACComponent" + auto awhps = getObject().getModelObjectSources(HeatPumpAirToWater::iddObjectType()); + auto count = awhps.size(); + if (count == 1) { + return awhps[0]; + } else if (count > 1) { + LOG(Warn, briefDescription() << " is referenced by more than one CoilHeatingElectricMultiStage, returning the first"); + return awhps[0]; + } return boost::none; } + std::vector HeatPumpAirToWaterHeating_Impl::heatPumpAirToWaters() const { + return getObject().getModelObjectSources(HeatPumpAirToWater::iddObjectType()); + } + ModelObject HeatPumpAirToWaterHeating_Impl::clone(Model model) const { // This handles resetting the ports, and bypassing ParentObject::clone so it doesn't clone children auto t_clone = StraightComponent_Impl::clone(model).cast(); @@ -688,6 +701,10 @@ namespace model { return getImpl()->removeSpeed(index); } + std::vector HeatPumpAirToWaterHeating::heatPumpAirToWaters() const { + return getImpl()->heatPumpAirToWaters(); + } + /// @cond HeatPumpAirToWaterHeating::HeatPumpAirToWaterHeating(std::shared_ptr impl) : StraightComponent(std::move(impl)) {} diff --git a/src/model/HeatPumpAirToWaterHeating.hpp b/src/model/HeatPumpAirToWaterHeating.hpp index 7011b8efaf..7c5eca92bd 100644 --- a/src/model/HeatPumpAirToWaterHeating.hpp +++ b/src/model/HeatPumpAirToWaterHeating.hpp @@ -13,9 +13,10 @@ namespace openstudio { namespace model { - class Schedule; class Curve; + class HeatPumpAirToWater; class HeatPumpAirToWaterHeatingSpeedData; + class Schedule; namespace detail { @@ -165,6 +166,9 @@ namespace model { */ bool removeSpeed(unsigned index); + // Convenience function to return all HeatPumpAirToWater objects that reference this heating coil + std::vector heatPumpAirToWaters() const; + // Autosize methods boost::optional autosizedRatedAirFlowRate() const; boost::optional autosizedRatedWaterFlowRate() const; diff --git a/src/model/HeatPumpAirToWaterHeatingSpeedData.cpp b/src/model/HeatPumpAirToWaterHeatingSpeedData.cpp index c2c9ee5327..2850a014ae 100644 --- a/src/model/HeatPumpAirToWaterHeatingSpeedData.cpp +++ b/src/model/HeatPumpAirToWaterHeatingSpeedData.cpp @@ -6,6 +6,9 @@ #include "HeatPumpAirToWaterHeatingSpeedData.hpp" #include "HeatPumpAirToWaterHeatingSpeedData_Impl.hpp" +#include "HeatPumpAirToWaterHeating.hpp" +#include "HeatPumpAirToWaterHeating_Impl.hpp" + #include "Model.hpp" #include "Model_Impl.hpp" #include "Curve.hpp" @@ -176,6 +179,26 @@ namespace model { 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) @@ -353,5 +376,9 @@ namespace model { // 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 index 761ca320a9..86ede3171f 100644 --- a/src/model/HeatPumpAirToWaterHeatingSpeedData.hpp +++ b/src/model/HeatPumpAirToWaterHeatingSpeedData.hpp @@ -14,6 +14,7 @@ namespace openstudio { namespace model { class Curve; + class HeatPumpAirToWaterHeating; namespace detail { @@ -80,8 +81,8 @@ namespace model { /** @name Other */ //@{ - // returns a vector of HeatPumpAirToWater that use this as their Speed Data - // TODO: std::vector heatPumpsAirToWater() const; + // 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(); diff --git a/src/model/HeatPumpAirToWaterHeatingSpeedData_Impl.hpp b/src/model/HeatPumpAirToWaterHeatingSpeedData_Impl.hpp index bc220b89f0..2999b2497b 100644 --- a/src/model/HeatPumpAirToWaterHeatingSpeedData_Impl.hpp +++ b/src/model/HeatPumpAirToWaterHeatingSpeedData_Impl.hpp @@ -13,6 +13,7 @@ namespace openstudio { namespace model { class Curve; + class HeatPumpAirToWaterHeating; namespace detail { @@ -83,8 +84,8 @@ namespace model { /** @name Other */ //@{ - // returns a vector of HeatPumpAirToWater that use this as their Speed Data - // TODO: std::vector heatPumpsAirToWater() const; + // 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(); diff --git a/src/model/HeatPumpAirToWaterHeating_Impl.hpp b/src/model/HeatPumpAirToWaterHeating_Impl.hpp index 882a4c7293..8e62a5201a 100644 --- a/src/model/HeatPumpAirToWaterHeating_Impl.hpp +++ b/src/model/HeatPumpAirToWaterHeating_Impl.hpp @@ -12,11 +12,12 @@ namespace openstudio { namespace model { - class Schedule; class Curve; - class Node; - class ModelObjectList; + class HeatPumpAirToWater; class HeatPumpAirToWaterHeatingSpeedData; + class ModelObjectList; + class Node; + class Schedule; namespace detail { @@ -157,6 +158,9 @@ namespace model { bool removeSpeed(unsigned index); void removeAllSpeeds(); + // Convenience function to return all HeatPumpAirToWater objects that reference this heating coil + std::vector heatPumpAirToWaters() const; + // Autosize methods boost::optional autosizedRatedAirFlowRate() const; diff --git a/src/model/test/HeatPumpAirToWaterCoolingSpeedData_GTest.cpp b/src/model/test/HeatPumpAirToWaterCoolingSpeedData_GTest.cpp index 5a05e3afa4..e2c607795e 100644 --- a/src/model/test/HeatPumpAirToWaterCoolingSpeedData_GTest.cpp +++ b/src/model/test/HeatPumpAirToWaterCoolingSpeedData_GTest.cpp @@ -192,10 +192,25 @@ TEST_F(ModelFixture, HeatPumpAirToWaterCoolingSpeedData_RemoveParentModelObjectL 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}; @@ -209,4 +224,7 @@ TEST_F(ModelFixture, HeatPumpAirToWaterCoolingSpeedData_RemoveParentModelObjectL 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 index 6e6f371c66..8e324347e7 100644 --- a/src/model/test/HeatPumpAirToWaterCooling_GTest.cpp +++ b/src/model/test/HeatPumpAirToWaterCooling_GTest.cpp @@ -8,6 +8,8 @@ #include "../HeatPumpAirToWaterCooling.hpp" #include "../HeatPumpAirToWaterCooling_Impl.hpp" +#include "../HeatPumpAirToWater.hpp" +#include "../HeatPumpAirToWater_Impl.hpp" #include "../HeatPumpAirToWaterCoolingSpeedData.hpp" #include "../HeatPumpAirToWaterCoolingSpeedData_Impl.hpp" @@ -409,3 +411,66 @@ TEST_F(ModelFixture, HeatPumpAirToWaterCooling_addToNode) { 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); + EXPECT_FALSE(awhp_cc.containingHVACComponent()); + EXPECT_EQ(0, awhp_cc.heatPumpAirToWaters().size()); + + EXPECT_TRUE(awhp.setCoolingOperationMode(awhp_cc)); + ASSERT_TRUE(awhp_cc.containingHVACComponent()); + EXPECT_EQ(awhp, awhp_cc.containingHVACComponent().get()); + EXPECT_EQ(1, awhp_cc.heatPumpAirToWaters().size()); + + EXPECT_TRUE(awhp2.setCoolingOperationMode(awhp_cc)); + ASSERT_TRUE(awhp_cc.containingHVACComponent()); + EXPECT_EQ(2, awhp_cc.heatPumpAirToWaters().size()); + + // 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(0, awhp_cc.remove().size()); + EXPECT_EQ(2, m.getConcreteModelObjects().size()); + EXPECT_EQ(1, m.getConcreteModelObjects().size()); + EXPECT_EQ(1, m.getConcreteModelObjects().size()); + + // Remove the first awhp + EXPECT_EQ(1, awhp.remove().size()); + EXPECT_EQ(1, awhp_cc.heatPumpAirToWaters().size()); + ASSERT_TRUE(awhp_cc.containingHVACComponent()); + EXPECT_EQ(awhp2, awhp_cc.containingHVACComponent().get()); + EXPECT_EQ(1, m.getConcreteModelObjects().size()); + EXPECT_EQ(1, m.getConcreteModelObjects().size()); + EXPECT_EQ(1, m.getConcreteModelObjects().size()); + + // Still not removable + EXPECT_FALSE(awhp_cc.isRemovable()); + EXPECT_EQ(0, awhp_cc.remove().size()); + EXPECT_EQ(1, m.getConcreteModelObjects().size()); + EXPECT_EQ(1, m.getConcreteModelObjects().size()); + EXPECT_EQ(1, m.getConcreteModelObjects().size()); + + // Remove the second awhp + EXPECT_EQ(1, awhp2.remove().size()); + EXPECT_EQ(0, m.getConcreteModelObjects().size()); + EXPECT_EQ(1, m.getConcreteModelObjects().size()); + EXPECT_EQ(1, m.getConcreteModelObjects().size()); + + EXPECT_EQ(0, awhp_cc.heatPumpAirToWaters().size()); + EXPECT_FALSE(awhp_cc.containingHVACComponent()); + EXPECT_TRUE(awhp_cc.isRemovable()); + EXPECT_EQ(2, awhp_cc.remove().size()); + EXPECT_EQ(0, m.getConcreteModelObjects().size()); + EXPECT_EQ(0, m.getConcreteModelObjects().size()); + EXPECT_EQ(0, m.getConcreteModelObjects().size()); +} diff --git a/src/model/test/HeatPumpAirToWaterHeatingSpeedData_GTest.cpp b/src/model/test/HeatPumpAirToWaterHeatingSpeedData_GTest.cpp index 5de99d57f2..ed9f529e1d 100644 --- a/src/model/test/HeatPumpAirToWaterHeatingSpeedData_GTest.cpp +++ b/src/model/test/HeatPumpAirToWaterHeatingSpeedData_GTest.cpp @@ -192,10 +192,25 @@ TEST_F(ModelFixture, HeatPumpAirToWaterHeatingSpeedData_RemoveParentModelObjectL 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}; @@ -209,4 +224,7 @@ TEST_F(ModelFixture, HeatPumpAirToWaterHeatingSpeedData_RemoveParentModelObjectL 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 index 698974a359..9b4495e41e 100644 --- a/src/model/test/HeatPumpAirToWaterHeating_GTest.cpp +++ b/src/model/test/HeatPumpAirToWaterHeating_GTest.cpp @@ -8,6 +8,8 @@ #include "../HeatPumpAirToWaterHeating.hpp" #include "../HeatPumpAirToWaterHeating_Impl.hpp" +#include "../HeatPumpAirToWater.hpp" +#include "../HeatPumpAirToWater_Impl.hpp" #include "../HeatPumpAirToWaterHeatingSpeedData.hpp" #include "../HeatPumpAirToWaterHeatingSpeedData_Impl.hpp" @@ -410,3 +412,66 @@ TEST_F(ModelFixture, HeatPumpAirToWaterHeating_addToNode) { 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); + EXPECT_FALSE(awhp_hc.containingHVACComponent()); + EXPECT_EQ(0, awhp_hc.heatPumpAirToWaters().size()); + + EXPECT_TRUE(awhp.setHeatingOperationMode(awhp_hc)); + ASSERT_TRUE(awhp_hc.containingHVACComponent()); + EXPECT_EQ(awhp, awhp_hc.containingHVACComponent().get()); + EXPECT_EQ(1, awhp_hc.heatPumpAirToWaters().size()); + + EXPECT_TRUE(awhp2.setHeatingOperationMode(awhp_hc)); + ASSERT_TRUE(awhp_hc.containingHVACComponent()); + EXPECT_EQ(2, awhp_hc.heatPumpAirToWaters().size()); + + // 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(0, awhp_hc.remove().size()); + EXPECT_EQ(2, m.getConcreteModelObjects().size()); + EXPECT_EQ(1, m.getConcreteModelObjects().size()); + EXPECT_EQ(1, m.getConcreteModelObjects().size()); + + // Remove the first awhp + EXPECT_EQ(1, awhp.remove().size()); + EXPECT_EQ(1, awhp_hc.heatPumpAirToWaters().size()); + ASSERT_TRUE(awhp_hc.containingHVACComponent()); + EXPECT_EQ(awhp2, awhp_hc.containingHVACComponent().get()); + EXPECT_EQ(1, m.getConcreteModelObjects().size()); + EXPECT_EQ(1, m.getConcreteModelObjects().size()); + EXPECT_EQ(1, m.getConcreteModelObjects().size()); + + // Still not removable + EXPECT_FALSE(awhp_hc.isRemovable()); + EXPECT_EQ(0, awhp_hc.remove().size()); + EXPECT_EQ(1, m.getConcreteModelObjects().size()); + EXPECT_EQ(1, m.getConcreteModelObjects().size()); + EXPECT_EQ(1, m.getConcreteModelObjects().size()); + + // Remove the second awhp + EXPECT_EQ(1, awhp2.remove().size()); + EXPECT_EQ(0, m.getConcreteModelObjects().size()); + EXPECT_EQ(1, m.getConcreteModelObjects().size()); + EXPECT_EQ(1, m.getConcreteModelObjects().size()); + + EXPECT_EQ(0, awhp_hc.heatPumpAirToWaters().size()); + EXPECT_FALSE(awhp_hc.containingHVACComponent()); + EXPECT_TRUE(awhp_hc.isRemovable()); + EXPECT_EQ(2, awhp_hc.remove().size()); + EXPECT_EQ(0, m.getConcreteModelObjects().size()); + EXPECT_EQ(0, m.getConcreteModelObjects().size()); + EXPECT_EQ(0, m.getConcreteModelObjects().size()); +} From 6df9e34f13e7a2fb2e7337cc8174c646a643efe9 Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Thu, 9 Oct 2025 18:54:43 +0200 Subject: [PATCH 19/45] Forgot to swig HeatPumpAirToWaterCooling --- src/model/ModelHVAC.i | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/model/ModelHVAC.i b/src/model/ModelHVAC.i index 27dc2b719b..c8145dd8f2 100644 --- a/src/model/ModelHVAC.i +++ b/src/model/ModelHVAC.i @@ -225,6 +225,7 @@ MODELOBJECT_TEMPLATES(HeatPumpWaterToWaterEquationFitHeating); MODELOBJECT_TEMPLATES(HeatPumpPlantLoopEIRCooling); MODELOBJECT_TEMPLATES(HeatPumpPlantLoopEIRHeating); MODELOBJECT_TEMPLATES(HeatPumpAirToWater) +MODELOBJECT_TEMPLATES(HeatPumpAirToWaterCooling); MODELOBJECT_TEMPLATES(HeatPumpAirToWaterCoolingSpeedData); MODELOBJECT_TEMPLATES(HeatPumpAirToWaterHeating); MODELOBJECT_TEMPLATES(HeatPumpAirToWaterHeatingSpeedData); @@ -359,6 +360,7 @@ 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); From 98a8c95a28230a54a4b8f7df457a212efa40d7c8 Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Thu, 9 Oct 2025 19:24:35 +0200 Subject: [PATCH 20/45] More cloning tweaks: when cloning the top level AWHP, we clone the HeatPump:AirToWater:Cooling/Heating. The autosized values are reported at the top level --- src/model/HeatPumpAirToWater.cpp | 62 +++++++++++++++++++ src/model/HeatPumpAirToWaterCooling.cpp | 8 +-- src/model/HeatPumpAirToWaterHeating.cpp | 5 -- src/model/HeatPumpAirToWater_Impl.hpp | 12 ++-- .../test/HeatPumpAirToWaterCooling_GTest.cpp | 60 ++++++++++++------ .../test/HeatPumpAirToWaterHeating_GTest.cpp | 59 ++++++++++++------ src/model/test/HeatPumpAirToWater_GTest.cpp | 52 ++++++++++++++++ 7 files changed, 200 insertions(+), 58 deletions(-) diff --git a/src/model/HeatPumpAirToWater.cpp b/src/model/HeatPumpAirToWater.cpp index 2747282fb4..275fc9bf9b 100644 --- a/src/model/HeatPumpAirToWater.cpp +++ b/src/model/HeatPumpAirToWater.cpp @@ -6,6 +6,7 @@ #include "HeatPumpAirToWater.hpp" #include "HeatPumpAirToWater_Impl.hpp" +#include "Model.hpp" #include "Curve.hpp" #include "Curve_Impl.hpp" #include "HeatPumpAirToWaterHeating.hpp" @@ -19,6 +20,7 @@ #include "ScheduleTypeRegistry.hpp" #include "../utilities/core/Assert.hpp" +#include "../utilities/core/ContainersMove.hpp" #include "../utilities/data/DataEnums.hpp" #include @@ -132,6 +134,66 @@ namespace model { 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 + 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; + + // Don't delete the underlying HeatPumpAirToWaterCooling / Heating objects if used by several HeatPumpAirToWater + if (auto coolingOpMode_ = coolingOperationMode()) { + if (coolingOpMode_->heatPumpAirToWaters().size() > 1) { + resetCoolingOperationMode(); + } else { + // If we are the only one using this cooling operation mode, remove it (it will remove the children too) + resetCoolingOperationMode(); // Need to reset so it's marked as removable first + result = coolingOpMode_->remove(); + } + } + if (auto heatingOpMode_ = heatingOperationMode()) { + if (heatingOpMode_->heatPumpAirToWaters().size() > 1) { + resetHeatingOperationMode(); + } else { + // If we are the only one using this heating operation mode, remove it (it will remove the children too) + 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(); diff --git a/src/model/HeatPumpAirToWaterCooling.cpp b/src/model/HeatPumpAirToWaterCooling.cpp index 3b8bc88416..76809079f8 100644 --- a/src/model/HeatPumpAirToWaterCooling.cpp +++ b/src/model/HeatPumpAirToWaterCooling.cpp @@ -114,11 +114,6 @@ namespace model { // This handles resetting the ports, and bypassing ParentObject::clone so it doesn't clone children auto t_clone = StraightComponent_Impl::clone(model).cast(); - // TODO: Cloning a ModelObjectList clones the underlying objects too, I don't know if want that - // TODO: if we do not want that, we need to make the Speed Data a ResourceObject instead and not just a ParentObject - // auto speedDataListClone = speedDataList().clone(model).cast(); - // t_clone.getImpl()->setSpeedDataList(speedDataListClone); - // Make a clean list and repopulate it, without cloning the SpeedData objects auto newSpeedList = ModelObjectList(model); newSpeedList.setName(t_clone.nameString() + " Speed Data List"); @@ -502,8 +497,7 @@ namespace model { } void HeatPumpAirToWaterCooling_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 + speedDataList().removeAllModelObjects(); // This just clears the list, does not delete the underlying objects } } // namespace detail diff --git a/src/model/HeatPumpAirToWaterHeating.cpp b/src/model/HeatPumpAirToWaterHeating.cpp index 5daa866dfa..8d5988c46d 100644 --- a/src/model/HeatPumpAirToWaterHeating.cpp +++ b/src/model/HeatPumpAirToWaterHeating.cpp @@ -114,11 +114,6 @@ namespace model { // This handles resetting the ports, and bypassing ParentObject::clone so it doesn't clone children auto t_clone = StraightComponent_Impl::clone(model).cast(); - // TODO: Cloning a ModelObjectList clones the underlying objects too, I don't know if want that - // TODO: if we do not want that, we need to make the Speed Data a ResourceObject instead and not just a ParentObject - // auto speedDataListClone = speedDataList().clone(model).cast(); - // t_clone.getImpl()->setSpeedDataList(speedDataListClone); - // Make a clean list and repopulate it, without cloning the SpeedData objects auto newSpeedList = ModelObjectList(model); newSpeedList.setName(t_clone.nameString() + " Speed Data List"); diff --git a/src/model/HeatPumpAirToWater_Impl.hpp b/src/model/HeatPumpAirToWater_Impl.hpp index bec440b6b8..122eec78c4 100644 --- a/src/model/HeatPumpAirToWater_Impl.hpp +++ b/src/model/HeatPumpAirToWater_Impl.hpp @@ -45,13 +45,13 @@ namespace model { virtual std::vector getScheduleTypeKeys(const Schedule& schedule) const override; - // TODO: You may need to override these since base is StraightComponent - // virtual ModelObject clone(Model model) const override; + // base is StraightComponent + virtual ModelObject clone(Model model) const override; - // virtual std::vector children() const override; + virtual std::vector children() const override; // virtual std::vector allowableChildTypes() const override; - // virtual std::vector remove() override; + virtual std::vector remove() override; // Overrides from StraightComponent virtual unsigned inletPort() const override; @@ -59,10 +59,6 @@ namespace model { virtual bool addToNode(Node& node) override; - // TODO: If your component can be contained, override these - // 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; diff --git a/src/model/test/HeatPumpAirToWaterCooling_GTest.cpp b/src/model/test/HeatPumpAirToWaterCooling_GTest.cpp index 8e324347e7..544c21da98 100644 --- a/src/model/test/HeatPumpAirToWaterCooling_GTest.cpp +++ b/src/model/test/HeatPumpAirToWaterCooling_GTest.cpp @@ -12,6 +12,7 @@ #include "../HeatPumpAirToWater_Impl.hpp" #include "../HeatPumpAirToWaterCoolingSpeedData.hpp" #include "../HeatPumpAirToWaterCoolingSpeedData_Impl.hpp" +#include "../HeatPumpAirToWaterHeating.hpp" #include "../Model.hpp" #include "../Schedule.hpp" @@ -35,9 +36,17 @@ #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); @@ -312,13 +321,6 @@ TEST_F(ModelFixture, HeatPumpAirToWaterCooling_clone) { auto rmed = awhp.remove(); - auto 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); - }; - 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()); @@ -420,6 +422,11 @@ TEST_F(ModelFixture, HeatPumpAirToWaterCooling_containingHVACComponent) { 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_EQ(0, awhp_cc.heatPumpAirToWaters().size()); @@ -427,50 +434,65 @@ TEST_F(ModelFixture, HeatPumpAirToWaterCooling_containingHVACComponent) { ASSERT_TRUE(awhp_cc.containingHVACComponent()); EXPECT_EQ(awhp, awhp_cc.containingHVACComponent().get()); EXPECT_EQ(1, awhp_cc.heatPumpAirToWaters().size()); + EXPECT_EQ(5, m.getConcreteModelObjects().size()); + // Each speed has 3 curves + EXPECT_EQ(5 * 3, m.getModelObjects().size()); EXPECT_TRUE(awhp2.setCoolingOperationMode(awhp_cc)); ASSERT_TRUE(awhp_cc.containingHVACComponent()); EXPECT_EQ(2, awhp_cc.heatPumpAirToWaters().size()); + EXPECT_TRUE(awhp.coolingOperationMode()); + EXPECT_TRUE(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 first awhp - EXPECT_EQ(1, awhp.remove().size()); + auto rmed = awhp.remove(); + EXPECT_EQ(1, rmed.size()) << getObjectNames(rmed); EXPECT_EQ(1, awhp_cc.heatPumpAirToWaters().size()); ASSERT_TRUE(awhp_cc.containingHVACComponent()); EXPECT_EQ(awhp2, 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()); - EXPECT_EQ(0, awhp_cc.remove().size()); + 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 second awhp - EXPECT_EQ(1, awhp2.remove().size()); - EXPECT_EQ(0, m.getConcreteModelObjects().size()); - EXPECT_EQ(1, m.getConcreteModelObjects().size()); - EXPECT_EQ(1, m.getConcreteModelObjects().size()); - - EXPECT_EQ(0, awhp_cc.heatPumpAirToWaters().size()); - EXPECT_FALSE(awhp_cc.containingHVACComponent()); - EXPECT_TRUE(awhp_cc.isRemovable()); - EXPECT_EQ(2, awhp_cc.remove().size()); + // Remove the second awhp: this time it's not shared by any other object so it should remove everything + rmed = awhp2.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/HeatPumpAirToWaterHeating_GTest.cpp b/src/model/test/HeatPumpAirToWaterHeating_GTest.cpp index 9b4495e41e..8903771b8a 100644 --- a/src/model/test/HeatPumpAirToWaterHeating_GTest.cpp +++ b/src/model/test/HeatPumpAirToWaterHeating_GTest.cpp @@ -12,6 +12,7 @@ #include "../HeatPumpAirToWater_Impl.hpp" #include "../HeatPumpAirToWaterHeatingSpeedData.hpp" #include "../HeatPumpAirToWaterHeatingSpeedData_Impl.hpp" +#include "../HeatPumpAirToWaterCooling.hpp" #include "../Model.hpp" #include "../Schedule.hpp" @@ -39,6 +40,13 @@ 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); @@ -313,13 +321,6 @@ TEST_F(ModelFixture, HeatPumpAirToWaterHeating_clone) { auto rmed = awhp.remove(); - auto 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); - }; - 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()); @@ -421,6 +422,11 @@ TEST_F(ModelFixture, HeatPumpAirToWaterHeating_containingHVACComponent) { 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_EQ(0, awhp_hc.heatPumpAirToWaters().size()); @@ -428,50 +434,65 @@ TEST_F(ModelFixture, HeatPumpAirToWaterHeating_containingHVACComponent) { ASSERT_TRUE(awhp_hc.containingHVACComponent()); EXPECT_EQ(awhp, awhp_hc.containingHVACComponent().get()); EXPECT_EQ(1, awhp_hc.heatPumpAirToWaters().size()); + EXPECT_EQ(5, m.getConcreteModelObjects().size()); + // Each speed has 3 curves + EXPECT_EQ(5 * 3, m.getModelObjects().size()); EXPECT_TRUE(awhp2.setHeatingOperationMode(awhp_hc)); ASSERT_TRUE(awhp_hc.containingHVACComponent()); EXPECT_EQ(2, awhp_hc.heatPumpAirToWaters().size()); + EXPECT_TRUE(awhp.heatingOperationMode()); + EXPECT_TRUE(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 first awhp - EXPECT_EQ(1, awhp.remove().size()); + auto rmed = awhp.remove(); + EXPECT_EQ(1, rmed.size()) << getObjectNames(rmed); EXPECT_EQ(1, awhp_hc.heatPumpAirToWaters().size()); ASSERT_TRUE(awhp_hc.containingHVACComponent()); EXPECT_EQ(awhp2, 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()); - EXPECT_EQ(0, awhp_hc.remove().size()); + 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 second awhp - EXPECT_EQ(1, awhp2.remove().size()); - EXPECT_EQ(0, m.getConcreteModelObjects().size()); - EXPECT_EQ(1, m.getConcreteModelObjects().size()); - EXPECT_EQ(1, m.getConcreteModelObjects().size()); - - EXPECT_EQ(0, awhp_hc.heatPumpAirToWaters().size()); - EXPECT_FALSE(awhp_hc.containingHVACComponent()); - EXPECT_TRUE(awhp_hc.isRemovable()); - EXPECT_EQ(2, awhp_hc.remove().size()); + // Remove the second awhp: this time it's not shared by any other object so it should remove everything + rmed = awhp2.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 index ba2fc3d600..08fad4ca3a 100644 --- a/src/model/test/HeatPumpAirToWater_GTest.cpp +++ b/src/model/test/HeatPumpAirToWater_GTest.cpp @@ -12,6 +12,7 @@ #include "../ScheduleConstant.hpp" #include "../Curve.hpp" +#include "../Curve_Impl.hpp" #include "../CurveCubic.hpp" #include "../CurveBicubic.hpp" @@ -20,10 +21,14 @@ #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; @@ -269,3 +274,50 @@ TEST_F(ModelFixture, HeatPumpAirToWater_Loop) { 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(2, m2.getConcreteModelObjects().size()); + EXPECT_EQ(5, m2.getConcreteModelObjects().size()); + EXPECT_EQ(2, m2.getConcreteModelObjects().size()); + EXPECT_EQ(5, m2.getConcreteModelObjects().size()); + EXPECT_EQ((5 + 5) * 3, m.getModelObjects().size()); + } +} From c92579340d678c84fc4763ea12a9598ec6c0d06b Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Thu, 9 Oct 2025 19:34:14 +0200 Subject: [PATCH 21/45] Handle cloning HeatPumpAirToWaterHeating/Cooling to another model: we need to clone the speeds too --- src/model/HeatPumpAirToWaterCooling.cpp | 8 +++++++- src/model/HeatPumpAirToWaterHeating.cpp | 8 +++++++- src/model/test/HeatPumpAirToWater_GTest.cpp | 4 ++-- 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/model/HeatPumpAirToWaterCooling.cpp b/src/model/HeatPumpAirToWaterCooling.cpp index 76809079f8..ec2139ec95 100644 --- a/src/model/HeatPumpAirToWaterCooling.cpp +++ b/src/model/HeatPumpAirToWaterCooling.cpp @@ -111,6 +111,7 @@ namespace model { } 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(); @@ -121,7 +122,12 @@ namespace model { OS_ASSERT(ok); for (const auto& speed : speeds()) { - ok = t_clone.addSpeed(speed); + if (same_model) { + ok = t_clone.addSpeed(speed); + } else { + auto speedClone = speed.clone(model).cast(); + ok = t_clone.addSpeed(speedClone); + } OS_ASSERT(ok); } diff --git a/src/model/HeatPumpAirToWaterHeating.cpp b/src/model/HeatPumpAirToWaterHeating.cpp index 8d5988c46d..b72ed38a28 100644 --- a/src/model/HeatPumpAirToWaterHeating.cpp +++ b/src/model/HeatPumpAirToWaterHeating.cpp @@ -111,6 +111,7 @@ namespace model { } 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(); @@ -121,7 +122,12 @@ namespace model { OS_ASSERT(ok); for (const auto& speed : speeds()) { - ok = t_clone.addSpeed(speed); + if (same_model) { + ok = t_clone.addSpeed(speed); + } else { + auto speedClone = speed.clone(model).cast(); + ok = t_clone.addSpeed(speedClone); + } OS_ASSERT(ok); } diff --git a/src/model/test/HeatPumpAirToWater_GTest.cpp b/src/model/test/HeatPumpAirToWater_GTest.cpp index 08fad4ca3a..e00d611988 100644 --- a/src/model/test/HeatPumpAirToWater_GTest.cpp +++ b/src/model/test/HeatPumpAirToWater_GTest.cpp @@ -314,9 +314,9 @@ TEST_F(ModelFixture, HeatPumpAirToWater_Clone) { Model m2; auto awhpClone2 = awhp.clone(m2).cast(); EXPECT_EQ(1, m2.getConcreteModelObjects().size()); - EXPECT_EQ(2, m2.getConcreteModelObjects().size()); + EXPECT_EQ(1, m2.getConcreteModelObjects().size()); EXPECT_EQ(5, m2.getConcreteModelObjects().size()); - EXPECT_EQ(2, m2.getConcreteModelObjects().size()); + EXPECT_EQ(1, m2.getConcreteModelObjects().size()); EXPECT_EQ(5, m2.getConcreteModelObjects().size()); EXPECT_EQ((5 + 5) * 3, m.getModelObjects().size()); } From 334073cc673c6c6224c5a6bffe39f2393a216eff Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Thu, 9 Oct 2025 21:03:57 +0200 Subject: [PATCH 22/45] Wrong min fields --- resources/model/OpenStudio.idd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/model/OpenStudio.idd b/resources/model/OpenStudio.idd index 27fe4f6772..c46678bcf1 100644 --- a/resources/model/OpenStudio.idd +++ b/resources/model/OpenStudio.idd @@ -14880,7 +14880,7 @@ OS:HeatPump:AirToWater:FuelFired:Cooling, 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 47 + \min-fields 20 A1, \field Handle \required-field \type handle From eb781ff069afbd29166fd1cdf9d5ffd5c0a1d6fd Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Thu, 9 Oct 2025 21:05:10 +0200 Subject: [PATCH 23/45] Enforce unicity: A HeatPumpAirToWaterCooling/Heating is unique to a HeatPumpAirToWater (realized this while doing FT: the Cooling/heating object is on an plant loop and triggers translation of wrapper) --- src/model/HeatPumpAirToWater.cpp | 31 ++++++++++--------- src/model/HeatPumpAirToWaterCooling.cpp | 25 ++++++++------- src/model/HeatPumpAirToWaterCooling.hpp | 4 +-- src/model/HeatPumpAirToWaterCooling_Impl.hpp | 4 +-- src/model/HeatPumpAirToWaterHeating.cpp | 25 ++++++++------- src/model/HeatPumpAirToWaterHeating.hpp | 4 +-- src/model/HeatPumpAirToWaterHeating_Impl.hpp | 4 +-- .../test/HeatPumpAirToWaterCooling_GTest.cpp | 25 ++++++++------- .../test/HeatPumpAirToWaterHeating_GTest.cpp | 25 ++++++++------- 9 files changed, 80 insertions(+), 67 deletions(-) diff --git a/src/model/HeatPumpAirToWater.cpp b/src/model/HeatPumpAirToWater.cpp index 275fc9bf9b..07fc84859f 100644 --- a/src/model/HeatPumpAirToWater.cpp +++ b/src/model/HeatPumpAirToWater.cpp @@ -154,6 +154,8 @@ namespace model { 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); @@ -171,24 +173,15 @@ namespace model { std::vector HeatPumpAirToWater_Impl::remove() { std::vector result; - // Don't delete the underlying HeatPumpAirToWaterCooling / Heating objects if used by several HeatPumpAirToWater + // 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()) { - if (coolingOpMode_->heatPumpAirToWaters().size() > 1) { - resetCoolingOperationMode(); - } else { - // If we are the only one using this cooling operation mode, remove it (it will remove the children too) - resetCoolingOperationMode(); // Need to reset so it's marked as removable first - result = coolingOpMode_->remove(); - } + resetCoolingOperationMode(); + result = coolingOpMode_->remove(); } if (auto heatingOpMode_ = heatingOperationMode()) { - if (heatingOpMode_->heatPumpAirToWaters().size() > 1) { - resetHeatingOperationMode(); - } else { - // If we are the only one using this heating operation mode, remove it (it will remove the children too) - resetHeatingOperationMode(); - openstudio::detail::concat_helper(result, heatingOpMode_->remove()); - } + resetHeatingOperationMode(); + openstudio::detail::concat_helper(result, heatingOpMode_->remove()); } openstudio::detail::concat_helper(result, StraightComponent_Impl::remove()); return result; @@ -459,6 +452,10 @@ namespace model { } 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; } @@ -469,6 +466,10 @@ namespace model { } 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; } diff --git a/src/model/HeatPumpAirToWaterCooling.cpp b/src/model/HeatPumpAirToWaterCooling.cpp index ec2139ec95..4e71ceb0f6 100644 --- a/src/model/HeatPumpAirToWaterCooling.cpp +++ b/src/model/HeatPumpAirToWaterCooling.cpp @@ -95,19 +95,22 @@ namespace model { } boost::optional HeatPumpAirToWaterCooling_Impl::containingHVACComponent() const { - auto awhps = getObject().getModelObjectSources(HeatPumpAirToWater::iddObjectType()); - auto count = awhps.size(); - if (count == 1) { - return awhps[0]; - } else if (count > 1) { - LOG(Warn, briefDescription() << " is referenced by more than one CoilCoolingElectricMultiStage, returning the first"); - return awhps[0]; + auto awhp_ = heatPumpAirToWater(); + if (awhp_) { + return std::move(*awhp_); } return boost::none; } - std::vector HeatPumpAirToWaterCooling_Impl::heatPumpAirToWaters() const { - return getObject().getModelObjectSources(HeatPumpAirToWater::iddObjectType()); + 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 { @@ -701,8 +704,8 @@ namespace model { return getImpl()->removeSpeed(index); } - std::vector HeatPumpAirToWaterCooling::heatPumpAirToWaters() const { - return getImpl()->heatPumpAirToWaters(); + boost::optional HeatPumpAirToWaterCooling::heatPumpAirToWater() const { + return getImpl()->heatPumpAirToWater(); } /// @cond diff --git a/src/model/HeatPumpAirToWaterCooling.hpp b/src/model/HeatPumpAirToWaterCooling.hpp index 34cb4827e9..a76285d867 100644 --- a/src/model/HeatPumpAirToWaterCooling.hpp +++ b/src/model/HeatPumpAirToWaterCooling.hpp @@ -166,8 +166,8 @@ namespace model { */ bool removeSpeed(unsigned index); - // Convenience function to return all HeatPumpAirToWater objects that reference this cooling coil - std::vector heatPumpAirToWaters() const; + // Convenience function to return the HeatPumpAirToWater object that reference this cooling coil if any + boost::optional heatPumpAirToWater() const; // Autosize methods boost::optional autosizedRatedAirFlowRate() const; diff --git a/src/model/HeatPumpAirToWaterCooling_Impl.hpp b/src/model/HeatPumpAirToWaterCooling_Impl.hpp index d4734ac22a..e901f66e89 100644 --- a/src/model/HeatPumpAirToWaterCooling_Impl.hpp +++ b/src/model/HeatPumpAirToWaterCooling_Impl.hpp @@ -158,8 +158,8 @@ namespace model { bool removeSpeed(unsigned index); void removeAllSpeeds(); - // Convenience function to return all HeatPumpAirToWater objects that reference this cooling coil - std::vector heatPumpAirToWaters() const; + // Convenience function to return the HeatPumpAirToWater object that reference this cooling coil if any + boost::optional heatPumpAirToWater() const; // Autosize methods diff --git a/src/model/HeatPumpAirToWaterHeating.cpp b/src/model/HeatPumpAirToWaterHeating.cpp index b72ed38a28..e33fe9a82f 100644 --- a/src/model/HeatPumpAirToWaterHeating.cpp +++ b/src/model/HeatPumpAirToWaterHeating.cpp @@ -95,19 +95,22 @@ namespace model { } boost::optional HeatPumpAirToWaterHeating_Impl::containingHVACComponent() const { - auto awhps = getObject().getModelObjectSources(HeatPumpAirToWater::iddObjectType()); - auto count = awhps.size(); - if (count == 1) { - return awhps[0]; - } else if (count > 1) { - LOG(Warn, briefDescription() << " is referenced by more than one CoilHeatingElectricMultiStage, returning the first"); - return awhps[0]; + auto awhp_ = heatPumpAirToWater(); + if (awhp_) { + return std::move(*awhp_); } return boost::none; } - std::vector HeatPumpAirToWaterHeating_Impl::heatPumpAirToWaters() const { - return getObject().getModelObjectSources(HeatPumpAirToWater::iddObjectType()); + 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 { @@ -702,8 +705,8 @@ namespace model { return getImpl()->removeSpeed(index); } - std::vector HeatPumpAirToWaterHeating::heatPumpAirToWaters() const { - return getImpl()->heatPumpAirToWaters(); + boost::optional HeatPumpAirToWaterHeating::heatPumpAirToWater() const { + return getImpl()->heatPumpAirToWater(); } /// @cond diff --git a/src/model/HeatPumpAirToWaterHeating.hpp b/src/model/HeatPumpAirToWaterHeating.hpp index 7c5eca92bd..57ef162662 100644 --- a/src/model/HeatPumpAirToWaterHeating.hpp +++ b/src/model/HeatPumpAirToWaterHeating.hpp @@ -166,8 +166,8 @@ namespace model { */ bool removeSpeed(unsigned index); - // Convenience function to return all HeatPumpAirToWater objects that reference this heating coil - std::vector heatPumpAirToWaters() const; + // Convenience function to return the HeatPumpAirToWater object that reference this heating coil if any + boost::optional heatPumpAirToWater() const; // Autosize methods boost::optional autosizedRatedAirFlowRate() const; diff --git a/src/model/HeatPumpAirToWaterHeating_Impl.hpp b/src/model/HeatPumpAirToWaterHeating_Impl.hpp index 8e62a5201a..743f1edc21 100644 --- a/src/model/HeatPumpAirToWaterHeating_Impl.hpp +++ b/src/model/HeatPumpAirToWaterHeating_Impl.hpp @@ -158,8 +158,8 @@ namespace model { bool removeSpeed(unsigned index); void removeAllSpeeds(); - // Convenience function to return all HeatPumpAirToWater objects that reference this heating coil - std::vector heatPumpAirToWaters() const; + // Convenience function to return the HeatPumpAirToWater object that reference this heating coil if any + boost::optional heatPumpAirToWater() const; // Autosize methods diff --git a/src/model/test/HeatPumpAirToWaterCooling_GTest.cpp b/src/model/test/HeatPumpAirToWaterCooling_GTest.cpp index 544c21da98..40353795ee 100644 --- a/src/model/test/HeatPumpAirToWaterCooling_GTest.cpp +++ b/src/model/test/HeatPumpAirToWaterCooling_GTest.cpp @@ -428,22 +428,25 @@ TEST_F(ModelFixture, HeatPumpAirToWaterCooling_containingHVACComponent) { EXPECT_TRUE(awhp_cc.addSpeed(speed)); } EXPECT_FALSE(awhp_cc.containingHVACComponent()); - EXPECT_EQ(0, awhp_cc.heatPumpAirToWaters().size()); + EXPECT_FALSE(awhp_cc.heatPumpAirToWater()); EXPECT_TRUE(awhp.setCoolingOperationMode(awhp_cc)); ASSERT_TRUE(awhp_cc.containingHVACComponent()); EXPECT_EQ(awhp, awhp_cc.containingHVACComponent().get()); - EXPECT_EQ(1, awhp_cc.heatPumpAirToWaters().size()); + 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()); - EXPECT_TRUE(awhp2.setCoolingOperationMode(awhp_cc)); + // Already assigned to another AWHP, not letting it be assigned again + EXPECT_FALSE(awhp2.setCoolingOperationMode(awhp_cc)); ASSERT_TRUE(awhp_cc.containingHVACComponent()); - EXPECT_EQ(2, awhp_cc.heatPumpAirToWaters().size()); + ASSERT_TRUE(awhp_cc.heatPumpAirToWater()); + EXPECT_EQ(awhp, awhp_cc.heatPumpAirToWater().get()); EXPECT_TRUE(awhp.coolingOperationMode()); - EXPECT_TRUE(awhp2.coolingOperationMode()); + EXPECT_FALSE(awhp2.coolingOperationMode()); EXPECT_FALSE(awhp.heatingOperationMode()); EXPECT_FALSE(awhp2.heatingOperationMode()); @@ -463,12 +466,12 @@ TEST_F(ModelFixture, HeatPumpAirToWaterCooling_containingHVACComponent) { EXPECT_EQ(5, m.getConcreteModelObjects().size()); EXPECT_EQ(5 * 3, m.getModelObjects().size()); - // Remove the first awhp - auto rmed = awhp.remove(); + // Remove the second awhp + auto rmed = awhp2.remove(); EXPECT_EQ(1, rmed.size()) << getObjectNames(rmed); - EXPECT_EQ(1, awhp_cc.heatPumpAirToWaters().size()); + EXPECT_TRUE(awhp_cc.heatPumpAirToWater()); ASSERT_TRUE(awhp_cc.containingHVACComponent()); - EXPECT_EQ(awhp2, awhp_cc.containingHVACComponent().get()); + 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()); @@ -485,8 +488,8 @@ TEST_F(ModelFixture, HeatPumpAirToWaterCooling_containingHVACComponent) { EXPECT_EQ(5, m.getConcreteModelObjects().size()); EXPECT_EQ(5 * 3, m.getModelObjects().size()); - // Remove the second awhp: this time it's not shared by any other object so it should remove everything - rmed = awhp2.remove(); + // 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); diff --git a/src/model/test/HeatPumpAirToWaterHeating_GTest.cpp b/src/model/test/HeatPumpAirToWaterHeating_GTest.cpp index 8903771b8a..47d1b288f1 100644 --- a/src/model/test/HeatPumpAirToWaterHeating_GTest.cpp +++ b/src/model/test/HeatPumpAirToWaterHeating_GTest.cpp @@ -428,22 +428,25 @@ TEST_F(ModelFixture, HeatPumpAirToWaterHeating_containingHVACComponent) { EXPECT_TRUE(awhp_hc.addSpeed(speed)); } EXPECT_FALSE(awhp_hc.containingHVACComponent()); - EXPECT_EQ(0, awhp_hc.heatPumpAirToWaters().size()); + EXPECT_FALSE(awhp_hc.heatPumpAirToWater()); EXPECT_TRUE(awhp.setHeatingOperationMode(awhp_hc)); ASSERT_TRUE(awhp_hc.containingHVACComponent()); EXPECT_EQ(awhp, awhp_hc.containingHVACComponent().get()); - EXPECT_EQ(1, awhp_hc.heatPumpAirToWaters().size()); + 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()); - EXPECT_TRUE(awhp2.setHeatingOperationMode(awhp_hc)); + // Already assigned to another AWHP, not letting it be assigned again + EXPECT_FALSE(awhp2.setHeatingOperationMode(awhp_hc)); ASSERT_TRUE(awhp_hc.containingHVACComponent()); - EXPECT_EQ(2, awhp_hc.heatPumpAirToWaters().size()); + ASSERT_TRUE(awhp_hc.heatPumpAirToWater()); + EXPECT_EQ(awhp, awhp_hc.heatPumpAirToWater().get()); EXPECT_TRUE(awhp.heatingOperationMode()); - EXPECT_TRUE(awhp2.heatingOperationMode()); + EXPECT_FALSE(awhp2.heatingOperationMode()); EXPECT_FALSE(awhp.coolingOperationMode()); EXPECT_FALSE(awhp2.coolingOperationMode()); @@ -463,12 +466,12 @@ TEST_F(ModelFixture, HeatPumpAirToWaterHeating_containingHVACComponent) { EXPECT_EQ(5, m.getConcreteModelObjects().size()); EXPECT_EQ(5 * 3, m.getModelObjects().size()); - // Remove the first awhp - auto rmed = awhp.remove(); + // Remove the second awhp + auto rmed = awhp2.remove(); EXPECT_EQ(1, rmed.size()) << getObjectNames(rmed); - EXPECT_EQ(1, awhp_hc.heatPumpAirToWaters().size()); + EXPECT_TRUE(awhp_hc.heatPumpAirToWater()); ASSERT_TRUE(awhp_hc.containingHVACComponent()); - EXPECT_EQ(awhp2, awhp_hc.containingHVACComponent().get()); + 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()); @@ -485,8 +488,8 @@ TEST_F(ModelFixture, HeatPumpAirToWaterHeating_containingHVACComponent) { EXPECT_EQ(5, m.getConcreteModelObjects().size()); EXPECT_EQ(5 * 3, m.getModelObjects().size()); - // Remove the second awhp: this time it's not shared by any other object so it should remove everything - rmed = awhp2.remove(); + // 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); From c177de54317d7998009d94e903172db6eca65183 Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Thu, 9 Oct 2025 21:06:02 +0200 Subject: [PATCH 24/45] Initial implementation of forward translator for HeatPumpAirToWater --- src/energyplus/CMakeLists.txt | 1 + src/energyplus/ForwardTranslator.cpp | 6 +- src/energyplus/ForwardTranslator.hpp | 7 + .../ForwardTranslateHeatPumpAirToWater.cpp | 496 ++++++++++++++++++ 4 files changed, 509 insertions(+), 1 deletion(-) create mode 100644 src/energyplus/ForwardTranslator/ForwardTranslateHeatPumpAirToWater.cpp diff --git a/src/energyplus/CMakeLists.txt b/src/energyplus/CMakeLists.txt index 9546eba6f0..69b267f7da 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 diff --git a/src/energyplus/ForwardTranslator.cpp b/src/energyplus/ForwardTranslator.cpp index 98575da2a6..ad95ab9936 100644 --- a/src/energyplus/ForwardTranslator.cpp +++ b/src/energyplus/ForwardTranslator.cpp @@ -1953,7 +1953,11 @@ 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_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..5534d5adbf --- /dev/null +++ b/src/energyplus/ForwardTranslator/ForwardTranslateHeatPumpAirToWater.cpp @@ -0,0 +1,496 @@ +/*********************************************************************************************************************** +* 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.numberOfSpeeds(); + 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.inletModelObject()) { + 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.numberOfSpeeds(); + 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(0, 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); + + if (modelObject.isResistiveDefrostHeaterCapacityAutosized()) { + idfObject.setString(HeatPump_AirToWaterFields::ResistiveDefrostHeaterCapacity, "Autosize"); + } else { + // Resistive Defrost Heater Capacity: boost::optional + if (boost::optional resistiveDefrostHeaterCapacity_ = modelObject.resistiveDefrostHeaterCapacity()) { + idfObject.setDouble(HeatPump_AirToWaterFields::ResistiveDefrostHeaterCapacity, resistiveDefrostHeaterCapacity_.get()); + } + } + + // 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 From 188c4ed3ff629b6ba0d87666193f37799508a422 Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Thu, 9 Oct 2025 21:06:40 +0200 Subject: [PATCH 25/45] Add function that will translate the wrapper object instead of the child --- ...ranslatePlantEquipmentOperationSchemes.cpp | 14 +++++++++++ .../ForwardTranslatePlantLoop.cpp | 24 +++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/src/energyplus/ForwardTranslator/ForwardTranslatePlantEquipmentOperationSchemes.cpp b/src/energyplus/ForwardTranslator/ForwardTranslatePlantEquipmentOperationSchemes.cpp index 64e7c03496..d05734d5b9 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(); 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(); From dec4fb7273e048991bcb509e284bc394e23eb5c9 Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Thu, 9 Oct 2025 23:06:58 +0200 Subject: [PATCH 26/45] An E+ PR that's open right now is removing the Resistive Defrost Capacity to be autosized cf https://github.com/NREL/EnergyPlus/pull/11251 --- resources/model/OpenStudio.idd | 1 - src/model/HeatPumpAirToWater.cpp | 60 ++++++++------------- src/model/HeatPumpAirToWater.hpp | 12 ++--- src/model/HeatPumpAirToWater_Impl.hpp | 11 ++-- src/model/test/HeatPumpAirToWater_GTest.cpp | 13 ++--- 5 files changed, 35 insertions(+), 62 deletions(-) diff --git a/resources/model/OpenStudio.idd b/resources/model/OpenStudio.idd index c46678bcf1..e877fe777a 100644 --- a/resources/model/OpenStudio.idd +++ b/resources/model/OpenStudio.idd @@ -14949,7 +14949,6 @@ OS:HeatPump:AirToWater, \required-field \type real \minimum 0.0 - \autosizable \units W \note only applicable if resistive defrost strategy is specified \ip-units W diff --git a/src/model/HeatPumpAirToWater.cpp b/src/model/HeatPumpAirToWater.cpp index 07fc84859f..95d0d0d243 100644 --- a/src/model/HeatPumpAirToWater.cpp +++ b/src/model/HeatPumpAirToWater.cpp @@ -269,21 +269,10 @@ namespace model { return value.get(); } - boost::optional HeatPumpAirToWater_Impl::resistiveDefrostHeaterCapacity() const { - return getDouble(OS_HeatPump_AirToWaterFields::ResistiveDefrostHeaterCapacity, true); - } - - bool HeatPumpAirToWater_Impl::isResistiveDefrostHeaterCapacityAutosized() const { - bool result = false; - boost::optional value = getString(OS_HeatPump_AirToWaterFields::ResistiveDefrostHeaterCapacity, true); - if (value) { - result = openstudio::istringEqual(value.get(), "autosize"); - } - return result; - } - - boost::optional HeatPumpAirToWater_Impl::autosizedResistiveDefrostHeaterCapacity() const { - return getAutosizedValue("TODO_CHECK_SQL Resistive Defrost Heater Capacity", "W"); + 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 { @@ -399,11 +388,6 @@ namespace model { return result; } - void HeatPumpAirToWater_Impl::autosizeResistiveDefrostHeaterCapacity() { - const bool result = setString(OS_HeatPump_AirToWaterFields::ResistiveDefrostHeaterCapacity, "autosize"); - OS_ASSERT(result); - } - bool HeatPumpAirToWater_Impl::setDefrostEnergyInputRatioFunctionofTemperatureCurve(const Curve& defrostEnergyInputRatioFunctionofTemperatureCurve) { const bool result = setPointer(OS_HeatPump_AirToWaterFields::DefrostEnergyInputRatioFunctionofTemperatureCurveName, @@ -479,16 +463,26 @@ namespace model { OS_ASSERT(result); } - void HeatPumpAirToWater_Impl::autosize() { - autosizeResistiveDefrostHeaterCapacity(); + boost::optional HeatPumpAirToWater_Impl::autosizedRatedAirFlowRateinHeatingMode() const { + return getAutosizedValue("Design Size Source Side Volume Flow Rate", "m3/s", "HeatPump:AirToWater:Heating"); } - void HeatPumpAirToWater_Impl::applySizingValues() { - if (boost::optional val_ = autosizedResistiveDefrostHeaterCapacity()) { - setResistiveDefrostHeaterCapacity(*val_); - } + boost::optional HeatPumpAirToWater_Impl::autosizedRatedWaterFlowRateinHeatingMode() const { + return getAutosizedValue("Design Size Load Side Volume Flow Rate", "m3/s", "HeatPump:AirToWater:Heating"); } + boost::optional HeatPumpAirToWater_Impl::autosizedRatedAirFlowRateinCoolingMode() const { + return getAutosizedValue("Design Size Source Side Volume Flow Rate", "m3/s", "HeatPump:AirToWater:Cooling"); + } + + boost::optional HeatPumpAirToWater_Impl::autosizedRatedWaterFlowRateinCoolingMode() const { + return getAutosizedValue("Design Size Load Side Volume Flow Rate", "m3/s", "HeatPump:AirToWater:Cooling"); + } + + void HeatPumpAirToWater_Impl::autosize() {} + + void HeatPumpAirToWater_Impl::applySizingValues() {} + boost::optional HeatPumpAirToWater_Impl::coolingLoop() const { if (auto mode_ = coolingOperationMode()) { return mode_->plantLoop(); @@ -581,18 +575,10 @@ namespace model { return getImpl()->heatPumpDefrostTimePeriodFraction(); } - boost::optional HeatPumpAirToWater::resistiveDefrostHeaterCapacity() const { + double HeatPumpAirToWater::resistiveDefrostHeaterCapacity() const { return getImpl()->resistiveDefrostHeaterCapacity(); } - bool HeatPumpAirToWater::isResistiveDefrostHeaterCapacityAutosized() const { - return getImpl()->isResistiveDefrostHeaterCapacityAutosized(); - } - - boost::optional HeatPumpAirToWater::autosizedResistiveDefrostHeaterCapacity() const { - return getImpl()->autosizedResistiveDefrostHeaterCapacity(); - } - boost::optional HeatPumpAirToWater::defrostEnergyInputRatioFunctionofTemperatureCurve() const { return getImpl()->defrostEnergyInputRatioFunctionofTemperatureCurve(); } @@ -678,10 +664,6 @@ namespace model { return getImpl()->setResistiveDefrostHeaterCapacity(resistiveDefrostHeaterCapacity); } - void HeatPumpAirToWater::autosizeResistiveDefrostHeaterCapacity() { - getImpl()->autosizeResistiveDefrostHeaterCapacity(); - } - bool HeatPumpAirToWater::setDefrostEnergyInputRatioFunctionofTemperatureCurve(const Curve& defrostEnergyInputRatioFunctionofTemperatureCurve) { return getImpl()->setDefrostEnergyInputRatioFunctionofTemperatureCurve( defrostEnergyInputRatioFunctionofTemperatureCurve); diff --git a/src/model/HeatPumpAirToWater.hpp b/src/model/HeatPumpAirToWater.hpp index 7286367bc2..92985f62ae 100644 --- a/src/model/HeatPumpAirToWater.hpp +++ b/src/model/HeatPumpAirToWater.hpp @@ -74,9 +74,7 @@ namespace model { double heatPumpDefrostTimePeriodFraction() const; - boost::optional resistiveDefrostHeaterCapacity() const; - - bool isResistiveDefrostHeaterCapacityAutosized() const; + double resistiveDefrostHeaterCapacity() const; boost::optional defrostEnergyInputRatioFunctionofTemperatureCurve() const; @@ -124,8 +122,6 @@ namespace model { bool setResistiveDefrostHeaterCapacity(double resistiveDefrostHeaterCapacity); - void autosizeResistiveDefrostHeaterCapacity(); - bool setDefrostEnergyInputRatioFunctionofTemperatureCurve(const Curve& defrostEnergyInputRatioFunctionofTemperatureCurve); void resetDefrostEnergyInputRatioFunctionofTemperatureCurve(); @@ -161,7 +157,11 @@ namespace model { boost::optional heatingLoop() const; // Autosize methods - boost::optional autosizedResistiveDefrostHeaterCapacity() const; + boost::optional autosizedRatedAirFlowRateinHeatingMode() const; + boost::optional autosizedRatedWaterFlowRateinHeatingMode() const; + boost::optional autosizedRatedAirFlowRateinCoolingMode() const; + boost::optional autosizedRatedWaterFlowRateinCoolingMode() const; + //@} protected: /// @cond diff --git a/src/model/HeatPumpAirToWater_Impl.hpp b/src/model/HeatPumpAirToWater_Impl.hpp index 122eec78c4..a0a42af7c9 100644 --- a/src/model/HeatPumpAirToWater_Impl.hpp +++ b/src/model/HeatPumpAirToWater_Impl.hpp @@ -90,9 +90,7 @@ namespace model { double heatPumpDefrostTimePeriodFraction() const; - boost::optional resistiveDefrostHeaterCapacity() const; - - bool isResistiveDefrostHeaterCapacityAutosized() const; + double resistiveDefrostHeaterCapacity() const; boost::optional defrostEnergyInputRatioFunctionofTemperatureCurve() const; @@ -140,8 +138,6 @@ namespace model { bool setResistiveDefrostHeaterCapacity(double resistiveDefrostHeaterCapacity); - void autosizeResistiveDefrostHeaterCapacity(); - bool setDefrostEnergyInputRatioFunctionofTemperatureCurve(const Curve& defrostEnergyInputRatioFunctionofTemperatureCurve); void resetDefrostEnergyInputRatioFunctionofTemperatureCurve(); @@ -174,7 +170,10 @@ namespace model { boost::optional heatingLoop() const; // Autosize methods - boost::optional autosizedResistiveDefrostHeaterCapacity() const; + boost::optional autosizedRatedAirFlowRateinHeatingMode() const; + boost::optional autosizedRatedWaterFlowRateinHeatingMode() const; + boost::optional autosizedRatedAirFlowRateinCoolingMode() const; + boost::optional autosizedRatedWaterFlowRateinCoolingMode() const; //@} protected: diff --git a/src/model/test/HeatPumpAirToWater_GTest.cpp b/src/model/test/HeatPumpAirToWater_GTest.cpp index e00d611988..d8446711ab 100644 --- a/src/model/test/HeatPumpAirToWater_GTest.cpp +++ b/src/model/test/HeatPumpAirToWater_GTest.cpp @@ -111,20 +111,13 @@ TEST_F(ModelFixture, HeatPumpAirToWater_GettersSetters) { // Resistive Defrost Heater Capacity: Required Double // Ctor default - ASSERT_TRUE(heatPumpAirToWater.resistiveDefrostHeaterCapacity()); - EXPECT_EQ(0.0, heatPumpAirToWater.resistiveDefrostHeaterCapacity().get()); - // Autosize - heatPumpAirToWater.autosizeResistiveDefrostHeaterCapacity(); - EXPECT_TRUE(heatPumpAirToWater.isResistiveDefrostHeaterCapacityAutosized()); + EXPECT_EQ(0.0, heatPumpAirToWater.resistiveDefrostHeaterCapacity()); // Set EXPECT_TRUE(heatPumpAirToWater.setResistiveDefrostHeaterCapacity(1.2)); - ASSERT_TRUE(heatPumpAirToWater.resistiveDefrostHeaterCapacity()); - EXPECT_EQ(1.2, heatPumpAirToWater.resistiveDefrostHeaterCapacity().get()); + EXPECT_EQ(1.2, heatPumpAirToWater.resistiveDefrostHeaterCapacity()); // Bad Value EXPECT_FALSE(heatPumpAirToWater.setResistiveDefrostHeaterCapacity(-10.0)); - ASSERT_TRUE(heatPumpAirToWater.resistiveDefrostHeaterCapacity()); - EXPECT_EQ(1.2, heatPumpAirToWater.resistiveDefrostHeaterCapacity().get()); - EXPECT_FALSE(heatPumpAirToWater.isResistiveDefrostHeaterCapacityAutosized()); + EXPECT_EQ(1.2, heatPumpAirToWater.resistiveDefrostHeaterCapacity()); // Defrost Energy Input Ratio Function of Temperature Curve Name: Optional Object, BivariateFunctions CurveBicubic defrostEnergyInputRatioFunctionofTemperatureCurve(m); From d52f0ca7ae8b70252df1ab55e688d09e98c6baa7 Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Thu, 9 Oct 2025 23:21:06 +0200 Subject: [PATCH 27/45] Add FT test (WIP) --- src/energyplus/CMakeLists.txt | 1 + .../ForwardTranslateHeatPumpAirToWater.cpp | 18 +- .../Test/HeatPumpAirToWater_GTest.cpp | 504 ++++++++++++++++++ 3 files changed, 511 insertions(+), 12 deletions(-) create mode 100644 src/energyplus/Test/HeatPumpAirToWater_GTest.cpp diff --git a/src/energyplus/CMakeLists.txt b/src/energyplus/CMakeLists.txt index 69b267f7da..1ddabb500f 100644 --- a/src/energyplus/CMakeLists.txt +++ b/src/energyplus/CMakeLists.txt @@ -770,6 +770,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/ForwardTranslateHeatPumpAirToWater.cpp b/src/energyplus/ForwardTranslator/ForwardTranslateHeatPumpAirToWater.cpp index 5534d5adbf..26dc56b47a 100644 --- a/src/energyplus/ForwardTranslator/ForwardTranslateHeatPumpAirToWater.cpp +++ b/src/energyplus/ForwardTranslator/ForwardTranslateHeatPumpAirToWater.cpp @@ -28,7 +28,7 @@ namespace energyplus { void ForwardTranslator::translateHeatPumpAirToWaterCooling(model::HeatPumpAirToWaterCooling& modelObject, IdfObject& idfObject) { // Number of Speeds for Cooling: Optional Integer - const int numberofSpeedsforCooling = modelObject.numberOfSpeeds(); + const int numberofSpeedsforCooling = modelObject.plantLoop() ? modelObject.numberOfSpeeds() : 0; idfObject.setInt(HeatPump_AirToWaterFields::NumberofSpeedsforCooling, numberofSpeedsforCooling); if (numberofSpeedsforCooling == 0) { return; @@ -208,7 +208,7 @@ namespace energyplus { void ForwardTranslator::translateHeatPumpAirToWaterHeating(model::HeatPumpAirToWaterHeating& modelObject, IdfObject& idfObject) { - const int numberofSpeedsforHeating = modelObject.numberOfSpeeds(); + const int numberofSpeedsforHeating = modelObject.plantLoop() ? modelObject.numberOfSpeeds() : 0; idfObject.setInt(HeatPump_AirToWaterFields::NumberofSpeedsforHeating, numberofSpeedsforHeating); if (numberofSpeedsforHeating == 0) { return; @@ -294,7 +294,7 @@ namespace energyplus { const unsigned number_fields = static_cast(HeatPump_AirToWaterFields::HeatingEnergyInputRatioFunctionofPLRCurveNameatSpeed1) - startIndex + 1; - auto getFieldIndex = [startIndex](HeatPump_AirToWaterFields::domain field) -> unsigned { + auto getFieldIndex = [&startIndex](HeatPump_AirToWaterFields::domain field) -> unsigned { return startIndex + (static_cast(field) - static_cast(HeatPump_AirToWaterFields::RatedHeatingCapacityatSpeed1)); }; @@ -423,7 +423,7 @@ namespace energyplus { { const std::string airOutletNodeName = modelObject.airOutletNodeName().value_or(modelObject.nameString() + " Air Outlet Node"); idfObject.setString(HeatPump_AirToWaterFields::AirOutletNodeName, airOutletNodeName); - oaNodeListIdf.setString(0, airOutletNodeName); + oaNodeListIdf.setString(1, airOutletNodeName); } m_idfObjects.emplace_back(std::move(oaNodeListIdf)); } @@ -441,14 +441,8 @@ namespace energyplus { const double heatPumpDefrostTimePeriodFraction = modelObject.heatPumpDefrostTimePeriodFraction(); idfObject.setDouble(HeatPump_AirToWaterFields::HeatPumpDefrostTimePeriodFraction, heatPumpDefrostTimePeriodFraction); - if (modelObject.isResistiveDefrostHeaterCapacityAutosized()) { - idfObject.setString(HeatPump_AirToWaterFields::ResistiveDefrostHeaterCapacity, "Autosize"); - } else { - // Resistive Defrost Heater Capacity: boost::optional - if (boost::optional resistiveDefrostHeaterCapacity_ = modelObject.resistiveDefrostHeaterCapacity()) { - idfObject.setDouble(HeatPump_AirToWaterFields::ResistiveDefrostHeaterCapacity, resistiveDefrostHeaterCapacity_.get()); - } - } + 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()) { diff --git a/src/energyplus/Test/HeatPumpAirToWater_GTest.cpp b/src/energyplus/Test/HeatPumpAirToWater_GTest.cpp new file mode 100644 index 0000000000..f5e1e1fc19 --- /dev/null +++ b/src/energyplus/Test/HeatPumpAirToWater_GTest.cpp @@ -0,0 +1,504 @@ +/*********************************************************************************************************************** +* 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/HeatPumpAirToWaterCooling.hpp" +#include "../../model/HeatPumpAirToWaterCoolingSpeedData.hpp" +#include "../../model/HeatPumpAirToWaterHeating.hpp" +#include "../../model/HeatPumpAirToWaterHeatingSpeedData.hpp" + +#include "../../model/Model.hpp" +#include "../../model/Curve.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 Method: Required String + EXPECT_TRUE(awhp.setOperatingModeControlMethod("ScheduledModes")); + + // 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)); + + // 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; + + 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("AWHP Heating Avail Schedule", idfObject.getString(HeatPump_AirToWaterFields::AvailabilityScheduleNameHeating).get()); + 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(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()); + + 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()); + } + } +} From 0e15323b5a751233f69666dba477802f41182094 Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Thu, 9 Oct 2025 23:22:43 +0200 Subject: [PATCH 28/45] Tweak PlantEquopmentOpSchemes + add missing references to E+ IDD object --- resources/energyplus/ProposedEnergy+.idd | 1 - .../ForwardTranslatePlantEquipmentOperationSchemes.cpp | 10 ++++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/resources/energyplus/ProposedEnergy+.idd b/resources/energyplus/ProposedEnergy+.idd index 166b3cf07e..1e4cb34ca4 100644 --- a/resources/energyplus/ProposedEnergy+.idd +++ b/resources/energyplus/ProposedEnergy+.idd @@ -46223,7 +46223,6 @@ HeatPump:AirToWater, A1 , \field Name \required-field \type alpha - \required-field \reference-class-name validPlantEquipmentTypes \reference validPlantEquipmentNames \reference-class-name validBranchEquipmentTypes diff --git a/src/energyplus/ForwardTranslator/ForwardTranslatePlantEquipmentOperationSchemes.cpp b/src/energyplus/ForwardTranslator/ForwardTranslatePlantEquipmentOperationSchemes.cpp index d05734d5b9..b8c933a424 100644 --- a/src/energyplus/ForwardTranslator/ForwardTranslatePlantEquipmentOperationSchemes.cpp +++ b/src/energyplus/ForwardTranslator/ForwardTranslatePlantEquipmentOperationSchemes.cpp @@ -409,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; } From 8a7d1b1d99599fb4d81213ab257376d3b12cb3c2 Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Thu, 9 Oct 2025 23:22:59 +0200 Subject: [PATCH 29/45] tweak generateclass --- developer/ruby/SubProjectClassGenerators/ModelClassGenerator.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/developer/ruby/SubProjectClassGenerators/ModelClassGenerator.rb b/developer/ruby/SubProjectClassGenerators/ModelClassGenerator.rb index 4b2fd2a751..ec741d6ab8 100644 --- a/developer/ruby/SubProjectClassGenerators/ModelClassGenerator.rb +++ b/developer/ruby/SubProjectClassGenerators/ModelClassGenerator.rb @@ -851,7 +851,7 @@ def implHppPublicMethods() result << " virtual std::vector getScheduleTypeKeys(const Schedule& schedule) const override;\n\n" end - if @derivesHVACComponent || @baseClassName == 'ParentObject' + 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" From f37bfcbddc87ba1731317fecd0d0182304ca1eaf Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Thu, 9 Oct 2025 23:38:07 +0200 Subject: [PATCH 30/45] Finish testing and fix a small bug in FT for cooling side (lambda param was captured by value) --- .../ForwardTranslateHeatPumpAirToWater.cpp | 4 +- .../Test/HeatPumpAirToWater_GTest.cpp | 260 +++++++++++++++++- 2 files changed, 261 insertions(+), 3 deletions(-) diff --git a/src/energyplus/ForwardTranslator/ForwardTranslateHeatPumpAirToWater.cpp b/src/energyplus/ForwardTranslator/ForwardTranslateHeatPumpAirToWater.cpp index 26dc56b47a..901eddeb8c 100644 --- a/src/energyplus/ForwardTranslator/ForwardTranslateHeatPumpAirToWater.cpp +++ b/src/energyplus/ForwardTranslator/ForwardTranslateHeatPumpAirToWater.cpp @@ -103,7 +103,7 @@ namespace energyplus { } // Chilled Water Outlet Node Name: Optional Node - if (boost::optional mo_ = modelObject.inletModelObject()) { + if (boost::optional mo_ = modelObject.outletModelObject()) { if (boost::optional node_ = mo_->optionalCast()) { idfObject.setString(HeatPump_AirToWaterFields::ChilledWaterOutletNodeName, node_->nameString()); } @@ -114,7 +114,7 @@ namespace energyplus { const unsigned number_fields = static_cast(HeatPump_AirToWaterFields::CoolingEnergyInputRatioFunctionofPLRCurveNameatSpeed1) - startIndex + 1; - auto getFieldIndex = [startIndex](HeatPump_AirToWaterFields::domain field) -> unsigned { + auto getFieldIndex = [&startIndex](HeatPump_AirToWaterFields::domain field) -> unsigned { return startIndex + (static_cast(field) - static_cast(HeatPump_AirToWaterFields::RatedCoolingCapacityatSpeed1)); }; diff --git a/src/energyplus/Test/HeatPumpAirToWater_GTest.cpp b/src/energyplus/Test/HeatPumpAirToWater_GTest.cpp index f5e1e1fc19..3a26705578 100644 --- a/src/energyplus/Test/HeatPumpAirToWater_GTest.cpp +++ b/src/energyplus/Test/HeatPumpAirToWater_GTest.cpp @@ -306,7 +306,6 @@ TEST_F(EnergyPlusFixture, ForwardTranslator_HeatPumpAirToWater) { ASSERT_EQ(1u, idfObjs.size()); const auto& idfObject = idfObjs.front(); - EXPECT_EQ("AWHP Heating Avail Schedule", idfObject.getString(HeatPump_AirToWaterFields::AvailabilityScheduleNameHeating).get()); 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()); @@ -329,6 +328,7 @@ TEST_F(EnergyPlusFixture, ForwardTranslator_HeatPumpAirToWater) { 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()); @@ -443,11 +443,269 @@ TEST_F(EnergyPlusFixture, ForwardTranslator_HeatPumpAirToWater) { 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); + + m.save("awhp.osm"); + w.save("awhp.idf", true); + 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); From 01b8f3b9969374fb50ac031a01582338751ff97e Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Thu, 9 Oct 2025 23:39:22 +0200 Subject: [PATCH 31/45] remove save of workspace/model --- src/energyplus/Test/HeatPumpAirToWater_GTest.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/energyplus/Test/HeatPumpAirToWater_GTest.cpp b/src/energyplus/Test/HeatPumpAirToWater_GTest.cpp index 3a26705578..7143f96743 100644 --- a/src/energyplus/Test/HeatPumpAirToWater_GTest.cpp +++ b/src/energyplus/Test/HeatPumpAirToWater_GTest.cpp @@ -267,6 +267,7 @@ PlantLoop createLoop(Model& m, const std::string& prefix) { TEST_F(EnergyPlusFixture, ForwardTranslator_HeatPumpAirToWater) { ForwardTranslator ft; + ft.setExcludeLCCObjects(true); Model m; @@ -511,8 +512,6 @@ TEST_F(EnergyPlusFixture, ForwardTranslator_HeatPumpAirToWater) { { Workspace w = ft.translateModel(m); - m.save("awhp.osm"); - w.save("awhp.idf", true); const auto idfObjs = w.getObjectsByType(IddObjectType::HeatPump_AirToWater); ASSERT_EQ(1u, idfObjs.size()); From dddd08058dbdbe2412e18065f10446ff5f1bc91b Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Thu, 9 Oct 2025 23:53:42 +0200 Subject: [PATCH 32/45] Implement autosizedRated Air/Water FlowRate in Cooling/Heating Mode by looking up with the parnet HeatPump:AirToWater's name but looking up the compType --- src/model/HeatPumpAirToWater.cpp | 16 +++++++++++++++ src/model/HeatPumpAirToWaterCooling.cpp | 26 +++++++++++++++---------- src/model/HeatPumpAirToWaterHeating.cpp | 26 +++++++++++++++---------- 3 files changed, 48 insertions(+), 20 deletions(-) diff --git a/src/model/HeatPumpAirToWater.cpp b/src/model/HeatPumpAirToWater.cpp index 95d0d0d243..e5c06a526e 100644 --- a/src/model/HeatPumpAirToWater.cpp +++ b/src/model/HeatPumpAirToWater.cpp @@ -723,6 +723,22 @@ namespace model { 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(); + } + /// @cond HeatPumpAirToWater::HeatPumpAirToWater(std::shared_ptr impl) : StraightComponent(std::move(impl)) {} /// @endcond diff --git a/src/model/HeatPumpAirToWaterCooling.cpp b/src/model/HeatPumpAirToWaterCooling.cpp index 4e71ceb0f6..431077f658 100644 --- a/src/model/HeatPumpAirToWaterCooling.cpp +++ b/src/model/HeatPumpAirToWaterCooling.cpp @@ -195,11 +195,6 @@ namespace model { return result; } - boost::optional HeatPumpAirToWaterCooling_Impl::autosizedRatedAirFlowRate() const { - LOG_AND_THROW("TODO: need to use the name of the WRAPPER object"); - return getAutosizedValue("Design Size Source Side Volume Flow Rate", "m3/s"); - } - double HeatPumpAirToWaterCooling_Impl::ratedLeavingWaterTemperature() const { boost::optional value = getDouble(OS_HeatPump_AirToWater_CoolingFields::RatedLeavingWaterTemperature, true); OS_ASSERT(value); @@ -219,11 +214,6 @@ namespace model { return result; } - boost::optional HeatPumpAirToWaterCooling_Impl::autosizedRatedWaterFlowRate() const { - LOG_AND_THROW("TODO: need to use the name of the WRAPPER object"); - return getAutosizedValue("Design Size Load Side Volume Flow Rate", "m3/s"); - } - double HeatPumpAirToWaterCooling_Impl::minimumOutdoorAirTemperature() const { boost::optional value = getDouble(OS_HeatPump_AirToWater_CoolingFields::MinimumOutdoorAirTemperature, true); OS_ASSERT(value); @@ -342,6 +332,22 @@ namespace model { 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; + } + void HeatPumpAirToWaterCooling_Impl::autosize() { autosizeRatedAirFlowRate(); autosizeRatedWaterFlowRate(); diff --git a/src/model/HeatPumpAirToWaterHeating.cpp b/src/model/HeatPumpAirToWaterHeating.cpp index e33fe9a82f..54c053060f 100644 --- a/src/model/HeatPumpAirToWaterHeating.cpp +++ b/src/model/HeatPumpAirToWaterHeating.cpp @@ -195,11 +195,6 @@ namespace model { return result; } - boost::optional HeatPumpAirToWaterHeating_Impl::autosizedRatedAirFlowRate() const { - LOG_AND_THROW("TODO: need to use the name of the WRAPPER object"); - return getAutosizedValue("Design Size Source Side Volume Flow Rate", "m3/s"); - } - double HeatPumpAirToWaterHeating_Impl::ratedLeavingWaterTemperature() const { boost::optional value = getDouble(OS_HeatPump_AirToWater_HeatingFields::RatedLeavingWaterTemperature, true); OS_ASSERT(value); @@ -219,11 +214,6 @@ namespace model { return result; } - boost::optional HeatPumpAirToWaterHeating_Impl::autosizedRatedWaterFlowRate() const { - LOG_AND_THROW("TODO: need to use the name of the WRAPPER object"); - return getAutosizedValue("Design Size Load Side Volume Flow Rate", "m3/s"); - } - double HeatPumpAirToWaterHeating_Impl::minimumOutdoorAirTemperature() const { boost::optional value = getDouble(OS_HeatPump_AirToWater_HeatingFields::MinimumOutdoorAirTemperature, true); OS_ASSERT(value); @@ -342,6 +332,22 @@ namespace model { 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; + } + void HeatPumpAirToWaterHeating_Impl::autosize() { autosizeRatedAirFlowRate(); autosizeRatedWaterFlowRate(); From e95636ace3eb15ae5b7e276dcc413c3b9cbb0f90 Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Fri, 10 Oct 2025 00:12:57 +0200 Subject: [PATCH 33/45] Enforce link between OperatingModeControlMethod (ScheduleModes) and OperatingModeControlSchedule --- src/model/HeatPumpAirToWater.cpp | 10 ++++++++-- src/model/test/HeatPumpAirToWater_GTest.cpp | 13 ++++++++++--- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/src/model/HeatPumpAirToWater.cpp b/src/model/HeatPumpAirToWater.cpp index e5c06a526e..ee32d50cb5 100644 --- a/src/model/HeatPumpAirToWater.cpp +++ b/src/model/HeatPumpAirToWater.cpp @@ -318,6 +318,10 @@ namespace model { } 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; } @@ -329,14 +333,16 @@ namespace model { } bool HeatPumpAirToWater_Impl::setOperatingModeControlSchedule(Schedule& operatingModeControlSchedule) { - const bool result = setSchedule(OS_HeatPump_AirToWaterFields::OperatingModeControlScheduleName, "HeatPumpAirToWater", "Operating Mode Control", - 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) { diff --git a/src/model/test/HeatPumpAirToWater_GTest.cpp b/src/model/test/HeatPumpAirToWater_GTest.cpp index d8446711ab..91355e3dd9 100644 --- a/src/model/test/HeatPumpAirToWater_GTest.cpp +++ b/src/model/test/HeatPumpAirToWater_GTest.cpp @@ -42,11 +42,12 @@ TEST_F(ModelFixture, HeatPumpAirToWater_GettersSetters) { // Operating Mode Control Method: Required String // Ctor default EXPECT_EQ("Load", heatPumpAirToWater.operatingModeControlMethod()); - EXPECT_TRUE(heatPumpAirToWater.setOperatingModeControlMethod("ScheduledModes")); - EXPECT_EQ("ScheduledModes", 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("ScheduledModes", heatPumpAirToWater.operatingModeControlMethod()); + EXPECT_EQ("EMSControlled", heatPumpAirToWater.operatingModeControlMethod()); // Operating Mode Control Option for Multiple Unit: Required String // Ctor default @@ -59,10 +60,16 @@ TEST_F(ModelFixture, HeatPumpAirToWater_GettersSetters) { // 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 From 6a2062e7c06ecfc4f3f9ddfa32e9de06a93c52e0 Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Fri, 10 Oct 2025 00:24:23 +0200 Subject: [PATCH 34/45] Implement emsActuatorNames for the Top level wrapper --- src/model/HeatPumpAirToWater.cpp | 15 +++++++++++++++ src/model/HeatPumpAirToWater_Impl.hpp | 3 +++ 2 files changed, 18 insertions(+) diff --git a/src/model/HeatPumpAirToWater.cpp b/src/model/HeatPumpAirToWater.cpp index ee32d50cb5..756167953b 100644 --- a/src/model/HeatPumpAirToWater.cpp +++ b/src/model/HeatPumpAirToWater.cpp @@ -503,6 +503,21 @@ namespace model { 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) { diff --git a/src/model/HeatPumpAirToWater_Impl.hpp b/src/model/HeatPumpAirToWater_Impl.hpp index a0a42af7c9..797e858a87 100644 --- a/src/model/HeatPumpAirToWater_Impl.hpp +++ b/src/model/HeatPumpAirToWater_Impl.hpp @@ -68,6 +68,9 @@ namespace model { 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 */ //@{ From e607ce83a6d746eb0aef0e80eccc9eb590e3407e Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Fri, 10 Oct 2025 01:42:55 +0200 Subject: [PATCH 35/45] Write a reverse translator and quick RT roundtrip test. I did manual diffing of the OSM and they are A-OK --- src/energyplus/CMakeLists.txt | 1 + src/energyplus/ReverseTranslator.cpp | 4 + src/energyplus/ReverseTranslator.hpp | 4 + .../ReverseTranslateHeatPumpAirToWater.cpp | 601 ++++++++++++++++++ .../Test/HeatPumpAirToWater_GTest.cpp | 54 +- 5 files changed, 661 insertions(+), 3 deletions(-) create mode 100644 src/energyplus/ReverseTranslator/ReverseTranslateHeatPumpAirToWater.cpp diff --git a/src/energyplus/CMakeLists.txt b/src/energyplus/CMakeLists.txt index 1ddabb500f..ffd0a3fd4e 100644 --- a/src/energyplus/CMakeLists.txt +++ b/src/energyplus/CMakeLists.txt @@ -547,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 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 index 7143f96743..da689ccb96 100644 --- a/src/energyplus/Test/HeatPumpAirToWater_GTest.cpp +++ b/src/energyplus/Test/HeatPumpAirToWater_GTest.cpp @@ -10,13 +10,19 @@ #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" @@ -55,9 +61,6 @@ HeatPumpAirToWater makeAWHP(const Model& m) { HeatPumpAirToWater awhp(m); awhp.setName("AWHP"); - // Operating Mode Control Method: Required String - EXPECT_TRUE(awhp.setOperatingModeControlMethod("ScheduledModes")); - // Operating Mode Control Option for Multiple Unit: Required String EXPECT_TRUE(awhp.setOperatingModeControlOptionforMultipleUnit("CoolingPriority")); @@ -65,6 +68,8 @@ HeatPumpAirToWater makeAWHP(const Model& m) { 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)); @@ -759,3 +764,46 @@ TEST_F(EnergyPlusFixture, ForwardTranslator_HeatPumpAirToWater) { } } } + +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"); + } +} From 177fab2faae2f37bc7f3e871cc049d384de68890 Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Fri, 14 Nov 2025 00:00:56 +0100 Subject: [PATCH 36/45] Update SQL queries --- src/model/HeatPumpAirToWater.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/model/HeatPumpAirToWater.cpp b/src/model/HeatPumpAirToWater.cpp index 756167953b..cab72ce948 100644 --- a/src/model/HeatPumpAirToWater.cpp +++ b/src/model/HeatPumpAirToWater.cpp @@ -470,19 +470,19 @@ namespace model { } boost::optional HeatPumpAirToWater_Impl::autosizedRatedAirFlowRateinHeatingMode() const { - return getAutosizedValue("Design Size Source Side Volume Flow Rate", "m3/s", "HeatPump:AirToWater:Heating"); + return getAutosizedValue("Design Size Rated Air Volume Flow Rate in Heating Mode", "m3/s"); } boost::optional HeatPumpAirToWater_Impl::autosizedRatedWaterFlowRateinHeatingMode() const { - return getAutosizedValue("Design Size Load Side Volume Flow Rate", "m3/s", "HeatPump:AirToWater:Heating"); + return getAutosizedValue("Design Size Rated Water Volume Flow Rate in Heating Mode", "m3/s"); } boost::optional HeatPumpAirToWater_Impl::autosizedRatedAirFlowRateinCoolingMode() const { - return getAutosizedValue("Design Size Source Side Volume Flow Rate", "m3/s", "HeatPump:AirToWater:Cooling"); + return getAutosizedValue("Design Size Rated Air Volume Flow Rate in Cooling Mode", "m3/s"); } boost::optional HeatPumpAirToWater_Impl::autosizedRatedWaterFlowRateinCoolingMode() const { - return getAutosizedValue("Design Size Load Side Volume Flow Rate", "m3/s", "HeatPump:AirToWater:Cooling"); + return getAutosizedValue("Design Size Rated Water Volume Flow Rate in Cooling Mode", "m3/s"); } void HeatPumpAirToWater_Impl::autosize() {} From e09420bf46a525d7145bf71c0fd7240e69490a34 Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Fri, 14 Nov 2025 00:22:41 +0100 Subject: [PATCH 37/45] Implement top-level autosizedRatedHeating/CoolingCapacity --- src/model/HeatPumpAirToWater.cpp | 16 ++++++++++++++++ src/model/HeatPumpAirToWater.hpp | 2 ++ src/model/HeatPumpAirToWater_Impl.hpp | 2 ++ 3 files changed, 20 insertions(+) diff --git a/src/model/HeatPumpAirToWater.cpp b/src/model/HeatPumpAirToWater.cpp index cab72ce948..989e9fa214 100644 --- a/src/model/HeatPumpAirToWater.cpp +++ b/src/model/HeatPumpAirToWater.cpp @@ -485,6 +485,14 @@ namespace model { 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() {} @@ -760,6 +768,14 @@ namespace model { 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 diff --git a/src/model/HeatPumpAirToWater.hpp b/src/model/HeatPumpAirToWater.hpp index 92985f62ae..2e7fe7df8d 100644 --- a/src/model/HeatPumpAirToWater.hpp +++ b/src/model/HeatPumpAirToWater.hpp @@ -161,6 +161,8 @@ namespace model { boost::optional autosizedRatedWaterFlowRateinHeatingMode() const; boost::optional autosizedRatedAirFlowRateinCoolingMode() const; boost::optional autosizedRatedWaterFlowRateinCoolingMode() const; + boost::optional autosizedRatedHeatingCapacity() const; + boost::optional autosizedRatedCoolingCapacity() const; //@} protected: diff --git a/src/model/HeatPumpAirToWater_Impl.hpp b/src/model/HeatPumpAirToWater_Impl.hpp index 797e858a87..81f8bd4ad5 100644 --- a/src/model/HeatPumpAirToWater_Impl.hpp +++ b/src/model/HeatPumpAirToWater_Impl.hpp @@ -177,6 +177,8 @@ namespace model { boost::optional autosizedRatedWaterFlowRateinHeatingMode() const; boost::optional autosizedRatedAirFlowRateinCoolingMode() const; boost::optional autosizedRatedWaterFlowRateinCoolingMode() const; + boost::optional autosizedRatedHeatingCapacity() const; + boost::optional autosizedRatedCoolingCapacity() const; //@} protected: From 82c863130d687b5d11bb7824d7755195de631c4e Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Fri, 14 Nov 2025 00:39:24 +0100 Subject: [PATCH 38/45] Implement HeatPumpAirToWaterCooling/Heating convenience method for Implement top-level autosizedRatedHeating/CoolingCapacity --- src/model/HeatPumpAirToWaterCooling.cpp | 12 ++++++++++++ src/model/HeatPumpAirToWaterCooling.hpp | 1 + src/model/HeatPumpAirToWaterCooling_Impl.hpp | 2 ++ src/model/HeatPumpAirToWaterHeating.cpp | 12 ++++++++++++ src/model/HeatPumpAirToWaterHeating.hpp | 1 + src/model/HeatPumpAirToWaterHeating_Impl.hpp | 2 ++ 6 files changed, 30 insertions(+) diff --git a/src/model/HeatPumpAirToWaterCooling.cpp b/src/model/HeatPumpAirToWaterCooling.cpp index 431077f658..9dbbbce0b5 100644 --- a/src/model/HeatPumpAirToWaterCooling.cpp +++ b/src/model/HeatPumpAirToWaterCooling.cpp @@ -348,6 +348,14 @@ namespace model { 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(); @@ -714,6 +722,10 @@ namespace model { return getImpl()->heatPumpAirToWater(); } + boost::optional HeatPumpAirToWaterCooling::autosizedRatedCoolingCapacity() const { + return getImpl()->autosizedRatedCoolingCapacity(); + } + /// @cond HeatPumpAirToWaterCooling::HeatPumpAirToWaterCooling(std::shared_ptr impl) : StraightComponent(std::move(impl)) {} diff --git a/src/model/HeatPumpAirToWaterCooling.hpp b/src/model/HeatPumpAirToWaterCooling.hpp index a76285d867..c3a13270ee 100644 --- a/src/model/HeatPumpAirToWaterCooling.hpp +++ b/src/model/HeatPumpAirToWaterCooling.hpp @@ -172,6 +172,7 @@ namespace model { // Autosize methods boost::optional autosizedRatedAirFlowRate() const; boost::optional autosizedRatedWaterFlowRate() const; + boost::optional autosizedRatedCoolingCapacity() const; // Convenience method //@} protected: diff --git a/src/model/HeatPumpAirToWaterCooling_Impl.hpp b/src/model/HeatPumpAirToWaterCooling_Impl.hpp index e901f66e89..82ef07d228 100644 --- a/src/model/HeatPumpAirToWaterCooling_Impl.hpp +++ b/src/model/HeatPumpAirToWaterCooling_Impl.hpp @@ -165,6 +165,8 @@ namespace model { boost::optional autosizedRatedAirFlowRate() const; boost::optional autosizedRatedWaterFlowRate() const; + boost::optional autosizedRatedCoolingCapacity() const; // Convenience method + //@} protected: private: diff --git a/src/model/HeatPumpAirToWaterHeating.cpp b/src/model/HeatPumpAirToWaterHeating.cpp index 54c053060f..0a207a9b52 100644 --- a/src/model/HeatPumpAirToWaterHeating.cpp +++ b/src/model/HeatPumpAirToWaterHeating.cpp @@ -348,6 +348,14 @@ namespace model { 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(); @@ -715,6 +723,10 @@ namespace model { return getImpl()->heatPumpAirToWater(); } + boost::optional HeatPumpAirToWaterHeating::autosizedRatedHeatingCapacity() const { + return getImpl()->autosizedRatedHeatingCapacity(); + } + /// @cond HeatPumpAirToWaterHeating::HeatPumpAirToWaterHeating(std::shared_ptr impl) : StraightComponent(std::move(impl)) {} diff --git a/src/model/HeatPumpAirToWaterHeating.hpp b/src/model/HeatPumpAirToWaterHeating.hpp index 57ef162662..d9f6b0fb6f 100644 --- a/src/model/HeatPumpAirToWaterHeating.hpp +++ b/src/model/HeatPumpAirToWaterHeating.hpp @@ -172,6 +172,7 @@ namespace model { // Autosize methods boost::optional autosizedRatedAirFlowRate() const; boost::optional autosizedRatedWaterFlowRate() const; + boost::optional autosizedRatedHeatingCapacity() const; // Convenience method //@} protected: diff --git a/src/model/HeatPumpAirToWaterHeating_Impl.hpp b/src/model/HeatPumpAirToWaterHeating_Impl.hpp index 743f1edc21..af65ae3a66 100644 --- a/src/model/HeatPumpAirToWaterHeating_Impl.hpp +++ b/src/model/HeatPumpAirToWaterHeating_Impl.hpp @@ -165,6 +165,8 @@ namespace model { boost::optional autosizedRatedAirFlowRate() const; boost::optional autosizedRatedWaterFlowRate() const; + boost::optional autosizedRatedHeatingCapacity() const; // Convenience method + //@} protected: private: From 606fb81dd2b69850e96d6a788c3b87877b589f29 Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Fri, 14 Nov 2025 00:40:38 +0100 Subject: [PATCH 39/45] HeatPumpAirToWaterCooling(/Heating)-SpeedData: autosizedRatedCoolingCapacity: grab it from SQL only if it's the last speed (highest) --- .../HeatPumpAirToWaterCoolingSpeedData.cpp | 28 ++++++++++++++++++- .../HeatPumpAirToWaterHeatingSpeedData.cpp | 28 ++++++++++++++++++- 2 files changed, 54 insertions(+), 2 deletions(-) diff --git a/src/model/HeatPumpAirToWaterCoolingSpeedData.cpp b/src/model/HeatPumpAirToWaterCoolingSpeedData.cpp index 14b0b0b875..e9ef9af702 100644 --- a/src/model/HeatPumpAirToWaterCoolingSpeedData.cpp +++ b/src/model/HeatPumpAirToWaterCoolingSpeedData.cpp @@ -88,7 +88,33 @@ namespace model { } boost::optional HeatPumpAirToWaterCoolingSpeedData_Impl::autosizedRatedCoolingCapacity() { - return getAutosizedValue("TODO_CHECK_SQL Rated Cooling Capacity", "W"); + 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 { diff --git a/src/model/HeatPumpAirToWaterHeatingSpeedData.cpp b/src/model/HeatPumpAirToWaterHeatingSpeedData.cpp index 2850a014ae..30ba81122c 100644 --- a/src/model/HeatPumpAirToWaterHeatingSpeedData.cpp +++ b/src/model/HeatPumpAirToWaterHeatingSpeedData.cpp @@ -88,7 +88,33 @@ namespace model { } boost::optional HeatPumpAirToWaterHeatingSpeedData_Impl::autosizedRatedHeatingCapacity() { - return getAutosizedValue("TODO_CHECK_SQL Rated Heating Capacity", "W"); + 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 { From ba72104cbe3f165c022c6ad51668d71870481414 Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Fri, 14 Nov 2025 00:42:22 +0100 Subject: [PATCH 40/45] HeatPumpAirToWaterHeating/cooling: autosize / applySizingValues affects the LAST speed too The way Model::autosize/applySizingValues works is that it does it for HVACComponent, and the speed data is not one and we do not want to add an explicit case because none of the speeds should be autosized except the last (highest) one --- src/model/HeatPumpAirToWaterCooling.cpp | 11 +++++++++++ src/model/HeatPumpAirToWaterHeating.cpp | 11 +++++++++++ 2 files changed, 22 insertions(+) diff --git a/src/model/HeatPumpAirToWaterCooling.cpp b/src/model/HeatPumpAirToWaterCooling.cpp index 9dbbbce0b5..07bf5f2701 100644 --- a/src/model/HeatPumpAirToWaterCooling.cpp +++ b/src/model/HeatPumpAirToWaterCooling.cpp @@ -359,6 +359,10 @@ namespace model { 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() { @@ -369,6 +373,13 @@ namespace model { 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 { diff --git a/src/model/HeatPumpAirToWaterHeating.cpp b/src/model/HeatPumpAirToWaterHeating.cpp index 0a207a9b52..bcec694fd9 100644 --- a/src/model/HeatPumpAirToWaterHeating.cpp +++ b/src/model/HeatPumpAirToWaterHeating.cpp @@ -359,6 +359,10 @@ namespace model { 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() { @@ -369,6 +373,13 @@ namespace model { 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 { From 421296e4b66d8275607ef98ed0b1f08c23f1b01b Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Fri, 14 Nov 2025 00:54:18 +0100 Subject: [PATCH 41/45] Avoid "Unknown IddObjectType" warning --- src/energyplus/ForwardTranslator.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/energyplus/ForwardTranslator.cpp b/src/energyplus/ForwardTranslator.cpp index ad95ab9936..7d31bd94b9 100644 --- a/src/energyplus/ForwardTranslator.cpp +++ b/src/energyplus/ForwardTranslator.cpp @@ -1958,6 +1958,16 @@ namespace energyplus { 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); From 71a7c6215e0f065a7ba9eb562276826a5d319eb6 Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Thu, 13 Nov 2025 17:32:08 +0100 Subject: [PATCH 42/45] Temporary (?): Fill PlantLoop's Water Loop Type: hoping this field will be gone by 25.2.0 release (will revert then) --- .../ForwardTranslatePlantLoop.cpp | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/energyplus/ForwardTranslator/ForwardTranslatePlantLoop.cpp b/src/energyplus/ForwardTranslator/ForwardTranslatePlantLoop.cpp index 8f1b02795e..001531e1e2 100644 --- a/src/energyplus/ForwardTranslator/ForwardTranslatePlantLoop.cpp +++ b/src/energyplus/ForwardTranslator/ForwardTranslatePlantLoop.cpp @@ -98,7 +98,10 @@ #include "../../model/SetpointManagerScheduledDualSetpoint.hpp" #include "../../model/SetpointManagerScheduledDualSetpoint_Impl.hpp" #include "../../model/LifeCycleCost.hpp" + #include "../../utilities/idf/IdfExtensibleGroup.hpp" +#include "../../utilities/data/DataEnums.hpp" + #include #include #include @@ -582,6 +585,24 @@ namespace energyplus { idfObject.setString(PlantLoopFields::CommonPipeSimulation, s.get()); } + // Water Loop Type + // TODO JM 2025-11-13: This field should never have been added to 25.2.0, so it is purposefully NOT wrapped in the model namespace + // MJW is looking into removing it today, will assess later + ComponentType waterLoopType = plantLoop.componentType(); + if (waterLoopType == ComponentType::Heating) { + idfObject.setString(PlantLoopFields::WaterLoopType, "HotWater"); + } else if (waterLoopType == ComponentType::Cooling) { + idfObject.setString(PlantLoopFields::WaterLoopType, "ChilledWater"); + } else if (waterLoopType == ComponentType::None) { + idfObject.setString(PlantLoopFields::WaterLoopType, "None"); + } else if (waterLoopType == ComponentType::Both) { + if (!plantLoop.supplyComponents(IddObjectType::OS_HeatPump_AirToWater_Cooling).empty()) { + idfObject.setString(PlantLoopFields::WaterLoopType, "ChilledWater"); + } else if (!plantLoop.supplyComponents(IddObjectType::OS_HeatPump_AirToWater_Heating).empty()) { + idfObject.setString(PlantLoopFields::WaterLoopType, "HotWater"); + } + } + // Inlet/Outlet Nodes idfObject.setString(PlantLoopFields::PlantSideInletNodeName, plantLoop.supplyInletNode().name().get()); idfObject.setString(PlantLoopFields::PlantSideOutletNodeName, plantLoop.supplyOutletNode().name().get()); From a31240e01e429b6941da6e702d1d7bee48c27e30 Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Fri, 14 Nov 2025 12:51:00 +0100 Subject: [PATCH 43/45] Revert "Temporary (?): Fill PlantLoop's Water Loop Type: hoping this field will be gone by 25.2.0 release (will revert then)" This reverts commit 71a7c6215e0f065a7ba9eb562276826a5d319eb6. --- .../ForwardTranslatePlantLoop.cpp | 21 ------------------- 1 file changed, 21 deletions(-) diff --git a/src/energyplus/ForwardTranslator/ForwardTranslatePlantLoop.cpp b/src/energyplus/ForwardTranslator/ForwardTranslatePlantLoop.cpp index 001531e1e2..8f1b02795e 100644 --- a/src/energyplus/ForwardTranslator/ForwardTranslatePlantLoop.cpp +++ b/src/energyplus/ForwardTranslator/ForwardTranslatePlantLoop.cpp @@ -98,10 +98,7 @@ #include "../../model/SetpointManagerScheduledDualSetpoint.hpp" #include "../../model/SetpointManagerScheduledDualSetpoint_Impl.hpp" #include "../../model/LifeCycleCost.hpp" - #include "../../utilities/idf/IdfExtensibleGroup.hpp" -#include "../../utilities/data/DataEnums.hpp" - #include #include #include @@ -585,24 +582,6 @@ namespace energyplus { idfObject.setString(PlantLoopFields::CommonPipeSimulation, s.get()); } - // Water Loop Type - // TODO JM 2025-11-13: This field should never have been added to 25.2.0, so it is purposefully NOT wrapped in the model namespace - // MJW is looking into removing it today, will assess later - ComponentType waterLoopType = plantLoop.componentType(); - if (waterLoopType == ComponentType::Heating) { - idfObject.setString(PlantLoopFields::WaterLoopType, "HotWater"); - } else if (waterLoopType == ComponentType::Cooling) { - idfObject.setString(PlantLoopFields::WaterLoopType, "ChilledWater"); - } else if (waterLoopType == ComponentType::None) { - idfObject.setString(PlantLoopFields::WaterLoopType, "None"); - } else if (waterLoopType == ComponentType::Both) { - if (!plantLoop.supplyComponents(IddObjectType::OS_HeatPump_AirToWater_Cooling).empty()) { - idfObject.setString(PlantLoopFields::WaterLoopType, "ChilledWater"); - } else if (!plantLoop.supplyComponents(IddObjectType::OS_HeatPump_AirToWater_Heating).empty()) { - idfObject.setString(PlantLoopFields::WaterLoopType, "HotWater"); - } - } - // Inlet/Outlet Nodes idfObject.setString(PlantLoopFields::PlantSideInletNodeName, plantLoop.supplyInletNode().name().get()); idfObject.setString(PlantLoopFields::PlantSideOutletNodeName, plantLoop.supplyOutletNode().name().get()); From fb97ea33faaba9f8972c1d0122c720d7af94a4c7 Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Fri, 14 Nov 2025 12:51:39 +0100 Subject: [PATCH 44/45] Remove "Water Loop Type" from ProposedE+.idd https://github.com/NREL/EnergyPlus/pull/11332 --- resources/energyplus/ProposedEnergy+.idd | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) 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 From 595d4699836f6642d781bb53345ad5b9b35b6af0 Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Fri, 14 Nov 2025 14:39:29 +0100 Subject: [PATCH 45/45] size_t to unsigned conversion error on MSVC --- src/model/HeatPumpAirToWaterCooling.cpp | 2 +- src/model/HeatPumpAirToWaterHeating.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/model/HeatPumpAirToWaterCooling.cpp b/src/model/HeatPumpAirToWaterCooling.cpp index 07bf5f2701..0c8a6ef111 100644 --- a/src/model/HeatPumpAirToWaterCooling.cpp +++ b/src/model/HeatPumpAirToWaterCooling.cpp @@ -447,7 +447,7 @@ namespace model { const auto speedVector = speeds(); auto it = std::find_if(speedVector.cbegin(), speedVector.cend(), [&](const HeatPumpAirToWaterCoolingSpeedData& s) { return s == speed; }); if (it != speedVector.cend()) { - return std::distance(speedVector.cbegin(), it) + 1; // 1-indexed + return static_cast(std::distance(speedVector.cbegin(), it)) + 1; // 1-indexed } return boost::none; } diff --git a/src/model/HeatPumpAirToWaterHeating.cpp b/src/model/HeatPumpAirToWaterHeating.cpp index bcec694fd9..bd7ee2e147 100644 --- a/src/model/HeatPumpAirToWaterHeating.cpp +++ b/src/model/HeatPumpAirToWaterHeating.cpp @@ -447,7 +447,7 @@ namespace model { const auto speedVector = speeds(); auto it = std::find_if(speedVector.cbegin(), speedVector.cend(), [&](const HeatPumpAirToWaterHeatingSpeedData& s) { return s == speed; }); if (it != speedVector.cend()) { - return std::distance(speedVector.cbegin(), it) + 1; // 1-indexed + return static_cast(std::distance(speedVector.cbegin(), it)) + 1; // 1-indexed } return boost::none; }