Repository: NREL/OpenStudio (language: python)

Issue:
CoilWaterHeaterDesuperheater: Issue Error in ForwardTranslator if Heat Reclaim Efficiency is out of bounds
https://github.com/NREL/OpenStudio/blob/d6d7665b56030fb20146a096f7366ee1141d4d24/openstudiocore/src/model/CoilWaterHeatingDesuperheater.cpp#L544

E+ will throw with this default.

```
   ** Severe  ** Coil:WaterHeating:Desuperheater = COIL WATER HEATING DESUPERHEATER 1: Rated Heat Reclaim Recovery Efficiency must be > 0.0 and <= 0.3, Rated Heat Reclaim Recovery Efficiency = 0.800
   ** Severe  ** Coil:WaterHeating:Desuperheater = COIL WATER HEATING DESUPERHEATER 2: Rated Heat Reclaim Recovery Efficiency must be > 0.0 and <= 0.3, Rated Heat Reclaim Recovery Efficiency = 0.800
   **  Fatal  ** Errors found in getting Coil:WaterHeating:Desuperheater input. Preceding condition causes termination.
   ...Summary of Errors that led to program termination:
   ..... Reference severe error count=2
   ..... Last severe error=Coil:WaterHeating:Desuperheater = COIL WATER HEATING DESUPERHEATER 2: Rated Heat Reclaim Recovery Efficiency must be > 0.0 and <= 0.3, Rated Heat Reclaim Recovery Efficiency = 0.800
```


cf: https://github.com/NREL/OpenStudio-resources/pull/71#issuecomment-528782153
CoilWaterHeaterDesuperheater: Issue Error in ForwardTranslator if Heat Reclaim Efficiency is out of bounds
https://github.com/NREL/OpenStudio/blob/d6d7665b56030fb20146a096f7366ee1141d4d24/openstudiocore/src/model/CoilWaterHeatingDesuperheater.cpp#L544

E+ will throw with this default.

```
   ** Severe  ** Coil:WaterHeating:Desuperheater = COIL WATER HEATING DESUPERHEATER 1: Rated Heat Reclaim Recovery Efficiency must be > 0.0 and <= 0.3, Rated Heat Reclaim Recovery Efficiency = 0.800
   ** Severe  ** Coil:WaterHeating:Desuperheater = COIL WATER HEATING DESUPERHEATER 2: Rated Heat Reclaim Recovery Efficiency must be > 0.0 and <= 0.3, Rated Heat Reclaim Recovery Efficiency = 0.800
   **  Fatal  ** Errors found in getting Coil:WaterHeating:Desuperheater input. Preceding condition causes termination.
   ...Summary of Errors that led to program termination:
   ..... Reference severe error count=2
   ..... Last severe error=Coil:WaterHeating:Desuperheater = COIL WATER HEATING DESUPERHEATER 2: Rated Heat Reclaim Recovery Efficiency must be > 0.0 and <= 0.3, Rated Heat Reclaim Recovery Efficiency = 0.800
```


cf: https://github.com/NREL/OpenStudio-resources/pull/71#issuecomment-528782153


Discussion / hints:
This is only the case if it's NOT a refrigeration equipment:

https://github.com/jmarrec/EnergyPlus/blob/2f2115f490c523cfc2c22e506a795b48799b4562/src/EnergyPlus/WaterThermalTanks.cc#L1372:L1398

This checks has been there for at least 6 years in E+. Probably should issue an Error in the FT... but not much else we can do. Re-titling and labeling.
This is only the case if it's NOT a refrigeration equipment:

https://github.com/jmarrec/EnergyPlus/blob/2f2115f490c523cfc2c22e506a795b48799b4562/src/EnergyPlus/WaterThermalTanks.cc#L1372:L1398

This checks has been there for at least 6 years in E+. Probably should issue an Error in the FT... but not much else we can do. Re-titling and labeling.

Fix patch:
diff --git a/resources/model/OpenStudio.idd b/resources/model/OpenStudio.idd
index ad99daf1db9..367a5b187ff 100644
--- a/resources/model/OpenStudio.idd
+++ b/resources/model/OpenStudio.idd
@@ -18032,7 +18032,9 @@ OS:Coil:WaterHeating:Desuperheater,
   N2,  \field Rated Heat Reclaim Recovery Efficiency
        \type real
        \minimum> 0.0
+       \maximum 0.9
        \note Enter the fraction of waste heat reclaimed by the desuperheater water heating coil.
+       \note if not provided, uses a default
   N3,  \field Rated Inlet Water Temperature
        \required-field
        \type real
diff --git a/src/energyplus/Test/CoilWaterHeatingDesuperheater_GTest.cpp b/src/energyplus/Test/CoilWaterHeatingDesuperheater_GTest.cpp
index 102a67a4a70..ffba79a7bca 100644
--- a/src/energyplus/Test/CoilWaterHeatingDesuperheater_GTest.cpp
+++ b/src/energyplus/Test/CoilWaterHeatingDesuperheater_GTest.cpp
@@ -64,6 +64,7 @@ TEST_F(EnergyPlusFixture, ForwardTranslator_CoilWaterHeatingDesuperheater_DXCoil
 
   ScheduleConstant temperatureSetpointSchedule(m);
   CoilWaterHeatingDesuperheater desuperheater(m, temperatureSetpointSchedule);
+  desuperheater.setRatedHeatReclaimRecoveryEfficiency(0.25);
 
   // DX Coils that will be wrapped in CoilSystem:Cooling:DX
   std::vector<HVACComponent> testCoils = {CoilCoolingDXSingleSpeed(m), CoilCoolingDXTwoSpeed(m), CoilCoolingDXTwoStageWithHumidityControlMode(m),
@@ -95,6 +96,7 @@ TEST_F(EnergyPlusFixture, ForwardTranslator_CoilWaterHeatingDesuperheater_NonDX)
 
   ScheduleConstant temperatureSetpointSchedule(m);
   CoilWaterHeatingDesuperheater desuperheater(m, temperatureSetpointSchedule);
+  desuperheater.setRatedHeatReclaimRecoveryEfficiency(0.25);
 
   // Refrigeration stuff aren't HVACComponent but ParentObject
   std::vector<ModelObject> testCoils = {CoilCoolingWaterToAirHeatPumpEquationFit(m), RefrigerationCondenserAirCooled(m),
diff --git a/src/model/CoilWaterHeatingDesuperheater.cpp b/src/model/CoilWaterHeatingDesuperheater.cpp
index 623a6b5794b..c289c1b979e 100644
--- a/src/model/CoilWaterHeatingDesuperheater.cpp
+++ b/src/model/CoilWaterHeatingDesuperheater.cpp
@@ -98,12 +98,13 @@ namespace model {
     std::vector<ScheduleTypeKey> CoilWaterHeatingDesuperheater_Impl::getScheduleTypeKeys(const Schedule& schedule) const {
       std::vector<ScheduleTypeKey> result;
       UnsignedVector fieldIndices = getSourceIndices(schedule.handle());
-      UnsignedVector::const_iterator b(fieldIndices.begin()), e(fieldIndices.end());
+      UnsignedVector::const_iterator b(fieldIndices.begin());
+      UnsignedVector::const_iterator e(fieldIndices.end());
       if (std::find(b, e, OS_Coil_WaterHeating_DesuperheaterFields::AvailabilityScheduleName) != e) {
-        result.push_back(ScheduleTypeKey("CoilWaterHeatingDesuperheater", "Availability"));
+        result.emplace_back("CoilWaterHeatingDesuperheater", "Availability");
       }
       if (std::find(b, e, OS_Coil_WaterHeating_DesuperheaterFields::SetpointTemperatureScheduleName) != e) {
-        result.push_back(ScheduleTypeKey("CoilWaterHeatingDesuperheater", "Setpoint Temperature"));
+        result.emplace_back("CoilWaterHeatingDesuperheater", "Setpoint Temperature");
       }
       return result;
     }
@@ -117,7 +118,7 @@ namespace model {
     }
 
     ModelObject CoilWaterHeatingDesuperheater_Impl::clone(Model model) const {
-      CoilWaterHeatingDesuperheater modelObjectClone = ModelObject_Impl::clone(model).cast<CoilWaterHeatingDesuperheater>();
+      auto modelObjectClone = ModelObject_Impl::clone(model).cast<CoilWaterHeatingDesuperheater>();  // NOLINT
 
       modelObjectClone.resetHeatingSource();
 
@@ -125,9 +126,7 @@ namespace model {
     }
 
     std::vector<IddObjectType> CoilWaterHeatingDesuperheater_Impl::allowableChildTypes() const {
-      std::vector<IddObjectType> result;
-      result.push_back(IddObjectType::OS_Curve_Biquadratic);
-      return result;
+      return {IddObjectType::OS_Curve_Biquadratic};
     }
 
     std::vector<ModelObject> CoilWaterHeatingDesuperheater_Impl::children() const {
@@ -139,7 +138,7 @@ namespace model {
       return result;
     }
 
-    bool CoilWaterHeatingDesuperheater_Impl::addToNode(Node& node) {
+    bool CoilWaterHeatingDesuperheater_Impl::addToNode(Node& /*node*/) {
       return false;
     }
 
@@ -151,7 +150,7 @@ namespace model {
     boost::optional<ModelObject> CoilWaterHeatingDesuperheater_Impl::heatRejectionTarget() const {
       boost::optional<ModelObject> inletHeatRejectionTarget;
       boost::optional<ModelObject> outletHeatRejectionTarget;
-      StraightComponent desuperheater = this->getObject<ModelObject>().cast<StraightComponent>();
+      auto desuperheater = this->getObject<ModelObject>().cast<StraightComponent>();
       if (boost::optional<ModelObject> outletModelObject = desuperheater.outletModelObject()) {
         if (boost::optional<Node> outletNode = outletModelObject->optionalCast<Node>()) {
           if (boost::optional<ModelObject> heatRejectionTarget = outletNode->outletModelObject()) {
@@ -179,7 +178,9 @@ namespace model {
         validChoice = true;
       }
 
-      if (!validChoice) return false;
+      if (!validChoice) {
+        return false;
+      }
 
       if (boost::optional<WaterToWaterComponent> _heatRejectionTarget = heatRejectionTarget.optionalCast<WaterToWaterComponent>()) {
         Model _model = this->model();
@@ -269,22 +270,8 @@ namespace model {
         OS_Coil_WaterHeating_DesuperheaterFields::HeatReclaimEfficiencyFunctionofTemperatureCurveName);
     }
 
-    // boost::optional<HVACComponent> CoilWaterHeatingDesuperheater_Impl::tank() const {
-    //   return getObject<ModelObject>().getModelObjectTarget<HVACComponent>(OS_Coil_WaterHeating_DesuperheaterFields::TankName);
-    // boost::optional<HVACComponent> value = optionalTank();
-    // if (!value) {
-    //   LOG_AND_THROW(briefDescription() << " does not have an Tank attached.");
-    // }
-    // return value.get();
-    // }
-
     boost::optional<ModelObject> CoilWaterHeatingDesuperheater_Impl::heatingSource() const {
       return getObject<ModelObject>().getModelObjectTarget<ModelObject>(OS_Coil_WaterHeating_DesuperheaterFields::HeatingSourceName);
-      // boost::optional<ModelObject> value = optionalHeatingSource();
-      // if (!value) {
-      //   LOG_AND_THROW(briefDescription() << " does not have an Heating Source attached.");
-      // }
-      // return value.get();
     }
 
     double CoilWaterHeatingDesuperheater_Impl::waterFlowRate() const {
@@ -360,22 +347,6 @@ namespace model {
       OS_ASSERT(result);
     }
 
-    bool CoilWaterHeatingDesuperheater_Impl::setRatedHeatReclaimRecoveryEfficiency(boost::optional<double> ratedHeatReclaimRecoveryEfficiency) {
-      bool result(false);
-      if (ratedHeatReclaimRecoveryEfficiency) {
-        result = setDouble(OS_Coil_WaterHeating_DesuperheaterFields::RatedHeatReclaimRecoveryEfficiency, ratedHeatReclaimRecoveryEfficiency.get());
-      } else {
-        resetRatedHeatReclaimRecoveryEfficiency();
-        result = true;
-      }
-      return result;
-    }
-
-    void CoilWaterHeatingDesuperheater_Impl::resetRatedHeatReclaimRecoveryEfficiency() {
-      bool result = setString(OS_Coil_WaterHeating_DesuperheaterFields::RatedHeatReclaimRecoveryEfficiency, "");
-      OS_ASSERT(result);
-    }
-
     bool CoilWaterHeatingDesuperheater_Impl::setRatedInletWaterTemperature(double ratedInletWaterTemperature) {
       bool result = setDouble(OS_Coil_WaterHeating_DesuperheaterFields::RatedInletWaterTemperature, ratedInletWaterTemperature);
       OS_ASSERT(result);
@@ -395,16 +366,9 @@ namespace model {
       return result;
     }
 
-    bool CoilWaterHeatingDesuperheater_Impl::setHeatReclaimEfficiencyFunctionofTemperatureCurve(
-      const boost::optional<CurveBiquadratic>& curveBiquadratic) {
-      bool result(false);
-      if (curveBiquadratic) {
-        result =
-          setPointer(OS_Coil_WaterHeating_DesuperheaterFields::HeatReclaimEfficiencyFunctionofTemperatureCurveName, curveBiquadratic.get().handle());
-      } else {
-        resetHeatReclaimEfficiencyFunctionofTemperatureCurve();
-        result = true;
-      }
+    bool CoilWaterHeatingDesuperheater_Impl::setHeatReclaimEfficiencyFunctionofTemperatureCurve(const CurveBiquadratic& curveBiquadratic) {
+      bool result =
+        setPointer(OS_Coil_WaterHeating_DesuperheaterFields::HeatReclaimEfficiencyFunctionofTemperatureCurveName, curveBiquadratic.handle());
       return result;
     }
 
@@ -413,20 +377,64 @@ namespace model {
       OS_ASSERT(result);
     }
 
-    // bool CoilWaterHeatingDesuperheater_Impl::setTank(const HVACComponent& waterHeater) {
-    //   bool result = setPointer(OS_Coil_WaterHeating_DesuperheaterFields::TankName, waterHeater.handle());
-    //   if (result) addToWaterHeater(waterHeater);
-    //   return result;
-    // }
+    bool CoilWaterHeatingDesuperheater_Impl::setRatedHeatReclaimRecoveryEfficiency(double ratedHeatReclaimRecoveryEfficiency) {
+
+      if (auto heatingSource_ = heatingSource()) {
+
+        auto heatingSourceIddObjectType = heatingSource_->iddObjectType();
+        if ((heatingSourceIddObjectType == IddObjectType::OS_Refrigeration_Condenser_AirCooled)
+            || (heatingSourceIddObjectType == IddObjectType::OS_Refrigeration_Condenser_EvaporativeCooled)
+            || (heatingSourceIddObjectType == IddObjectType::OS_Refrigeration_Condenser_WaterCooled)) {
+          if (ratedHeatReclaimRecoveryEfficiency > 0.9) {
+            LOG(Error, briefDescription() << ": Rated Heat Reclaim Recovery Efficiency must be"
+                                          << " <= 0.9 when Heating Source Object Type = " << heatingSourceIddObjectType.valueDescription() << ".");
+            return false;
+          }
+        } else {
+          if (ratedHeatReclaimRecoveryEfficiency > 0.3) {
+            LOG(Error, briefDescription() << ": Rated Heat Reclaim Recovery Efficiency must be"
+                                          << " <= 0.3 when Heating Source Object Type = " << heatingSourceIddObjectType.valueDescription() << ".");
+            return false;
+          }
+        }
+      }
+
+      bool result = setDouble(OS_Coil_WaterHeating_DesuperheaterFields::RatedHeatReclaimRecoveryEfficiency, ratedHeatReclaimRecoveryEfficiency);
+      return result;
+    }
+
+    void CoilWaterHeatingDesuperheater_Impl::resetRatedHeatReclaimRecoveryEfficiency() {
+      bool result = setString(OS_Coil_WaterHeating_DesuperheaterFields::RatedHeatReclaimRecoveryEfficiency, "");
+      OS_ASSERT(result);
+    }
 
-    bool CoilWaterHeatingDesuperheater_Impl::setHeatingSource(const boost::optional<ModelObject>& heatingSource) {
-      bool result(false);
-      if (heatingSource) {
-        result = setPointer(OS_Coil_WaterHeating_DesuperheaterFields::HeatingSourceName, heatingSource.get().handle());
-      } else {
-        resetHeatingSource();
-        result = true;
+    bool CoilWaterHeatingDesuperheater_Impl::setHeatingSource(const ModelObject& heatingSource) {
+      bool result = setPointer(OS_Coil_WaterHeating_DesuperheaterFields::HeatingSourceName, heatingSource.handle());
+
+      if (boost::optional<double> ratedHeatReclaimRecoveryEfficiency_ = this->ratedHeatReclaimRecoveryEfficiency()) {
+        auto heatingSourceIddObjectType = heatingSource.iddObjectType();
+        if ((heatingSourceIddObjectType == IddObjectType::OS_Refrigeration_Condenser_AirCooled)
+            || (heatingSourceIddObjectType == IddObjectType::OS_Refrigeration_Condenser_EvaporativeCooled)
+            || (heatingSourceIddObjectType == IddObjectType::OS_Refrigeration_Condenser_WaterCooled)) {
+          if (ratedHeatReclaimRecoveryEfficiency_.get() > 0.9) {
+            LOG(Error, briefDescription() << ": Rated Heat Reclaim Recovery Efficiency must be"
+                                          << " <= 0.9 when Heating Source Object Type = " << heatingSourceIddObjectType.valueDescription()
+                                          << ". Resetting it to the default of 0.8.");
+            // setDouble to avoid a double check
+            // setRatedHeatReclaimRecoveryEfficiency(0.8);
+            setDouble(OS_Coil_WaterHeating_DesuperheaterFields::RatedHeatReclaimRecoveryEfficiency, 0.8);
+          }
+        } else {
+          if (ratedHeatReclaimRecoveryEfficiency_.get() > 0.3) {
+            LOG(Error, briefDescription() << ": Rated Heat Reclaim Recovery Efficiency must be"
+                                          << " <= 0.3 when Heating Source Object Type = " << heatingSourceIddObjectType.valueDescription()
+                                          << ". Resetting it to the default of 0.25.");
+            // setRatedHeatReclaimRecoveryEfficiency(0.25);
+            setDouble(OS_Coil_WaterHeating_DesuperheaterFields::RatedHeatReclaimRecoveryEfficiency, 0.25);
+          }
+        }
       }
+
       return result;
     }
 
@@ -484,12 +492,23 @@ namespace model {
       return getObject<ModelObject>().getModelObjectTarget<Schedule>(OS_Coil_WaterHeating_DesuperheaterFields::SetpointTemperatureScheduleName);
     }
 
+    // boost::optional<HVACComponent> CoilWaterHeatingDesuperheater_Impl::tank() const {
+    //   return getObject<ModelObject>().getModelObjectTarget<HVACComponent>(OS_Coil_WaterHeating_DesuperheaterFields::TankName);
+    // boost::optional<HVACComponent> value = optionalTank();
+    // if (!value) {
+    //   LOG_AND_THROW(briefDescription() << " does not have an Tank attached.");
+    // }
+    // return value.get();
+    // }
+
     // boost::optional<HVACComponent> CoilWaterHeatingDesuperheater_Impl::optionalTank() const {
     //   return getObject<ModelObject>().getModelObjectTarget<HVACComponent>(OS_Coil_WaterHeating_DesuperheaterFields::TankName);
     // }
 
-    // boost::optional<ModelObject> CoilWaterHeatingDesuperheater_Impl::optionalHeatingSource() const {
-    //   return getObject<ModelObject>().getModelObjectTarget<ModelObject>(OS_Coil_WaterHeating_DesuperheaterFields::HeatingSourceName);
+    // bool CoilWaterHeatingDesuperheater_Impl::setTank(const HVACComponent& waterHeater) {
+    //   bool result = setPointer(OS_Coil_WaterHeating_DesuperheaterFields::TankName, waterHeater.handle());
+    //   if (result) addToWaterHeater(waterHeater);
+    //   return result;
     // }
 
   }  // namespace detail
@@ -498,15 +517,20 @@ namespace model {
     : StraightComponent(CoilWaterHeatingDesuperheater::iddObjectType(), model) {
     OS_ASSERT(getImpl<detail::CoilWaterHeatingDesuperheater_Impl>());
 
+    // That's the only bit that's user-specified and could be expected to really throw
+    bool ok = setSetpointTemperatureSchedule(setpointTemperatureSchedule);
+    if (!ok) {
+      remove();
+      LOG_AND_THROW("Unable to set " << briefDescription() << "'s Setpoint Temperature Schedule to " << setpointTemperatureSchedule.briefDescription()
+                                     << ".");
+    }
+
+    // All of the below is expected to work so an OS_ASSERT is good enough
     Schedule availabilitySchedule = model.alwaysOnDiscreteSchedule();
-    bool ok = setAvailabilitySchedule(availabilitySchedule);
-    OS_ASSERT(ok);
-    ok = setSetpointTemperatureSchedule(setpointTemperatureSchedule);
+    ok = setAvailabilitySchedule(availabilitySchedule);
     OS_ASSERT(ok);
     ok = setDeadBandTemperatureDifference(5.0);
     OS_ASSERT(ok);
-    ok = setRatedHeatReclaimRecoveryEfficiency(0.8);
-    OS_ASSERT(ok);
     setRatedInletWaterTemperature(50.0);
     setRatedOutdoorAirTemperature(35.0);
     setMaximumInletWaterTemperatureforHeatReclaim(60.0);
@@ -523,7 +547,7 @@ namespace model {
   }
 
   IddObjectType CoilWaterHeatingDesuperheater::iddObjectType() {
-    return IddObjectType(IddObjectType::OS_Coil_WaterHeating_Desuperheater);
+    return {IddObjectType::OS_Coil_WaterHeating_Desuperheater};
   }
 
   boost::optional<ModelObject> CoilWaterHeatingDesuperheater::heatRejectionTarget() const {
diff --git a/src/model/CoilWaterHeatingDesuperheater_Impl.hpp b/src/model/CoilWaterHeatingDesuperheater_Impl.hpp
index e13661259d1..83c3fcaf3fc 100644
--- a/src/model/CoilWaterHeatingDesuperheater_Impl.hpp
+++ b/src/model/CoilWaterHeatingDesuperheater_Impl.hpp
@@ -145,7 +145,7 @@ namespace model {
 
       void resetDeadBandTemperatureDifference();
 
-      bool setRatedHeatReclaimRecoveryEfficiency(boost::optional<double> ratedHeatReclaimRecoveryEfficiency);
+      bool setRatedHeatReclaimRecoveryEfficiency(double ratedHeatReclaimRecoveryEfficiency);
 
       void resetRatedHeatReclaimRecoveryEfficiency();
 
@@ -155,13 +155,13 @@ namespace model {
 
       bool setMaximumInletWaterTemperatureforHeatReclaim(double maximumInletWaterTemperatureforHeatReclaim);
 
-      bool setHeatReclaimEfficiencyFunctionofTemperatureCurve(const boost::optional<CurveBiquadratic>& curveBiquadratic);
+      bool setHeatReclaimEfficiencyFunctionofTemperatureCurve(const CurveBiquadratic& curveBiquadratic);
 
       void resetHeatReclaimEfficiencyFunctionofTemperatureCurve();
 
       // bool setTank(const HVACComponent& waterHeater);
 
-      bool setHeatingSource(const boost::optional<ModelObject>& heatingSource);
+      bool setHeatingSource(const ModelObject& heatingSource);
 
       void resetHeatingSource();
 
@@ -197,7 +197,6 @@ namespace model {
       // objects should be returned as boost::optionals
       boost::optional<Schedule> optionalSetpointTemperatureSchedule() const;
       // boost::optional<HVACComponent> optionalTank() const;
-      // boost::optional<ModelObject> optionalHeatingSource() const;
     };
 
   }  // namespace detail


Test patch:
diff --git a/src/model/test/CoilWaterHeatingDesuperheater_GTest.cpp b/src/model/test/CoilWaterHeatingDesuperheater_GTest.cpp
index 38fc2cc458f..6e1019035f0 100644
--- a/src/model/test/CoilWaterHeatingDesuperheater_GTest.cpp
+++ b/src/model/test/CoilWaterHeatingDesuperheater_GTest.cpp
@@ -67,6 +67,13 @@ TEST_F(ModelFixture, CoilWaterHeatingDesuperheater_DefaultConstructor) {
       exit(0);
     },
     ::testing::ExitedWithCode(0), "");
+
+  // Passing an incompatible schedule: we expect the Ctor to throw, and no object to be created
+  Model model;
+  EXPECT_EQ(0, model.getConcreteModelObjects<CoilWaterHeatingDesuperheater>().size());
+  auto alwaysOn = model.alwaysOnDiscreteSchedule();
+  EXPECT_ANY_THROW(CoilWaterHeatingDesuperheater(model, alwaysOn));
+  EXPECT_EQ(0, model.getConcreteModelObjects<CoilWaterHeatingDesuperheater>().size());
 }
 
 TEST_F(ModelFixture, CoilWaterHeatingDesuperheater_Remove1) {
@@ -294,7 +301,7 @@ TEST_F(ModelFixture, CoilWaterHeatingDesuperheater_CloneModelWithDefaultData) {
   CoilWaterHeatingDesuperheater testObjectClone = testObject.clone(model).cast<CoilWaterHeatingDesuperheater>();
 
   EXPECT_DOUBLE_EQ(5.0, testObjectClone.deadBandTemperatureDifference());
-  EXPECT_DOUBLE_EQ(0.8, testObjectClone.ratedHeatReclaimRecoveryEfficiency().get());
+  EXPECT_FALSE(testObjectClone.ratedHeatReclaimRecoveryEfficiency());
   EXPECT_DOUBLE_EQ(50.0, testObjectClone.ratedInletWaterTemperature());
   EXPECT_DOUBLE_EQ(35.0, testObjectClone.ratedOutdoorAirTemperature());
   EXPECT_DOUBLE_EQ(60.0, testObjectClone.maximumInletWaterTemperatureforHeatReclaim());
@@ -314,7 +321,7 @@ TEST_F(ModelFixture, CoilWaterHeatingDesuperheater_CloneModelWithCustomData) {
   CurveBiquadratic curve = CurveBiquadratic(model);
 
   EXPECT_TRUE(testObject.setDeadBandTemperatureDifference(20.0));
-  EXPECT_TRUE(testObject.setRatedHeatReclaimRecoveryEfficiency(0.99));
+  EXPECT_TRUE(testObject.setRatedHeatReclaimRecoveryEfficiency(0.89));
   EXPECT_TRUE(testObject.setRatedInletWaterTemperature(999.0));
   EXPECT_TRUE(testObject.setRatedOutdoorAirTemperature(999.0));
   EXPECT_TRUE(testObject.setMaximumInletWaterTemperatureforHeatReclaim(999.0));
@@ -330,7 +337,7 @@ TEST_F(ModelFixture, CoilWaterHeatingDesuperheater_CloneModelWithCustomData) {
   CoilWaterHeatingDesuperheater testObjectClone = testObject.clone(model).cast<CoilWaterHeatingDesuperheater>();
 
   EXPECT_DOUBLE_EQ(20.0, testObjectClone.deadBandTemperatureDifference());
-  EXPECT_DOUBLE_EQ(0.99, testObjectClone.ratedHeatReclaimRecoveryEfficiency().get());
+  EXPECT_DOUBLE_EQ(0.89, testObjectClone.ratedHeatReclaimRecoveryEfficiency().get());
   EXPECT_DOUBLE_EQ(999.0, testObjectClone.ratedInletWaterTemperature());
   EXPECT_DOUBLE_EQ(999.0, testObjectClone.ratedOutdoorAirTemperature());
   EXPECT_DOUBLE_EQ(999.0, testObjectClone.maximumInletWaterTemperatureforHeatReclaim());
@@ -354,7 +361,7 @@ TEST_F(ModelFixture, CoilWaterHeatingDesuperheater_CloneTwoModelWithCustomData)
   CurveBiquadratic curve = CurveBiquadratic(model);
 
   EXPECT_TRUE(testObject.setDeadBandTemperatureDifference(20.0));
-  EXPECT_TRUE(testObject.setRatedHeatReclaimRecoveryEfficiency(0.99));
+  EXPECT_TRUE(testObject.setRatedHeatReclaimRecoveryEfficiency(0.89));
   EXPECT_TRUE(testObject.setRatedInletWaterTemperature(999.0));
   EXPECT_TRUE(testObject.setRatedOutdoorAirTemperature(999.0));
   EXPECT_TRUE(testObject.setMaximumInletWaterTemperatureforHeatReclaim(999.0));
@@ -373,7 +380,7 @@ TEST_F(ModelFixture, CoilWaterHeatingDesuperheater_CloneTwoModelWithCustomData)
   CoilWaterHeatingDesuperheater testObjectClone2 = testObject.clone(model2).cast<CoilWaterHeatingDesuperheater>();
 
   EXPECT_DOUBLE_EQ(20.0, testObjectClone2.deadBandTemperatureDifference());
-  EXPECT_DOUBLE_EQ(0.99, testObjectClone2.ratedHeatReclaimRecoveryEfficiency().get());
+  EXPECT_DOUBLE_EQ(0.89, testObjectClone2.ratedHeatReclaimRecoveryEfficiency().get());
   EXPECT_DOUBLE_EQ(999.0, testObjectClone2.ratedInletWaterTemperature());
   EXPECT_DOUBLE_EQ(999.0, testObjectClone2.ratedOutdoorAirTemperature());
   EXPECT_DOUBLE_EQ(999.0, testObjectClone2.maximumInletWaterTemperatureforHeatReclaim());
@@ -389,3 +396,39 @@ TEST_F(ModelFixture, CoilWaterHeatingDesuperheater_CloneTwoModelWithCustomData)
   EXPECT_NE(testObjectClone2, testObjectClone);
   EXPECT_NE(testObjectClone, testObject);
 }
+
+TEST_F(ModelFixture, CoilWaterHeatingDesuperheater_HeatReclaimEfficiency) {
+  Model model;
+  ScheduleCompact schedule = ScheduleCompact(model);
+  CoilWaterHeatingDesuperheater c = CoilWaterHeatingDesuperheater(model, schedule);
+
+  // Ctor doesn't initialize it
+  EXPECT_FALSE(c.ratedHeatReclaimRecoveryEfficiency());
+
+  // No heating source type yet: max Rated Heat Reclaim Efficiency is per IDD at 0.9
+  EXPECT_FALSE(c.heatingSource());
+  EXPECT_TRUE(c.setRatedHeatReclaimRecoveryEfficiency(0.9));
+  EXPECT_EQ(0.9, c.ratedHeatReclaimRecoveryEfficiency().get());
+  EXPECT_FALSE(c.setRatedHeatReclaimRecoveryEfficiency(0.95));
+  EXPECT_EQ(0.90, c.ratedHeatReclaimRecoveryEfficiency().get());
+
+  // Adding a Heating Source of Refrigeration type: limit should be 0.9
+  RefrigerationCondenserAirCooled condenser = RefrigerationCondenserAirCooled(model);
+  EXPECT_TRUE(c.setHeatingSource(condenser));
+  EXPECT_EQ(0.90, c.ratedHeatReclaimRecoveryEfficiency().get());
+  // It should also enforce on the setter
+  EXPECT_TRUE(c.setRatedHeatReclaimRecoveryEfficiency(0.85));
+  EXPECT_EQ(0.85, c.ratedHeatReclaimRecoveryEfficiency().get());
+  EXPECT_FALSE(c.setRatedHeatReclaimRecoveryEfficiency(0.95));
+  EXPECT_EQ(0.85, c.ratedHeatReclaimRecoveryEfficiency().get());
+
+  // Now add a Heating Source of non refrigeration type: limit should be 0.3, and it should reset to default of 0.25
+  CoilCoolingDXMultiSpeed dxCoil = CoilCoolingDXMultiSpeed(model);
+  EXPECT_TRUE(c.setHeatingSource(dxCoil));
+  EXPECT_EQ(0.25, c.ratedHeatReclaimRecoveryEfficiency().get()) << c;
+  // It should also enforce on the setter
+  EXPECT_TRUE(c.setRatedHeatReclaimRecoveryEfficiency(0.3));
+  EXPECT_EQ(0.3, c.ratedHeatReclaimRecoveryEfficiency().get());
+  EXPECT_FALSE(c.setRatedHeatReclaimRecoveryEfficiency(0.35));
+  EXPECT_EQ(0.3, c.ratedHeatReclaimRecoveryEfficiency().get());
+}