From 041adbd8ea0e52c83e21a9136d50c4b9be86b62d Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Mon, 5 May 2025 11:44:15 +0200 Subject: [PATCH 01/16] Make the coolingCoil/companion return a HVACComponent + restrict the companion to CoilCoolingWater per IDD/IO ref CoilSystem:Cooling:Water:HeatExchangerAssisted isn't WaterToAirComponent --- ...ForwardTranslateCoilSystemCoolingWater.cpp | 2 +- src/model/CoilCoolingWater.cpp | 2 +- src/model/CoilSystemCoolingWater.cpp | 55 +++++++++++-------- src/model/CoilSystemCoolingWater.hpp | 14 +++-- src/model/CoilSystemCoolingWater_Impl.hpp | 14 ++--- 5 files changed, 48 insertions(+), 39 deletions(-) diff --git a/src/energyplus/ForwardTranslator/ForwardTranslateCoilSystemCoolingWater.cpp b/src/energyplus/ForwardTranslator/ForwardTranslateCoilSystemCoolingWater.cpp index b2ec41fadf8..84d0ca4dfb1 100644 --- a/src/energyplus/ForwardTranslator/ForwardTranslateCoilSystemCoolingWater.cpp +++ b/src/energyplus/ForwardTranslator/ForwardTranslateCoilSystemCoolingWater.cpp @@ -177,7 +177,7 @@ namespace energyplus { idfObject.setDouble(CoilSystem_Cooling_WaterFields::MinimumWaterLoopTemperatureForHeatRecovery, minimumWaterLoopTemperatureForHeatRecovery); // Companion Coil Used For Heat Recovery: Optional Object - if (boost::optional companionCoilUsedForHeatRecovery_ = modelObject.companionCoilUsedForHeatRecovery()) { + if (boost::optional companionCoilUsedForHeatRecovery_ = modelObject.companionCoilUsedForHeatRecovery()) { if (boost::optional wo_ = translateAndMapModelObject(companionCoilUsedForHeatRecovery_.get())) { idfObject.setString(CoilSystem_Cooling_WaterFields::CompanionCoilUsedForHeatRecovery, wo_->nameString()); if (wo_->iddObject().type() == IddObjectType::Coil_Cooling_Water) { diff --git a/src/model/CoilCoolingWater.cpp b/src/model/CoilCoolingWater.cpp index afe42feb17f..8576570f2f8 100644 --- a/src/model/CoilCoolingWater.cpp +++ b/src/model/CoilCoolingWater.cpp @@ -389,7 +389,7 @@ namespace model { if (coilSystem.coolingCoil().handle() == handle()) { return coilSystem; } - if (boost::optional companionCoilUsedForHeatRecovery = coilSystem.companionCoilUsedForHeatRecovery()) { + if (boost::optional companionCoilUsedForHeatRecovery = coilSystem.companionCoilUsedForHeatRecovery()) { if (companionCoilUsedForHeatRecovery->handle() == this->handle()) { return coilSystem; } diff --git a/src/model/CoilSystemCoolingWater.cpp b/src/model/CoilSystemCoolingWater.cpp index 2b200413ff2..359aef8b051 100644 --- a/src/model/CoilSystemCoolingWater.cpp +++ b/src/model/CoilSystemCoolingWater.cpp @@ -8,8 +8,8 @@ #include "Schedule.hpp" #include "Schedule_Impl.hpp" -#include "WaterToAirComponent.hpp" -#include "WaterToAirComponent_Impl.hpp" +#include "HVACComponent.hpp" +#include "HVACComponent_Impl.hpp" #include "CoilCoolingWater.hpp" #include "CoilCoolingWater_Impl.hpp" #include "Model.hpp" @@ -87,10 +87,10 @@ namespace model { std::vector CoilSystemCoolingWater_Impl::children() const { std::vector result; - if (OptionalWaterToAirComponent intermediate = optionalCoolingCoil()) { + if (OptionalHVACComponent intermediate = optionalCoolingCoil()) { result.push_back(*intermediate); } - if (OptionalWaterToAirComponent intermediate = optionalCompanionCoilUsedForHeatRecovery()) { + if (OptionalHVACComponent intermediate = optionalCompanionCoilUsedForHeatRecovery()) { result.push_back(*intermediate); } @@ -100,11 +100,11 @@ namespace model { ModelObject CoilSystemCoolingWater_Impl::clone(Model model) const { auto coilSystemClone = StraightComponent_Impl::clone(model).cast(); - if (OptionalWaterToAirComponent intermediate = optionalCoolingCoil()) { - coilSystemClone.setCoolingCoil(intermediate->clone(model).cast()); + if (OptionalHVACComponent intermediate = optionalCoolingCoil()) { + coilSystemClone.setCoolingCoil(intermediate->clone(model).cast()); } - if (OptionalWaterToAirComponent intermediate = optionalCompanionCoilUsedForHeatRecovery()) { - coilSystemClone.setCompanionCoilUsedForHeatRecovery(intermediate->clone(model).cast()); + if (OptionalHVACComponent intermediate = optionalCompanionCoilUsedForHeatRecovery()) { + coilSystemClone.setCompanionCoilUsedForHeatRecovery(intermediate->clone(model).cast()); } return std::move(coilSystemClone); @@ -198,8 +198,8 @@ namespace model { return value.get(); } - WaterToAirComponent CoilSystemCoolingWater_Impl::coolingCoil() const { - boost::optional value = optionalCoolingCoil(); + HVACComponent CoilSystemCoolingWater_Impl::coolingCoil() const { + boost::optional value = optionalCoolingCoil(); if (!value) { LOG_AND_THROW(briefDescription() << " does not have an Cooling Coil attached."); } @@ -236,7 +236,7 @@ namespace model { return value.get(); } - boost::optional CoilSystemCoolingWater_Impl::companionCoilUsedForHeatRecovery() const { + boost::optional CoilSystemCoolingWater_Impl::companionCoilUsedForHeatRecovery() const { return optionalCompanionCoilUsedForHeatRecovery(); } @@ -246,7 +246,7 @@ namespace model { return result; } - bool CoilSystemCoolingWater_Impl::setCoolingCoil(const WaterToAirComponent& coolingCoil) { + bool CoilSystemCoolingWater_Impl::setCoolingCoil(const HVACComponent& coolingCoil) { const bool result = setPointer(OS_CoilSystem_Cooling_WaterFields::CoolingCoil, coolingCoil.handle()); return result; } @@ -286,9 +286,16 @@ namespace model { return result; } - bool CoilSystemCoolingWater_Impl::setCompanionCoilUsedForHeatRecovery(const WaterToAirComponent& companionCoilUsedForHeatRecovery) { - const bool result = setPointer(OS_CoilSystem_Cooling_WaterFields::CompanionCoilUsedForHeatRecovery, companionCoilUsedForHeatRecovery.handle()); - return result; + bool CoilSystemCoolingWater_Impl::setCompanionCoilUsedForHeatRecovery(const HVACComponent& companionCoilUsedForHeatRecovery) { + if (companionCoilUsedForHeatRecovery.iddObjectType() == IddObjectType::OS_Coil_Cooling_Water) { + const bool result = + setPointer(OS_CoilSystem_Cooling_WaterFields::CompanionCoilUsedForHeatRecovery, companionCoilUsedForHeatRecovery.handle()); + return result; + } else { + LOG(Warn, "Invalid Companion Coil Used For Heat Recovery Type (expected CoilCoolingWater, not '" + << companionCoilUsedForHeatRecovery.iddObjectType().valueName() << "') for " << briefDescription()); + return false; + } } void CoilSystemCoolingWater_Impl::resetCompanionCoilUsedForHeatRecovery() { @@ -300,12 +307,12 @@ namespace model { return getObject().getModelObjectTarget(OS_CoilSystem_Cooling_WaterFields::AvailabilityScheduleName); } - boost::optional CoilSystemCoolingWater_Impl::optionalCoolingCoil() const { - return getObject().getModelObjectTarget(OS_CoilSystem_Cooling_WaterFields::CoolingCoil); + boost::optional CoilSystemCoolingWater_Impl::optionalCoolingCoil() const { + return getObject().getModelObjectTarget(OS_CoilSystem_Cooling_WaterFields::CoolingCoil); } - boost::optional CoilSystemCoolingWater_Impl::optionalCompanionCoilUsedForHeatRecovery() const { - return getObject().getModelObjectTarget(OS_CoilSystem_Cooling_WaterFields::CompanionCoilUsedForHeatRecovery); + boost::optional CoilSystemCoolingWater_Impl::optionalCompanionCoilUsedForHeatRecovery() const { + return getObject().getModelObjectTarget(OS_CoilSystem_Cooling_WaterFields::CompanionCoilUsedForHeatRecovery); } } // namespace detail @@ -335,7 +342,7 @@ namespace model { OS_ASSERT(ok); } - CoilSystemCoolingWater::CoilSystemCoolingWater(const Model& model, const WaterToAirComponent& coolingCoil) + CoilSystemCoolingWater::CoilSystemCoolingWater(const Model& model, const HVACComponent& coolingCoil) : StraightComponent(CoilSystemCoolingWater::iddObjectType(), model) { OS_ASSERT(getImpl()); @@ -372,7 +379,7 @@ namespace model { return getImpl()->availabilitySchedule(); } - WaterToAirComponent CoilSystemCoolingWater::coolingCoil() const { + HVACComponent CoilSystemCoolingWater::coolingCoil() const { return getImpl()->coolingCoil(); } @@ -400,7 +407,7 @@ namespace model { return getImpl()->minimumWaterLoopTemperatureForHeatRecovery(); } - boost::optional CoilSystemCoolingWater::companionCoilUsedForHeatRecovery() const { + boost::optional CoilSystemCoolingWater::companionCoilUsedForHeatRecovery() const { return getImpl()->companionCoilUsedForHeatRecovery(); } @@ -408,7 +415,7 @@ namespace model { return getImpl()->setAvailabilitySchedule(schedule); } - bool CoilSystemCoolingWater::setCoolingCoil(const WaterToAirComponent& coolingCoil) { + bool CoilSystemCoolingWater::setCoolingCoil(const HVACComponent& coolingCoil) { return getImpl()->setCoolingCoil(coolingCoil); } @@ -436,7 +443,7 @@ namespace model { return getImpl()->setMinimumWaterLoopTemperatureForHeatRecovery(minimumWaterLoopTemperatureForHeatRecovery); } - bool CoilSystemCoolingWater::setCompanionCoilUsedForHeatRecovery(const WaterToAirComponent& companionCoilUsedForHeatRecovery) { + bool CoilSystemCoolingWater::setCompanionCoilUsedForHeatRecovery(const HVACComponent& companionCoilUsedForHeatRecovery) { return getImpl()->setCompanionCoilUsedForHeatRecovery(companionCoilUsedForHeatRecovery); } diff --git a/src/model/CoilSystemCoolingWater.hpp b/src/model/CoilSystemCoolingWater.hpp index e22211ecbd1..2d2b29ce9df 100644 --- a/src/model/CoilSystemCoolingWater.hpp +++ b/src/model/CoilSystemCoolingWater.hpp @@ -14,7 +14,7 @@ namespace openstudio { namespace model { class Schedule; - class WaterToAirComponent; + class HVACComponent; namespace detail { @@ -31,7 +31,7 @@ namespace model { explicit CoilSystemCoolingWater(const Model& model); - explicit CoilSystemCoolingWater(const Model& model, const WaterToAirComponent& coolingCoil); + explicit CoilSystemCoolingWater(const Model& model, const HVACComponent& coolingCoil); virtual ~CoilSystemCoolingWater() = default; // Default the copy and move operators because the virtual dtor is explicit @@ -51,7 +51,7 @@ namespace model { Schedule availabilitySchedule() const; - WaterToAirComponent coolingCoil() const; + HVACComponent coolingCoil() const; std::string dehumidificationControlType() const; @@ -65,7 +65,7 @@ namespace model { double minimumWaterLoopTemperatureForHeatRecovery() const; - boost::optional companionCoilUsedForHeatRecovery() const; + boost::optional companionCoilUsedForHeatRecovery() const; //@} /** @name Setters */ @@ -73,7 +73,8 @@ namespace model { bool setAvailabilitySchedule(Schedule& schedule); - bool setCoolingCoil(const WaterToAirComponent& coolingCoil); + // CoilCoolingWater or CoilSystemCoolingWaterHeatExchangerAssisted + bool setCoolingCoil(const HVACComponent& coolingCoil); bool setDehumidificationControlType(const std::string& dehumidificationControlType); @@ -87,7 +88,8 @@ namespace model { bool setMinimumWaterLoopTemperatureForHeatRecovery(double minimumWaterLoopTemperatureForHeatRecovery); - bool setCompanionCoilUsedForHeatRecovery(const WaterToAirComponent& companionCoilUsedForHeatRecovery); + // Only CoilCoolingWater is supported by E+ as a companion coil right now + bool setCompanionCoilUsedForHeatRecovery(const HVACComponent& companionCoilUsedForHeatRecovery); void resetCompanionCoilUsedForHeatRecovery(); diff --git a/src/model/CoilSystemCoolingWater_Impl.hpp b/src/model/CoilSystemCoolingWater_Impl.hpp index 860dd9dc5f2..5b583051ddb 100644 --- a/src/model/CoilSystemCoolingWater_Impl.hpp +++ b/src/model/CoilSystemCoolingWater_Impl.hpp @@ -13,7 +13,7 @@ namespace openstudio { namespace model { class Schedule; - class WaterToAirComponent; + class HVACComponent; namespace detail { @@ -70,7 +70,7 @@ namespace model { Schedule availabilitySchedule() const; - WaterToAirComponent coolingCoil() const; + HVACComponent coolingCoil() const; std::string dehumidificationControlType() const; @@ -84,7 +84,7 @@ namespace model { double minimumWaterLoopTemperatureForHeatRecovery() const; - boost::optional companionCoilUsedForHeatRecovery() const; + boost::optional companionCoilUsedForHeatRecovery() const; //@} /** @name Setters */ @@ -92,7 +92,7 @@ namespace model { bool setAvailabilitySchedule(Schedule& schedule); - bool setCoolingCoil(const WaterToAirComponent& coolingCoil); + bool setCoolingCoil(const HVACComponent& coolingCoil); bool setDehumidificationControlType(const std::string& dehumidificationControlType); @@ -106,7 +106,7 @@ namespace model { bool setMinimumWaterLoopTemperatureForHeatRecovery(double minimumWaterLoopTemperatureForHeatRecovery); - bool setCompanionCoilUsedForHeatRecovery(const WaterToAirComponent& companionCoilUsedForHeatRecovery); + bool setCompanionCoilUsedForHeatRecovery(const HVACComponent& companionCoilUsedForHeatRecovery); void resetCompanionCoilUsedForHeatRecovery(); @@ -120,8 +120,8 @@ namespace model { REGISTER_LOGGER("openstudio.model.CoilSystemCoolingWater"); boost::optional optionalAvailabilitySchedule() const; - boost::optional optionalCoolingCoil() const; - boost::optional optionalCompanionCoilUsedForHeatRecovery() const; + boost::optional optionalCoolingCoil() const; + boost::optional optionalCompanionCoilUsedForHeatRecovery() const; }; } // namespace detail From 310a14f7df9ccc215ea5e0a67a649c8924aab86f Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Mon, 5 May 2025 11:45:02 +0200 Subject: [PATCH 02/16] Improve model gtests --- .../test/CoilSystemCoolingWater_GTest.cpp | 90 ++++++++++++++++--- 1 file changed, 76 insertions(+), 14 deletions(-) diff --git a/src/model/test/CoilSystemCoolingWater_GTest.cpp b/src/model/test/CoilSystemCoolingWater_GTest.cpp index 7c1aa4b661a..c1b3241c63c 100644 --- a/src/model/test/CoilSystemCoolingWater_GTest.cpp +++ b/src/model/test/CoilSystemCoolingWater_GTest.cpp @@ -7,13 +7,18 @@ #include "../CoilSystemCoolingWater.hpp" #include "../CoilSystemCoolingWater_Impl.hpp" -#include "../Schedule.hpp" -#include "../Schedule_Impl.hpp" + #include "../CoilCoolingWater.hpp" #include "../CoilCoolingWater_Impl.hpp" +#include "../CoilSystemCoolingWaterHeatExchangerAssisted.hpp" +#include "../CoilSystemCoolingWaterHeatExchangerAssisted_Impl.hpp" #include "../AirLoopHVAC.hpp" -#include "../PlantLoop.hpp" +#include "../ChillerElectricEIR.hpp" #include "../Node.hpp" +#include "../PlantLoop.hpp" +#include "../Schedule.hpp" +#include "../ScheduleConstant.hpp" +#include "../ScheduleTypeLimits.hpp" using namespace openstudio; using namespace openstudio::model; @@ -26,35 +31,70 @@ TEST_F(ModelFixture, CoilSystemCoolingWater_GettersSetters) { coilSystemCoolingWater.setName("My CoilSystemCoolingWater"); // Availability Schedule Name: Required Object + // Ctor default Schedule availabilitySchedule = m.alwaysOnDiscreteSchedule(); - EXPECT_TRUE(coilSystemCoolingWater.setAvailabilitySchedule(availabilitySchedule)); EXPECT_EQ(availabilitySchedule, coilSystemCoolingWater.availabilitySchedule()); + // set + ScheduleConstant availSch(m); + EXPECT_TRUE(coilSystemCoolingWater.setAvailabilitySchedule(availSch)); + EXPECT_EQ(availSch, coilSystemCoolingWater.availabilitySchedule()); + // The Schedule Type Registry should have done it's thing and assigned a scheduleTypeLimits + ASSERT_TRUE(availSch.scheduleTypeLimits()); + { + auto sch_type_lim = availSch.scheduleTypeLimits().get(); + EXPECT_EQ("OnOff", sch_type_lim.nameString()); + ASSERT_TRUE(sch_type_lim.lowerLimitValue()); + EXPECT_EQ(0.0, sch_type_lim.lowerLimitValue().get()); + ASSERT_TRUE(sch_type_lim.upperLimitValue()); + EXPECT_EQ(1.0, sch_type_lim.upperLimitValue().get()); + ASSERT_TRUE(sch_type_lim.numericType()); + EXPECT_EQ("Discrete", sch_type_lim.numericType().get()); + EXPECT_EQ("Availability", sch_type_lim.unitType()); + } // Cooling Coil: Required Object + // Ctor instantiates a CoilCoolingWater + EXPECT_TRUE(coilSystemCoolingWater.coolingCoil().optionalCast()); CoilCoolingWater coolingCoil(m); EXPECT_TRUE(coilSystemCoolingWater.setCoolingCoil(coolingCoil)); EXPECT_EQ(coolingCoil, coilSystemCoolingWater.coolingCoil()); + CoilSystemCoolingWaterHeatExchangerAssisted coilSystemCoolingWaterHeatExchangerAssisted(m); + EXPECT_TRUE(coilSystemCoolingWater.setCoolingCoil(coilSystemCoolingWaterHeatExchangerAssisted)); + EXPECT_EQ(coilSystemCoolingWaterHeatExchangerAssisted, coilSystemCoolingWater.coolingCoil()); + // Dehumidification Control Type: Required String - EXPECT_TRUE(coilSystemCoolingWater.setDehumidificationControlType("None")); + // Ctor default EXPECT_EQ("None", coilSystemCoolingWater.dehumidificationControlType()); + // set + EXPECT_TRUE(coilSystemCoolingWater.setDehumidificationControlType("CoolReheat")); + EXPECT_EQ("CoolReheat", coilSystemCoolingWater.dehumidificationControlType()); // Bad Value EXPECT_FALSE(coilSystemCoolingWater.setDehumidificationControlType("BADENUM")); - EXPECT_EQ("None", coilSystemCoolingWater.dehumidificationControlType()); + EXPECT_EQ("CoolReheat", coilSystemCoolingWater.dehumidificationControlType()); // Run on Sensible Load: Required Boolean - EXPECT_TRUE(coilSystemCoolingWater.setRunonSensibleLoad(true)); + // Ctor default EXPECT_TRUE(coilSystemCoolingWater.runonSensibleLoad()); + // set EXPECT_TRUE(coilSystemCoolingWater.setRunonSensibleLoad(false)); EXPECT_FALSE(coilSystemCoolingWater.runonSensibleLoad()); + EXPECT_TRUE(coilSystemCoolingWater.setRunonSensibleLoad(true)); + EXPECT_TRUE(coilSystemCoolingWater.runonSensibleLoad()); // Run on Latent Load: Required Boolean + // Ctor default + EXPECT_FALSE(coilSystemCoolingWater.runonLatentLoad()); + // set EXPECT_TRUE(coilSystemCoolingWater.setRunonLatentLoad(true)); EXPECT_TRUE(coilSystemCoolingWater.runonLatentLoad()); EXPECT_TRUE(coilSystemCoolingWater.setRunonLatentLoad(false)); EXPECT_FALSE(coilSystemCoolingWater.runonLatentLoad()); // Minimum Air To Water Temperature Offset: Required Double + // Ctor default + EXPECT_EQ(0.0, coilSystemCoolingWater.minimumAirToWaterTemperatureOffset()); + // set EXPECT_TRUE(coilSystemCoolingWater.setMinimumAirToWaterTemperatureOffset(1.0)); EXPECT_EQ(1.0, coilSystemCoolingWater.minimumAirToWaterTemperatureOffset()); // Bad Value @@ -62,31 +102,53 @@ TEST_F(ModelFixture, CoilSystemCoolingWater_GettersSetters) { EXPECT_EQ(1.0, coilSystemCoolingWater.minimumAirToWaterTemperatureOffset()); // Economizer Lockout: Required Boolean - EXPECT_TRUE(coilSystemCoolingWater.setEconomizerLockout(true)); + // Ctor default EXPECT_TRUE(coilSystemCoolingWater.economizerLockout()); + // set EXPECT_TRUE(coilSystemCoolingWater.setEconomizerLockout(false)); EXPECT_FALSE(coilSystemCoolingWater.economizerLockout()); + EXPECT_TRUE(coilSystemCoolingWater.setEconomizerLockout(true)); + EXPECT_TRUE(coilSystemCoolingWater.economizerLockout()); // Minimum Water Loop Temperature For Heat Recovery: Required Double + // Ctor default + EXPECT_EQ(0.0, coilSystemCoolingWater.minimumWaterLoopTemperatureForHeatRecovery()); + // set EXPECT_TRUE(coilSystemCoolingWater.setMinimumWaterLoopTemperatureForHeatRecovery(1.2)); EXPECT_EQ(1.2, coilSystemCoolingWater.minimumWaterLoopTemperatureForHeatRecovery()); // Companion Coil Used For Heat Recovery: Optional Object + // The only type allowed is CoilCoolingWater CoilCoolingWater companionCoilUsedForHeatRecovery(m); EXPECT_TRUE(coilSystemCoolingWater.setCompanionCoilUsedForHeatRecovery(companionCoilUsedForHeatRecovery)); ASSERT_TRUE(coilSystemCoolingWater.companionCoilUsedForHeatRecovery()); EXPECT_EQ(companionCoilUsedForHeatRecovery, coilSystemCoolingWater.companionCoilUsedForHeatRecovery().get()); + + CoilSystemCoolingWaterHeatExchangerAssisted wrongCompanionCoil(m); + EXPECT_FALSE(coilSystemCoolingWater.setCompanionCoilUsedForHeatRecovery(wrongCompanionCoil)); + ASSERT_TRUE(coilSystemCoolingWater.companionCoilUsedForHeatRecovery()); + EXPECT_EQ(companionCoilUsedForHeatRecovery, coilSystemCoolingWater.companionCoilUsedForHeatRecovery().get()); } TEST_F(ModelFixture, CoilSystemCoolingWater_HeatCoolFuelTypes) { Model m; - CoilSystemCoolingWater coilSystemCoolingWater(m); - - EXPECT_EQ(ComponentType(ComponentType::Cooling), coilSystemCoolingWater.componentType()); - testFuelTypeEquality({}, coilSystemCoolingWater.coolingFuelTypes()); - testFuelTypeEquality({}, coilSystemCoolingWater.heatingFuelTypes()); - testAppGFuelTypeEquality({}, coilSystemCoolingWater.appGHeatingFuelTypes()); + CoilSystemCoolingWater coilSystem(m); + auto cc = coilSystem.coolingCoil().cast(); + + EXPECT_EQ(ComponentType(ComponentType::Cooling), coilSystem.componentType()); + testFuelTypeEquality({}, coilSystem.coolingFuelTypes()); + testFuelTypeEquality({}, coilSystem.heatingFuelTypes()); + testAppGFuelTypeEquality({}, coilSystem.appGHeatingFuelTypes()); + + PlantLoop p(m); + ChillerElectricEIR ch(m); + EXPECT_TRUE(p.addSupplyBranchForComponent(ch)); + EXPECT_TRUE(p.addDemandBranchForComponent(cc)); + EXPECT_EQ(ComponentType(ComponentType::Cooling), coilSystem.componentType()); + testFuelTypeEquality({FuelType::Electricity}, coilSystem.coolingFuelTypes()); + testFuelTypeEquality({}, coilSystem.heatingFuelTypes()); + testAppGFuelTypeEquality({}, coilSystem.appGHeatingFuelTypes()); } TEST_F(ModelFixture, CoilSystemCoolingWater_clone) {} From d0cc34b698dc71fdbffeeb0ed9bbdebc9774a257 Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Mon, 5 May 2025 11:57:50 +0200 Subject: [PATCH 03/16] Test Explicit Ctor, including wrong cooling coil type + fixup explicit ctor to remove() and LOG_AND_THROW --- src/model/CoilSystemCoolingWater.cpp | 7 +++-- .../test/CoilSystemCoolingWater_GTest.cpp | 31 +++++++++++++++++++ 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/src/model/CoilSystemCoolingWater.cpp b/src/model/CoilSystemCoolingWater.cpp index 359aef8b051..de9d9174233 100644 --- a/src/model/CoilSystemCoolingWater.cpp +++ b/src/model/CoilSystemCoolingWater.cpp @@ -347,12 +347,15 @@ namespace model { OS_ASSERT(getImpl()); bool ok = true; + ok = setCoolingCoil(coolingCoil); + if (!ok) { + remove(); + LOG_AND_THROW("Unable to set " << briefDescription() << "'s Cooling Coil " << coolingCoil.briefDescription() << "."); + } auto alwaysOn = model.alwaysOnDiscreteSchedule(); ok = setAvailabilitySchedule(alwaysOn); OS_ASSERT(ok); - ok = setCoolingCoil(coolingCoil); - OS_ASSERT(ok); ok = setDehumidificationControlType("None"); OS_ASSERT(ok); ok = setRunonSensibleLoad(true); diff --git a/src/model/test/CoilSystemCoolingWater_GTest.cpp b/src/model/test/CoilSystemCoolingWater_GTest.cpp index c1b3241c63c..08cd8677b2d 100644 --- a/src/model/test/CoilSystemCoolingWater_GTest.cpp +++ b/src/model/test/CoilSystemCoolingWater_GTest.cpp @@ -12,8 +12,11 @@ #include "../CoilCoolingWater_Impl.hpp" #include "../CoilSystemCoolingWaterHeatExchangerAssisted.hpp" #include "../CoilSystemCoolingWaterHeatExchangerAssisted_Impl.hpp" + #include "../AirLoopHVAC.hpp" #include "../ChillerElectricEIR.hpp" +#include "../CoilCoolingDXSingleSpeed.hpp" +#include "../CoilCoolingDXSingleSpeed_Impl.hpp" #include "../Node.hpp" #include "../PlantLoop.hpp" #include "../Schedule.hpp" @@ -130,6 +133,34 @@ TEST_F(ModelFixture, CoilSystemCoolingWater_GettersSetters) { EXPECT_EQ(companionCoilUsedForHeatRecovery, coilSystemCoolingWater.companionCoilUsedForHeatRecovery().get()); } +TEST_F(ModelFixture, CoilSystemCoolingWater_ExplicitCtor) { + { + Model m; + CoilCoolingWater coolingCoil(m); + CoilSystemCoolingWater coilSystem(m, coolingCoil); + EXPECT_EQ(coolingCoil, coilSystem.coolingCoil()); + EXPECT_EQ(1, m.getConcreteModelObjects().size()); + EXPECT_EQ(1, m.getConcreteModelObjects().size()); + } + + { + Model m; + CoilSystemCoolingWaterHeatExchangerAssisted coolingCoil(m); + CoilSystemCoolingWater coilSystem(m, coolingCoil); + EXPECT_EQ(coolingCoil, coilSystem.coolingCoil()); + EXPECT_EQ(1, m.getConcreteModelObjects().size()); + EXPECT_EQ(1, m.getConcreteModelObjects().size()); + } + + { + Model m; + CoilCoolingDXSingleSpeed coolingCoil(m); + EXPECT_ANY_THROW(CoilSystemCoolingWater coilSystem(m, coolingCoil)); + EXPECT_EQ(0, m.getConcreteModelObjects().size()); + EXPECT_EQ(1, m.getConcreteModelObjects().size()); + } +} + TEST_F(ModelFixture, CoilSystemCoolingWater_HeatCoolFuelTypes) { Model m; From ba57cdab084f70b5b5e1b133b18ee39e310506df Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Mon, 5 May 2025 14:50:20 +0200 Subject: [PATCH 04/16] Remove useless optionalCompanionCoilUsedForHeatRecovery --- src/model/CoilSystemCoolingWater.cpp | 10 +++------- src/model/CoilSystemCoolingWater_Impl.hpp | 1 - 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/src/model/CoilSystemCoolingWater.cpp b/src/model/CoilSystemCoolingWater.cpp index de9d9174233..c993c17ee3b 100644 --- a/src/model/CoilSystemCoolingWater.cpp +++ b/src/model/CoilSystemCoolingWater.cpp @@ -90,7 +90,7 @@ namespace model { if (OptionalHVACComponent intermediate = optionalCoolingCoil()) { result.push_back(*intermediate); } - if (OptionalHVACComponent intermediate = optionalCompanionCoilUsedForHeatRecovery()) { + if (OptionalHVACComponent intermediate = companionCoilUsedForHeatRecovery()) { result.push_back(*intermediate); } @@ -103,7 +103,7 @@ namespace model { if (OptionalHVACComponent intermediate = optionalCoolingCoil()) { coilSystemClone.setCoolingCoil(intermediate->clone(model).cast()); } - if (OptionalHVACComponent intermediate = optionalCompanionCoilUsedForHeatRecovery()) { + if (OptionalHVACComponent intermediate = companionCoilUsedForHeatRecovery()) { coilSystemClone.setCompanionCoilUsedForHeatRecovery(intermediate->clone(model).cast()); } @@ -237,7 +237,7 @@ namespace model { } boost::optional CoilSystemCoolingWater_Impl::companionCoilUsedForHeatRecovery() const { - return optionalCompanionCoilUsedForHeatRecovery(); + return getObject().getModelObjectTarget(OS_CoilSystem_Cooling_WaterFields::CompanionCoilUsedForHeatRecovery); } bool CoilSystemCoolingWater_Impl::setAvailabilitySchedule(Schedule& schedule) { @@ -311,10 +311,6 @@ namespace model { return getObject().getModelObjectTarget(OS_CoilSystem_Cooling_WaterFields::CoolingCoil); } - boost::optional CoilSystemCoolingWater_Impl::optionalCompanionCoilUsedForHeatRecovery() const { - return getObject().getModelObjectTarget(OS_CoilSystem_Cooling_WaterFields::CompanionCoilUsedForHeatRecovery); - } - } // namespace detail CoilSystemCoolingWater::CoilSystemCoolingWater(const Model& model) : StraightComponent(CoilSystemCoolingWater::iddObjectType(), model) { diff --git a/src/model/CoilSystemCoolingWater_Impl.hpp b/src/model/CoilSystemCoolingWater_Impl.hpp index 5b583051ddb..9d558cc1536 100644 --- a/src/model/CoilSystemCoolingWater_Impl.hpp +++ b/src/model/CoilSystemCoolingWater_Impl.hpp @@ -121,7 +121,6 @@ namespace model { boost::optional optionalAvailabilitySchedule() const; boost::optional optionalCoolingCoil() const; - boost::optional optionalCompanionCoilUsedForHeatRecovery() const; }; } // namespace detail From 4f3e2d4111a21f58243a7c7ebf13c4055ce205a6 Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Mon, 5 May 2025 15:17:51 +0200 Subject: [PATCH 05/16] CoilSystemCoolingWaterHXAssisted:containingHVACComponent picks up CoilSystemCoolingWater --- .../CoilSystemCoolingWaterHeatExchangerAssisted.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/model/CoilSystemCoolingWaterHeatExchangerAssisted.cpp b/src/model/CoilSystemCoolingWaterHeatExchangerAssisted.cpp index ddfcb530900..f3ff98c8cce 100644 --- a/src/model/CoilSystemCoolingWaterHeatExchangerAssisted.cpp +++ b/src/model/CoilSystemCoolingWaterHeatExchangerAssisted.cpp @@ -11,6 +11,8 @@ #include "WaterToAirComponent_Impl.hpp" #include "CoilCoolingWater.hpp" #include "CoilCoolingWater_Impl.hpp" +#include "CoilSystemCoolingWater.hpp" +#include "CoilSystemCoolingWater_Impl.hpp" #include "Model.hpp" #include "Model_Impl.hpp" #include "Node.hpp" @@ -105,6 +107,17 @@ namespace model { } } + // CoilSystemCoolingWater + for (const auto& coilSystem : this->model().getConcreteModelObjects()) { + if (coilSystem.coolingCoil().handle() == this->handle()) { + return coilSystem; + } else if (boost::optional companionCoil_ = coilSystem.companionCoilUsedForHeatRecovery()) { + if (companionCoil_->handle() == this->handle()) { + return coilSystem; + } + } + } + return boost::none; } From 63acda6fe25e3f698503c21335a7a75c7d5c75a9 Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Mon, 5 May 2025 15:18:52 +0200 Subject: [PATCH 06/16] Extend testing to ensure that remove works in particular --- ...oolingWaterHeatExchangerAssisted_GTest.cpp | 28 ++- .../test/CoilSystemCoolingWater_GTest.cpp | 164 +++++++++++++++++- 2 files changed, 177 insertions(+), 15 deletions(-) diff --git a/src/model/test/CoilSystemCoolingWaterHeatExchangerAssisted_GTest.cpp b/src/model/test/CoilSystemCoolingWaterHeatExchangerAssisted_GTest.cpp index 914fe9cfa3a..78bf7460a02 100644 --- a/src/model/test/CoilSystemCoolingWaterHeatExchangerAssisted_GTest.cpp +++ b/src/model/test/CoilSystemCoolingWaterHeatExchangerAssisted_GTest.cpp @@ -98,11 +98,16 @@ TEST_F(ModelFixture, CoilSystemCoolingWaterHeatExchangerAssisted_clone) { auto hx = coilSystem.heatExchanger().cast(); AirLoopHVAC a(m); + EXPECT_EQ(2, a.supplyComponents().size()); // o --- o Node n = a.supplyOutletNode(); EXPECT_TRUE(coilSystem.addToNode(n)); + EXPECT_EQ(3, a.supplyComponents().size()); // o --- oa_sys --- o PlantLoop p(m); + EXPECT_EQ(5, p.demandComponents().size()); // o --- Splitter --- o --- Mixer --- o + EXPECT_TRUE(p.addDemandBranchForComponent(cc)); + EXPECT_EQ(7, p.demandComponents().size()); // o --- Splitter --- o --- coolingCoil --- Mixer --- o EXPECT_EQ(1u, m.getConcreteModelObjects().size()); EXPECT_EQ(1u, m.getConcreteModelObjects().size()); @@ -118,7 +123,7 @@ TEST_F(ModelFixture, CoilSystemCoolingWaterHeatExchangerAssisted_clone) { //EXPECT_EQ(coilSystem.plantLoop()->handle(), p.handle()); //EXPECT_TRUE(hx.airLoopHVAC()); - auto coilSystem2 = coilSystem.clone(m).cast(); + auto coilSystemClone = coilSystem.clone(m).cast(); EXPECT_EQ(2u, m.getConcreteModelObjects().size()); EXPECT_EQ(2u, m.getConcreteModelObjects().size()); @@ -128,12 +133,23 @@ TEST_F(ModelFixture, CoilSystemCoolingWaterHeatExchangerAssisted_clone) { EXPECT_TRUE(coilSystem.inletModelObject()); EXPECT_TRUE(coilSystem.outletModelObject()); - EXPECT_FALSE(coilSystem2.airLoopHVAC()); - EXPECT_FALSE(coilSystem2.inletModelObject()); - EXPECT_FALSE(coilSystem2.outletModelObject()); + EXPECT_FALSE(coilSystemClone.airLoopHVAC()); + EXPECT_FALSE(coilSystemClone.inletModelObject()); + EXPECT_FALSE(coilSystemClone.outletModelObject()); + + EXPECT_NE(coilSystemClone.coolingCoil().handle(), cc.handle()); + EXPECT_NE(coilSystemClone.heatExchanger().handle(), hx.handle()); + + coilSystemClone.remove(); + EXPECT_EQ(1, m.getConcreteModelObjects().size()); + EXPECT_EQ(1, m.getConcreteModelObjects().size()); + + coilSystem.remove(); + EXPECT_EQ(0, m.getConcreteModelObjects().size()); + EXPECT_EQ(0, m.getConcreteModelObjects().size()); - EXPECT_NE(coilSystem2.coolingCoil().handle(), cc.handle()); - EXPECT_NE(coilSystem2.heatExchanger().handle(), hx.handle()); + EXPECT_EQ(2, a.supplyComponents().size()); // o --- o + EXPECT_EQ(5, p.demandComponents().size()); // o --- Splitter --- o --- Mixer --- o } TEST_F(ModelFixture, CoilSystemCoolingWaterHeatExchangerAssisted_containingComponent) { diff --git a/src/model/test/CoilSystemCoolingWater_GTest.cpp b/src/model/test/CoilSystemCoolingWater_GTest.cpp index 08cd8677b2d..964dfa57a8b 100644 --- a/src/model/test/CoilSystemCoolingWater_GTest.cpp +++ b/src/model/test/CoilSystemCoolingWater_GTest.cpp @@ -14,10 +14,14 @@ #include "../CoilSystemCoolingWaterHeatExchangerAssisted_Impl.hpp" #include "../AirLoopHVAC.hpp" +#include "../AirLoopHVACOutdoorAirSystem.hpp" #include "../ChillerElectricEIR.hpp" #include "../CoilCoolingDXSingleSpeed.hpp" #include "../CoilCoolingDXSingleSpeed_Impl.hpp" +#include "../ControllerWaterCoil.hpp" +#include "../ControllerWaterCoil_Impl.hpp" #include "../Node.hpp" +#include "../Node_Impl.hpp" #include "../PlantLoop.hpp" #include "../Schedule.hpp" #include "../ScheduleConstant.hpp" @@ -182,9 +186,151 @@ TEST_F(ModelFixture, CoilSystemCoolingWater_HeatCoolFuelTypes) { testAppGFuelTypeEquality({}, coilSystem.appGHeatingFuelTypes()); } -TEST_F(ModelFixture, CoilSystemCoolingWater_clone) {} +TEST_F(ModelFixture, CoilSystemCoolingWater_clone_remove) { -TEST_F(ModelFixture, CoilSystemCoolingWater_remove) {} + Model m; + + // Wrap Around Water Coil Heat Recovery Mode + // CoilSystem has: + // - Primary Cooling Coil: a CoilSystemCoolingWaterHeatExchangerAssisted + // - This HXAssisted has a CoilCoolingWater as its coolingCoil + // - Companion Coil: a CoilCoolingWater + // + // CoilSystem is connected to an AirLoopHVACOutdoorAirSystem on the OA Intake + // Companion Coil is connected to OA Relief + // Both coils are connected to a PlantLoop in series on the demand side + + CoilSystemCoolingWaterHeatExchangerAssisted coilSystemHXAssisted(m); + auto coolingCoil = coilSystemHXAssisted.coolingCoil().cast(); + coolingCoil.setName("Primary Cooling Coil inside HXAssisted"); + CoilSystemCoolingWater coilSystem(m, coilSystemHXAssisted); + + CoilCoolingWater companionCoil(m); + companionCoil.setName("Companion Coil"); + EXPECT_TRUE(coilSystem.setCompanionCoilUsedForHeatRecovery(companionCoil)); + + // Air side connections + AirLoopHVAC a(m); + AirLoopHVACOutdoorAirSystem oa_sys(m); + EXPECT_EQ(2, a.supplyComponents().size()); // o --- o + EXPECT_EQ(1, oa_sys.oaComponents().size()); // o + EXPECT_EQ(1, oa_sys.reliefComponents().size()); // o + { + Node n = a.supplyOutletNode(); + EXPECT_TRUE(oa_sys.addToNode(n)); + } + EXPECT_EQ(3, a.supplyComponents().size()); // o --- oa_sys --- o + EXPECT_EQ(1, oa_sys.oaComponents().size()); // o + EXPECT_EQ(1, oa_sys.reliefComponents().size()); // o + { + Node n = oa_sys.outboardOANode().get(); + EXPECT_TRUE(coilSystem.addToNode(n)); + } + EXPECT_EQ(3, a.supplyComponents().size()); // o --- oa_sys --- o + EXPECT_EQ(3, oa_sys.oaComponents().size()); // o --- coilSystem --- o + EXPECT_EQ(1, oa_sys.reliefComponents().size()); // o + { + Node n = oa_sys.outboardReliefNode().get(); + EXPECT_TRUE(companionCoil.addToNode(n)); + } + EXPECT_EQ(3, a.supplyComponents().size()); // o --- oa_sys --- o + EXPECT_EQ(3, oa_sys.oaComponents().size()); // o --- coilSystem --- o + EXPECT_EQ(3, oa_sys.reliefComponents().size()); // o --- companionCoil --- o + + // Plant Side connections: in series + PlantLoop p(m); + EXPECT_EQ(5, p.demandComponents().size()); // o --- Splitter --- o --- Mixer --- o + + EXPECT_TRUE(p.addDemandBranchForComponent(coolingCoil)); + EXPECT_EQ(7, p.demandComponents().size()); // o --- Splitter --- o --- coolingCoil --- Mixer --- o + + { + Node n = coolingCoil.waterOutletModelObject()->cast(); + EXPECT_TRUE(companionCoil.addToNode(n)); + } + EXPECT_EQ(9, p.demandComponents().size()); // o --- Splitter --- o --- coolingCoil --- o --- HR Coil --- o --- Mixer --- o + + EXPECT_EQ(1, m.getConcreteModelObjects().size()); + EXPECT_EQ(1, m.getConcreteModelObjects().size()); + EXPECT_EQ(2, m.getConcreteModelObjects().size()); + + ASSERT_TRUE(coilSystem.airLoopHVAC()); + EXPECT_EQ(coilSystem.airLoopHVAC()->handle(), a.handle()); + + EXPECT_TRUE(coolingCoil.plantLoop()); + EXPECT_TRUE(companionCoil.plantLoop()); + + EXPECT_FALSE(coolingCoil.controllerWaterCoil()); + EXPECT_FALSE(companionCoil.controllerWaterCoil()); + EXPECT_EQ(0, m.getConcreteModelObjects().size()); + + auto coilSystemClone = coilSystem.clone(m).cast(); + + EXPECT_EQ(2, m.getConcreteModelObjects().size()); + EXPECT_EQ(2, m.getConcreteModelObjects().size()); + EXPECT_EQ(4, m.getConcreteModelObjects().size()); + EXPECT_EQ(0, m.getConcreteModelObjects().size()); + + EXPECT_TRUE(coilSystem.airLoopHVAC()); + EXPECT_TRUE(coilSystem.inletModelObject()); + EXPECT_TRUE(coilSystem.outletModelObject()); + + EXPECT_FALSE(coilSystemClone.airLoopHVAC()); + EXPECT_FALSE(coilSystemClone.inletModelObject()); + EXPECT_FALSE(coilSystemClone.outletModelObject()); + + EXPECT_TRUE(coilSystemClone.coolingCoil().optionalCast()); + EXPECT_NE(coilSystemHXAssisted.handle(), coilSystemClone.coolingCoil().handle()); + + ASSERT_TRUE(coilSystemClone.companionCoilUsedForHeatRecovery()); + EXPECT_TRUE(coilSystemClone.companionCoilUsedForHeatRecovery()->optionalCast()); + EXPECT_NE(companionCoil.handle(), coilSystemClone.companionCoilUsedForHeatRecovery()->handle()); + + // Clone into another Model + { + Model m2; + auto coilSystemClone = coilSystem.clone(m2).cast(); + + EXPECT_EQ(1, m2.getConcreteModelObjects().size()); + EXPECT_EQ(1, m2.getConcreteModelObjects().size()); + EXPECT_EQ(2, m2.getConcreteModelObjects().size()); + EXPECT_EQ(0, m2.getConcreteModelObjects().size()); + + EXPECT_FALSE(coilSystemClone.airLoopHVAC()); + EXPECT_FALSE(coilSystemClone.inletModelObject()); + EXPECT_FALSE(coilSystemClone.outletModelObject()); + + EXPECT_TRUE(coilSystemClone.coolingCoil().optionalCast()); + EXPECT_NE(coilSystemHXAssisted.handle(), coilSystemClone.coolingCoil().handle()); + + ASSERT_TRUE(coilSystemClone.companionCoilUsedForHeatRecovery()); + EXPECT_TRUE(coilSystemClone.companionCoilUsedForHeatRecovery()->optionalCast()); + EXPECT_NE(companionCoil.handle(), coilSystemClone.companionCoilUsedForHeatRecovery()->handle()); + + // Remove + coilSystemClone.remove(); + EXPECT_EQ(0, m2.getConcreteModelObjects().size()); + EXPECT_EQ(0, m2.getConcreteModelObjects().size()); + EXPECT_EQ(0, m2.getConcreteModelObjects().size()); + } + + coilSystemClone.remove(); + EXPECT_EQ(1, m.getConcreteModelObjects().size()); + EXPECT_EQ(1, m.getConcreteModelObjects().size()); + EXPECT_EQ(2, m.getConcreteModelObjects().size()); + EXPECT_EQ(0, m.getConcreteModelObjects().size()); + + coilSystem.remove(); + 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(3, a.supplyComponents().size()); // o --- oa_sys --- o + EXPECT_EQ(1, oa_sys.oaComponents().size()); // o + EXPECT_EQ(1, oa_sys.reliefComponents().size()); // o + EXPECT_EQ(5, p.demandComponents().size()); // o --- Splitter --- o --- Mixer --- o +} TEST_F(ModelFixture, CoilSystemCoolingWater_addToNode) { // Water Size Economizer Model @@ -199,11 +345,11 @@ TEST_F(ModelFixture, CoilSystemCoolingWater_addToNode) { EXPECT_EQ(2u, a.supplyComponents().size()); - EXPECT_TRUE(cc.addToNode(n)); - EXPECT_EQ(3u, a.supplyComponents().size()); + EXPECT_FALSE(cc.addToNode(n)); + EXPECT_EQ(2u, a.supplyComponents().size()); EXPECT_TRUE(coilSystem.addToNode(n)); - EXPECT_EQ(5u, a.supplyComponents().size()); + EXPECT_EQ(3u, a.supplyComponents().size()); { auto containingHVACComponent = cc.containingHVACComponent(); @@ -231,14 +377,14 @@ TEST_F(ModelFixture, CoilSystemCoolingWater_addToNode) { EXPECT_EQ(2u, a.supplyComponents().size()); - EXPECT_TRUE(cc.addToNode(n)); - EXPECT_EQ(3u, a.supplyComponents().size()); + EXPECT_FALSE(cc.addToNode(n)); + EXPECT_EQ(2, a.supplyComponents().size()); EXPECT_TRUE(hr.addToNode(n)); - EXPECT_EQ(5u, a.supplyComponents().size()); + EXPECT_EQ(3, a.supplyComponents().size()); EXPECT_TRUE(coilSystem.addToNode(n)); - EXPECT_EQ(7u, a.supplyComponents().size()); + EXPECT_EQ(5, a.supplyComponents().size()); { auto containingHVACComponent = cc.containingHVACComponent(); From b264a564dc3fabf86b538ee874daf5511530634d Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Mon, 5 May 2025 15:28:32 +0200 Subject: [PATCH 07/16] Override remove() for CoilSystemCoolingWaterXX objects: need to disconnect the water side for the children coils --- src/model/CoilSystemCoolingWater.cpp | 25 +++++++++++++++++++ ...ystemCoolingWaterHeatExchangerAssisted.cpp | 19 +++++++++++--- ...CoolingWaterHeatExchangerAssisted_Impl.hpp | 5 +++- src/model/CoilSystemCoolingWater_Impl.hpp | 5 +++- 4 files changed, 48 insertions(+), 6 deletions(-) diff --git a/src/model/CoilSystemCoolingWater.cpp b/src/model/CoilSystemCoolingWater.cpp index c993c17ee3b..ced7f236681 100644 --- a/src/model/CoilSystemCoolingWater.cpp +++ b/src/model/CoilSystemCoolingWater.cpp @@ -12,6 +12,8 @@ #include "HVACComponent_Impl.hpp" #include "CoilCoolingWater.hpp" #include "CoilCoolingWater_Impl.hpp" +#include "CoilSystemCoolingWaterHeatExchangerAssisted.hpp" +#include "CoilSystemCoolingWaterHeatExchangerAssisted_Impl.hpp" #include "Model.hpp" #include "Model_Impl.hpp" #include "Node.hpp" @@ -97,6 +99,29 @@ namespace model { return result; } + std::vector CoilSystemCoolingWater_Impl::remove() { + std::vector result; + + if (auto c_ = optionalCoolingCoil()) { // This is false when the explicit ctor throws + if (boost::optional cc_ = c_->optionalCast()) { + cc_->removeFromPlantLoop(); + } else if (auto cc_ = c_->optionalCast()) { + cc_->coolingCoil().removeFromPlantLoop(); + } else { + OS_ASSERT(false); + } + } + if (auto companionCoil_ = companionCoilUsedForHeatRecovery()) { + if (auto cc_ = companionCoil_->optionalCast()) { + cc_->removeFromPlantLoop(); + } else { + OS_ASSERT(false); + } + } + + return StraightComponent_Impl::remove(); + } + ModelObject CoilSystemCoolingWater_Impl::clone(Model model) const { auto coilSystemClone = StraightComponent_Impl::clone(model).cast(); diff --git a/src/model/CoilSystemCoolingWaterHeatExchangerAssisted.cpp b/src/model/CoilSystemCoolingWaterHeatExchangerAssisted.cpp index f3ff98c8cce..4c47c420be9 100644 --- a/src/model/CoilSystemCoolingWaterHeatExchangerAssisted.cpp +++ b/src/model/CoilSystemCoolingWaterHeatExchangerAssisted.cpp @@ -73,8 +73,12 @@ namespace model { std::vector CoilSystemCoolingWaterHeatExchangerAssisted_Impl::children() const { std::vector result; - result.push_back(coolingCoil()); - result.push_back(heatExchanger()); + if (auto intermediate = optionalCoolingCoil()) { + result.push_back(*intermediate); + } + if (auto intermediate = optionalHeatExchanger()) { + result.push_back(*intermediate); + } return result; } @@ -95,6 +99,14 @@ namespace model { return std::move(newCoilSystem); } + std::vector CoilSystemCoolingWaterHeatExchangerAssisted_Impl::remove() { + if (auto cc_ = optionalCoolingCoil()) { + cc_->removeFromPlantLoop(); + } + + return StraightComponent_Impl::remove(); + } + boost::optional CoilSystemCoolingWaterHeatExchangerAssisted_Impl::containingHVACComponent() const { // AirLoopHVACUnitarySystem std::vector airLoopHVACUnitarySystems = this->model().getConcreteModelObjects(); @@ -248,13 +260,12 @@ namespace model { bool ok = setHeatExchanger(heatExchanger); if (!ok) { + remove(); LOG_AND_THROW("Unable to set " << briefDescription() << "'s Heat Exchanger " << heatExchanger.briefDescription() << "."); } CoilCoolingWater coolingCoil(model); setCoolingCoil(coolingCoil); - - setHeatExchanger(heatExchanger); } IddObjectType CoilSystemCoolingWaterHeatExchangerAssisted::iddObjectType() { diff --git a/src/model/CoilSystemCoolingWaterHeatExchangerAssisted_Impl.hpp b/src/model/CoilSystemCoolingWaterHeatExchangerAssisted_Impl.hpp index 85bb4ef471c..375e842e9ed 100644 --- a/src/model/CoilSystemCoolingWaterHeatExchangerAssisted_Impl.hpp +++ b/src/model/CoilSystemCoolingWaterHeatExchangerAssisted_Impl.hpp @@ -51,9 +51,12 @@ namespace model { // Will also clone the coolingCoil and heatExchanger virtual ModelObject clone(Model model) const override; - // This function will connect the underlying Coil:Cooling:Water object + // This function will connect the air side only, for water side, use the coils directly virtual bool addToNode(Node& node) override; + // Need to disconnect the Water side of the coil(s) before removing + virtual std::vector remove() override; + virtual boost::optional containingHVACComponent() const override; virtual boost::optional containingZoneHVACComponent() const override; diff --git a/src/model/CoilSystemCoolingWater_Impl.hpp b/src/model/CoilSystemCoolingWater_Impl.hpp index 9d558cc1536..8793eba87ef 100644 --- a/src/model/CoilSystemCoolingWater_Impl.hpp +++ b/src/model/CoilSystemCoolingWater_Impl.hpp @@ -52,9 +52,12 @@ namespace model { // Will also clone the coolingCoil and companionCoilUsedForHeatRecovery virtual ModelObject clone(Model model) const override; - // This function will connect the underlying Coil:Cooling:Water object + // This function will connect the air side only, for water side, use the coils directly virtual bool addToNode(Node& node) override; + // Need to disconnect the Water side of the coil(s) before removing + virtual std::vector remove() override; + virtual boost::optional containingHVACComponent() const override; virtual boost::optional containingZoneHVACComponent() const override; From 53780c84a27e83c84839b5efa242fba76b5b4ddb Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Mon, 5 May 2025 15:29:38 +0200 Subject: [PATCH 08/16] Fixup the creation of the ControllerWaterCoil and disallow connection of the primary cooling cooling of a CoilSystemCoolingWater to an AirLoopHVAC Ensures coils that are part of a cooling system are not directly connected to an air loop, preventing potential errors. Also manages the creation and removal of ControllerWaterCoil objects based on the coil's parent system and presence within a ZoneHVACComponent. --- src/model/CoilCoolingWater.cpp | 80 +++++++++++++++++++++------------- 1 file changed, 50 insertions(+), 30 deletions(-) diff --git a/src/model/CoilCoolingWater.cpp b/src/model/CoilCoolingWater.cpp index 8576570f2f8..4be5cc9b666 100644 --- a/src/model/CoilCoolingWater.cpp +++ b/src/model/CoilCoolingWater.cpp @@ -273,48 +273,68 @@ namespace model { } bool CoilCoolingWater_Impl::addToNode(Node& node) { - bool success(false); + bool success = false; auto t_containingHVACComponent = containingHVACComponent(); auto t_airLoop = node.airLoopHVAC(); - if (t_airLoop && t_containingHVACComponent && t_containingHVACComponent->optionalCast()) { - LOG(Warn, this->briefDescription() - << " cannot be connected directly to an AirLoopHVAC when it's part of a parent CoilSystemCoolingWaterHeatExchangerAssisted. " - "Please call CoilSystemCoolingWaterHeatExchangerAssisted::addToNode instead"); - - /* } else if (t_airLoop && t_containingHVACComponent && t_containingHVACComponent->optionalCast()) { - LOG(Warn, this->briefDescription() - << " cannot be connected directly to an AirLoopHVAC when it's part of a parent CoilSystemCoolingWater. " - "Please call CoilSystemCoolingWater::addToNode instead"); */ + // Reject adding coils parent of a Parent System + if (t_airLoop && t_containingHVACComponent) { + if (t_containingHVACComponent->optionalCast()) { + LOG(Warn, this->briefDescription() + << " cannot be connected directly to an AirLoopHVAC when it's part of a parent CoilSystemCoolingWaterHeatExchangerAssisted. " + "Please call CoilSystemCoolingWaterHeatExchangerAssisted::addToNode instead"); + return false; + } - } else { + if (t_containingHVACComponent->optionalCast() + && t_containingHVACComponent->cast().coolingCoil().handle() == this->handle()) { + LOG(Warn, this->briefDescription() + << " cannot be connected directly to an AirLoopHVAC when it's the primary cooling coil of a parent CoilSystemCoolingWater. " + "Please call CoilSystemCoolingWater::addToNode instead"); + return false; + } + } - success = WaterToAirComponent_Impl::addToNode(node); - auto t_containingZoneHVACComponent = containingZoneHVACComponent(); + success = WaterToAirComponent_Impl::addToNode(node); + if (!success) { + return false; + } + // We only add the controller if we are not part of a containing ZoneHVACComponent, and we have a PlantLoop assigned + if (containingZoneHVACComponent() || !waterInletModelObject()) { + // We're done + return true; + } - if (success && (!t_containingZoneHVACComponent)) { - if (auto t_waterInletModelObject = waterInletModelObject()) { - if (auto oldController = controllerWaterCoil()) { - if (!openstudio::istringEqual(oldController->action().get(), "Reverse")) { - LOG(Warn, - briefDescription() - << " has an existing ControllerWaterCoil with action set to something else than 'Reverse'. Make sure this is what you want"); - } - } else { - if (t_containingHVACComponent && t_containingHVACComponent->optionalCast()) { - // no-op - } else { - Model t_model = model(); - ControllerWaterCoil controller(t_model); - controller.getImpl()->setWaterCoil(getObject()); - controller.setAction("Reverse"); - } + // We don't need a controller if we are part of a CoilSystemCoolingWater (directly, or indirectly via a + // CoilSystemCoolingWaterHeatExchangerAssisted) + bool needAController = true; + if (t_containingHVACComponent) { + if (t_containingHVACComponent->optionalCast()) { + needAController = false; + } else if (auto hxAssisted_ = t_containingHVACComponent->optionalCast()) { + if (auto parent_ = hxAssisted_->containingHVACComponent()) { + if (parent_->optionalCast()) { + needAController = false; } } } } + if (auto oldController_ = controllerWaterCoil()) { + if (!needAController) { + oldController_->remove(); + } else if (!openstudio::istringEqual(oldController_->action().get(), "Reverse")) { + LOG(Warn, briefDescription() + << " has an existing ControllerWaterCoil with action set to something else than 'Reverse'. Make sure this is what you want"); + } + } else if (needAController) { + Model t_model = model(); + ControllerWaterCoil controller(t_model); + controller.getImpl()->setWaterCoil(getObject()); + controller.setAction("Reverse"); + } + return success; } From 14c735774b8a534f1125c08ea5dfe9f99c63bff1 Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Mon, 5 May 2025 15:30:10 +0200 Subject: [PATCH 09/16] I have no reason to believe that CoilSystemCoolingWater would be allowed anywhere in a ZoneHVAC/HVACComponent --- src/model/CoilSystemCoolingWater.cpp | 40 ---------------------------- 1 file changed, 40 deletions(-) diff --git a/src/model/CoilSystemCoolingWater.cpp b/src/model/CoilSystemCoolingWater.cpp index ced7f236681..ad77a88ba47 100644 --- a/src/model/CoilSystemCoolingWater.cpp +++ b/src/model/CoilSystemCoolingWater.cpp @@ -152,50 +152,10 @@ namespace model { } boost::optional CoilSystemCoolingWater_Impl::containingHVACComponent() const { - // AirLoopHVACUnitarySystem - std::vector airLoopHVACUnitarySystems = this->model().getConcreteModelObjects(); - - for (const auto& airLoopHVACUnitarySystem : airLoopHVACUnitarySystems) { - if (boost::optional coolingCoil = airLoopHVACUnitarySystem.coolingCoil()) { - if (coolingCoil->handle() == this->handle()) { - return airLoopHVACUnitarySystem; - } - } - } - return boost::none; } boost::optional CoilSystemCoolingWater_Impl::containingZoneHVACComponent() const { - - // ZoneHVACFourPipeFanCoil - std::vector zoneHVACFourPipeFanCoils; - - zoneHVACFourPipeFanCoils = this->model().getConcreteModelObjects(); - - for (const auto& zoneHVACFourPipeFanCoil : zoneHVACFourPipeFanCoils) { - if (boost::optional coil = zoneHVACFourPipeFanCoil.coolingCoil()) { - if (coil->handle() == this->handle()) { - return zoneHVACFourPipeFanCoil; - } - } - } - - // ZoneHVACUnitVentilator - std::vector zoneHVACUnitVentilators; - - zoneHVACUnitVentilators = this->model().getConcreteModelObjects(); - - for (const auto& zoneHVACUnitVentilator : zoneHVACUnitVentilators) { - if (boost::optional coil = zoneHVACUnitVentilator.coolingCoil()) { - if (coil->handle() == this->handle()) { - return zoneHVACUnitVentilator; - } - } - } - - // ZoneHVAC:WindowAirConditioner not wrapped - return boost::none; } From 95c4727cac8ac93da17c99f6d71ec48892194aa0 Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Mon, 5 May 2025 15:48:26 +0200 Subject: [PATCH 10/16] Adjust FT test after changing return type, and remove the Unitary test (not supported AFAIK) --- .../Test/CoilSystemCoolingWater_GTest.cpp | 444 ++++++++---------- 1 file changed, 198 insertions(+), 246 deletions(-) diff --git a/src/energyplus/Test/CoilSystemCoolingWater_GTest.cpp b/src/energyplus/Test/CoilSystemCoolingWater_GTest.cpp index 85b1838cb40..2e0ce8ae009 100644 --- a/src/energyplus/Test/CoilSystemCoolingWater_GTest.cpp +++ b/src/energyplus/Test/CoilSystemCoolingWater_GTest.cpp @@ -14,6 +14,8 @@ #include "../../model/Schedule_Impl.hpp" #include "../../model/CoilCoolingWater.hpp" #include "../../model/CoilCoolingWater_Impl.hpp" +#include "../../model/CoilSystemCoolingWaterHeatExchangerAssisted.hpp" +#include "../../model/CoilSystemCoolingWaterHeatExchangerAssisted_Impl.hpp" #include "../../model/AirLoopHVAC.hpp" #include "../../model/AirLoopHVACUnitarySystem.hpp" @@ -48,13 +50,106 @@ using namespace openstudio::energyplus; using namespace openstudio::model; using namespace openstudio; -TEST_F(EnergyPlusFixture, ForwardTranslator_CoilSystemCoolingWater_Unitary) { +TEST_F(EnergyPlusFixture, ForwardTranslator_CoilSystemCoolingWater_WithoutCompanion) { + ForwardTranslator ft; + + Model m; + + CoilSystemCoolingWater coilSystemCoolingWater(m); + auto cc = coilSystemCoolingWater.coolingCoil().cast(); + + coilSystemCoolingWater.setName("My CoilSystemCoolingWater"); + Schedule availabilitySchedule = m.alwaysOnDiscreteSchedule(); + EXPECT_TRUE(coilSystemCoolingWater.setAvailabilitySchedule(availabilitySchedule)); + EXPECT_TRUE(coilSystemCoolingWater.setDehumidificationControlType("CoolReheat")); + EXPECT_TRUE(coilSystemCoolingWater.setRunonSensibleLoad(false)); // Opposite from IDD default + EXPECT_TRUE(coilSystemCoolingWater.setRunonLatentLoad(true)); // Opposite from IDD default + EXPECT_TRUE(coilSystemCoolingWater.setMinimumAirToWaterTemperatureOffset(1.0)); + EXPECT_TRUE(coilSystemCoolingWater.setEconomizerLockout(false)); // Opposite from IDD default + EXPECT_TRUE(coilSystemCoolingWater.setMinimumWaterLoopTemperatureForHeatRecovery(1.2)); + + AirLoopHVAC airLoop(m); + Node supplyOutletNode = airLoop.supplyOutletNode(); + EXPECT_TRUE(coilSystemCoolingWater.addToNode(supplyOutletNode)); + + // They must be connected to a PlantLoop too + PlantLoop chw_p(m); + chw_p.addDemandBranchForComponent(cc); + + const Workspace w = ft.translateModel(m); + + const auto idfCoilSystems = w.getObjectsByType(IddObjectType::CoilSystem_Cooling_Water); + ASSERT_EQ(1u, idfCoilSystems.size()); + const auto& idfCoilSystem = idfCoilSystems.front(); + + WorkspaceObjectVector idfCoils(w.getObjectsByType(IddObjectType::Coil_Cooling_Water)); + ASSERT_EQ(1u, idfCoils.size()); + WorkspaceObject idfCoil(idfCoils[0]); + + // Go from AirLoopHVAC to BranchList to Branch + WorkspaceObjectVector idf_airloops = w.getObjectsByType(IddObjectType::AirLoopHVAC); + ASSERT_EQ(1u, idf_airloops.size()); + + WorkspaceObject& idf_airLoopHVAC = idf_airloops.front(); + WorkspaceObject idf_brlist = idf_airLoopHVAC.getTarget(AirLoopHVACFields::BranchListName).get(); + + // Should have one branch only + ASSERT_EQ(1u, idf_brlist.extensibleGroups().size()); + auto w_eg = idf_brlist.extensibleGroups()[0].cast(); + WorkspaceObject idf_branch = w_eg.getTarget(BranchListExtensibleFields::BranchName).get(); + + // There should be only one equipment on the branch + ASSERT_EQ(1u, idf_branch.extensibleGroups().size()); + auto w_eg2 = idf_branch.extensibleGroups()[0].cast(); + + EXPECT_EQ(w_eg2.getString(BranchExtensibleFields::ComponentInletNodeName).get(), coilSystemCoolingWater.inletModelObject().get().nameString()); + EXPECT_EQ(w_eg2.getString(BranchExtensibleFields::ComponentOutletNodeName).get(), coilSystemCoolingWater.outletModelObject().get().nameString()); + EXPECT_EQ("CoilSystem:Cooling:Water", w_eg2.getString(BranchExtensibleFields::ComponentObjectType).get()); + + EXPECT_EQ(coilSystemCoolingWater.inletModelObject().get().nameString(), + idfCoilSystem.getString(CoilSystem_Cooling_WaterFields::AirInletNodeName).get()); + EXPECT_EQ(coilSystemCoolingWater.outletModelObject().get().nameString(), + idfCoilSystem.getString(CoilSystem_Cooling_WaterFields::AirOutletNodeName).get()); + EXPECT_EQ(availabilitySchedule.nameString(), idfCoilSystem.getString(CoilSystem_Cooling_WaterFields::AvailabilityScheduleName).get()); + EXPECT_EQ("Coil:Cooling:Water", idfCoilSystem.getString(CoilSystem_Cooling_WaterFields::CoolingCoilObjectType).get()); + EXPECT_EQ(coilSystemCoolingWater.coolingCoil().nameString(), idfCoilSystem.getString(CoilSystem_Cooling_WaterFields::CoolingCoilName).get()); + EXPECT_EQ("CoolReheat", idfCoilSystem.getString(CoilSystem_Cooling_WaterFields::DehumidificationControlType).get()); + EXPECT_EQ("No", idfCoilSystem.getString(CoilSystem_Cooling_WaterFields::RunonSensibleLoad).get()); + EXPECT_EQ("Yes", idfCoilSystem.getString(CoilSystem_Cooling_WaterFields::RunonLatentLoad).get()); + EXPECT_EQ(1.0, idfCoilSystem.getDouble(CoilSystem_Cooling_WaterFields::MinimumAirToWaterTemperatureOffset).get()); + EXPECT_EQ("No", idfCoilSystem.getString(CoilSystem_Cooling_WaterFields::EconomizerLockout).get()); + EXPECT_EQ(1.2, idfCoilSystem.getDouble(CoilSystem_Cooling_WaterFields::MinimumWaterLoopTemperatureForHeatRecovery).get()); + EXPECT_TRUE(idfCoilSystem.isEmpty(CoilSystem_Cooling_WaterFields::CompanionCoilUsedForHeatRecovery)); + + EXPECT_EQ(coilSystemCoolingWater.coolingCoil().nameString(), idfCoil.getString(Coil_Cooling_WaterFields::Name).get()); + EXPECT_EQ("Always On Discrete", idfCoil.getString(Coil_Cooling_WaterFields::AvailabilityScheduleName).get()); + EXPECT_EQ("Autosize", idfCoil.getString(Coil_Cooling_WaterFields::DesignWaterFlowRate).get()); + EXPECT_EQ("Autosize", idfCoil.getString(Coil_Cooling_WaterFields::DesignAirFlowRate).get()); + EXPECT_EQ("Autosize", idfCoil.getString(Coil_Cooling_WaterFields::DesignInletWaterTemperature).get()); + EXPECT_EQ("Autosize", idfCoil.getString(Coil_Cooling_WaterFields::DesignInletAirTemperature).get()); + EXPECT_EQ("Autosize", idfCoil.getString(Coil_Cooling_WaterFields::DesignInletAirHumidityRatio).get()); + EXPECT_EQ("Autosize", idfCoil.getString(Coil_Cooling_WaterFields::DesignOutletAirHumidityRatio).get()); + EXPECT_EQ(cc.waterInletModelObject().get().nameString(), idfCoil.getString(Coil_Cooling_WaterFields::WaterInletNodeName).get()); + EXPECT_EQ(cc.waterOutletModelObject().get().nameString(), idfCoil.getString(Coil_Cooling_WaterFields::WaterOutletNodeName).get()); + EXPECT_EQ(idfCoilSystem.getString(CoilSystem_Cooling_WaterFields::AirInletNodeName).get(), + idfCoil.getString(Coil_Cooling_WaterFields::AirInletNodeName).get()); + EXPECT_EQ(idfCoilSystem.getString(CoilSystem_Cooling_WaterFields::AirOutletNodeName).get(), + idfCoil.getString(Coil_Cooling_WaterFields::AirOutletNodeName).get()); + EXPECT_EQ("SimpleAnalysis", idfCoil.getString(Coil_Cooling_WaterFields::TypeofAnalysis).get()); + EXPECT_EQ("CrossFlow", idfCoil.getString(Coil_Cooling_WaterFields::HeatExchangerConfiguration).get()); + EXPECT_TRUE(idfCoil.isEmpty(Coil_Cooling_WaterFields::CondensateCollectionWaterStorageTankName)); + EXPECT_TRUE(idfCoil.isEmpty(Coil_Cooling_WaterFields::DesignWaterTemperatureDifference)); + + EXPECT_EQ(0, w.getObjectsByType(IddObjectType::Controller_WaterCoil).size()); +} +TEST_F(EnergyPlusFixture, ForwardTranslator_CoilSystemCoolingWater_WithCompanion) { ForwardTranslator ft; Model m; CoilSystemCoolingWater coilSystemCoolingWater(m); + auto cc = coilSystemCoolingWater.coolingCoil().cast(); coilSystemCoolingWater.setName("My CoilSystemCoolingWater"); Schedule availabilitySchedule = m.alwaysOnDiscreteSchedule(); @@ -68,260 +163,117 @@ TEST_F(EnergyPlusFixture, ForwardTranslator_CoilSystemCoolingWater_Unitary) { CoilCoolingWater companionCoilUsedForHeatRecovery(m); EXPECT_TRUE(coilSystemCoolingWater.setCompanionCoilUsedForHeatRecovery(companionCoilUsedForHeatRecovery)); - // put it inside a Unitary, and put Unitary on an AirLoopHVAC - AirLoopHVACUnitarySystem unitary(m); - EXPECT_FALSE(unitary.setCoolingCoil(coilSystemCoolingWater)); AirLoopHVAC airLoop(m); Node supplyOutletNode = airLoop.supplyOutletNode(); - unitary.addToNode(supplyOutletNode); - ASSERT_TRUE(unitary.inletNode()); - EXPECT_TRUE(unitary.inletNode()->setName("Coil Air Inlet Node")); - ASSERT_TRUE(unitary.outletNode()); - EXPECT_TRUE(unitary.outletNode()->setName("Coil Air Outlet Node")); + ControllerOutdoorAir controller1(m); + AirLoopHVACOutdoorAirSystem oaSystem(m, controller1); + EXPECT_TRUE(oaSystem.addToNode(supplyOutletNode)); + Node oaNode = oaSystem.outboardOANode().get(); + EXPECT_TRUE(coilSystemCoolingWater.addToNode(oaNode)); + Node reliefNode = oaSystem.reliefAirModelObject()->cast(); + EXPECT_TRUE(companionCoilUsedForHeatRecovery.addToNode(reliefNode)); // They must be connected to a PlantLoop too PlantLoop chw_p(m); - chw_p.addDemandBranchForComponent(coilSystemCoolingWater.coolingCoil()); + chw_p.addDemandBranchForComponent(cc); + PipeAdiabatic pipe(m); + Node coolingOutlet = cc.waterOutletModelObject()->cast(); + EXPECT_TRUE(pipe.addToNode(coolingOutlet)); + Node pipeOutlet = pipe.outletModelObject()->cast(); + EXPECT_TRUE(companionCoilUsedForHeatRecovery.addToNode(pipeOutlet)); const Workspace w = ft.translateModel(m); - const auto idfUnitarys = w.getObjectsByType(IddObjectType::AirLoopHVAC_UnitarySystem); - ASSERT_EQ(1u, idfUnitarys.size()); - const auto idfCoilSystems = w.getObjectsByType(IddObjectType::CoilSystem_Cooling_Water); - ASSERT_EQ(0u, idfCoilSystems.size()); -} + ASSERT_EQ(1u, idfCoilSystems.size()); + const auto& idfCoilSystem = idfCoilSystems.front(); + + WorkspaceObjectVector idfCoils(w.getObjectsByType(IddObjectType::Coil_Cooling_Water)); + ASSERT_EQ(2u, idfCoils.size()); + + WorkspaceObjectVector idfControllers(w.getObjectsByType(IddObjectType::Controller_OutdoorAir)); + ASSERT_EQ(1u, idfControllers.size()); + const auto& idfController = idfControllers.front(); + + // Go from AirLoopHVAC to BranchList to Branch + WorkspaceObjectVector idf_airloops = w.getObjectsByType(IddObjectType::AirLoopHVAC); + ASSERT_EQ(1u, idf_airloops.size()); + + WorkspaceObject& idf_airLoopHVAC = idf_airloops.front(); + WorkspaceObject idf_brlist = idf_airLoopHVAC.getTarget(AirLoopHVACFields::BranchListName).get(); + + // Should have one branch only + ASSERT_EQ(1u, idf_brlist.extensibleGroups().size()); + auto w_eg = idf_brlist.extensibleGroups()[0].cast(); + WorkspaceObject idf_branch = w_eg.getTarget(BranchListExtensibleFields::BranchName).get(); + + // There should be only one equipment on the branch + ASSERT_EQ(1u, idf_branch.extensibleGroups().size()); + auto w_eg2 = idf_branch.extensibleGroups()[0].cast(); -TEST_F(EnergyPlusFixture, ForwardTranslator_CoilSystemCoolingWater_AirLoopHVAC) { - // without companion - { - ForwardTranslator ft; - - Model m; - - CoilSystemCoolingWater coilSystemCoolingWater(m); - - coilSystemCoolingWater.setName("My CoilSystemCoolingWater"); - Schedule availabilitySchedule = m.alwaysOnDiscreteSchedule(); - EXPECT_TRUE(coilSystemCoolingWater.setAvailabilitySchedule(availabilitySchedule)); - EXPECT_TRUE(coilSystemCoolingWater.setDehumidificationControlType("CoolReheat")); - EXPECT_TRUE(coilSystemCoolingWater.setRunonSensibleLoad(false)); // Opposite from IDD default - EXPECT_TRUE(coilSystemCoolingWater.setRunonLatentLoad(true)); // Opposite from IDD default - EXPECT_TRUE(coilSystemCoolingWater.setMinimumAirToWaterTemperatureOffset(1.0)); - EXPECT_TRUE(coilSystemCoolingWater.setEconomizerLockout(false)); // Opposite from IDD default - EXPECT_TRUE(coilSystemCoolingWater.setMinimumWaterLoopTemperatureForHeatRecovery(1.2)); - - AirLoopHVAC airLoop(m); - Node supplyOutletNode = airLoop.supplyOutletNode(); - EXPECT_TRUE(coilSystemCoolingWater.addToNode(supplyOutletNode)); - - // They must be connected to a PlantLoop too - PlantLoop chw_p(m); - chw_p.addDemandBranchForComponent(coilSystemCoolingWater.coolingCoil()); - - const Workspace w = ft.translateModel(m); - - const auto idfCoilSystems = w.getObjectsByType(IddObjectType::CoilSystem_Cooling_Water); - ASSERT_EQ(1u, idfCoilSystems.size()); - const auto& idfCoilSystem = idfCoilSystems.front(); - - WorkspaceObjectVector idfCoils(w.getObjectsByType(IddObjectType::Coil_Cooling_Water)); - ASSERT_EQ(1u, idfCoils.size()); - WorkspaceObject idfCoil(idfCoils[0]); - - // Go from AirLoopHVAC to BranchList to Branch - WorkspaceObjectVector idf_airloops = w.getObjectsByType(IddObjectType::AirLoopHVAC); - ASSERT_EQ(1u, idf_airloops.size()); - - WorkspaceObject& idf_airLoopHVAC = idf_airloops.front(); - WorkspaceObject idf_brlist = idf_airLoopHVAC.getTarget(AirLoopHVACFields::BranchListName).get(); - - // Should have one branch only - ASSERT_EQ(1u, idf_brlist.extensibleGroups().size()); - auto w_eg = idf_brlist.extensibleGroups()[0].cast(); - WorkspaceObject idf_branch = w_eg.getTarget(BranchListExtensibleFields::BranchName).get(); - - // There should be only one equipment on the branch - ASSERT_EQ(1u, idf_branch.extensibleGroups().size()); - auto w_eg2 = idf_branch.extensibleGroups()[0].cast(); - - EXPECT_EQ(w_eg2.getString(BranchExtensibleFields::ComponentInletNodeName).get(), coilSystemCoolingWater.inletModelObject().get().nameString()); - EXPECT_EQ(w_eg2.getString(BranchExtensibleFields::ComponentOutletNodeName).get(), coilSystemCoolingWater.outletModelObject().get().nameString()); - EXPECT_EQ("CoilSystem:Cooling:Water", w_eg2.getString(BranchExtensibleFields::ComponentObjectType).get()); - - EXPECT_EQ(coilSystemCoolingWater.inletModelObject().get().nameString(), - idfCoilSystem.getString(CoilSystem_Cooling_WaterFields::AirInletNodeName).get()); - EXPECT_EQ(coilSystemCoolingWater.outletModelObject().get().nameString(), - idfCoilSystem.getString(CoilSystem_Cooling_WaterFields::AirOutletNodeName).get()); - EXPECT_EQ(availabilitySchedule.nameString(), idfCoilSystem.getString(CoilSystem_Cooling_WaterFields::AvailabilityScheduleName).get()); - EXPECT_EQ("Coil:Cooling:Water", idfCoilSystem.getString(CoilSystem_Cooling_WaterFields::CoolingCoilObjectType).get()); - EXPECT_EQ(coilSystemCoolingWater.coolingCoil().nameString(), idfCoilSystem.getString(CoilSystem_Cooling_WaterFields::CoolingCoilName).get()); - EXPECT_EQ("CoolReheat", idfCoilSystem.getString(CoilSystem_Cooling_WaterFields::DehumidificationControlType).get()); - EXPECT_EQ("No", idfCoilSystem.getString(CoilSystem_Cooling_WaterFields::RunonSensibleLoad).get()); - EXPECT_EQ("Yes", idfCoilSystem.getString(CoilSystem_Cooling_WaterFields::RunonLatentLoad).get()); - EXPECT_EQ(1.0, idfCoilSystem.getDouble(CoilSystem_Cooling_WaterFields::MinimumAirToWaterTemperatureOffset).get()); - EXPECT_EQ("No", idfCoilSystem.getString(CoilSystem_Cooling_WaterFields::EconomizerLockout).get()); - EXPECT_EQ(1.2, idfCoilSystem.getDouble(CoilSystem_Cooling_WaterFields::MinimumWaterLoopTemperatureForHeatRecovery).get()); - EXPECT_TRUE(idfCoilSystem.isEmpty(CoilSystem_Cooling_WaterFields::CompanionCoilUsedForHeatRecovery)); - - EXPECT_EQ(coilSystemCoolingWater.coolingCoil().nameString(), idfCoil.getString(Coil_Cooling_WaterFields::Name).get()); - EXPECT_EQ("Always On Discrete", idfCoil.getString(Coil_Cooling_WaterFields::AvailabilityScheduleName).get()); - EXPECT_EQ("Autosize", idfCoil.getString(Coil_Cooling_WaterFields::DesignWaterFlowRate).get()); - EXPECT_EQ("Autosize", idfCoil.getString(Coil_Cooling_WaterFields::DesignAirFlowRate).get()); - EXPECT_EQ("Autosize", idfCoil.getString(Coil_Cooling_WaterFields::DesignInletWaterTemperature).get()); - EXPECT_EQ("Autosize", idfCoil.getString(Coil_Cooling_WaterFields::DesignInletAirTemperature).get()); - EXPECT_EQ("Autosize", idfCoil.getString(Coil_Cooling_WaterFields::DesignInletAirHumidityRatio).get()); - EXPECT_EQ("Autosize", idfCoil.getString(Coil_Cooling_WaterFields::DesignOutletAirHumidityRatio).get()); - EXPECT_EQ(coilSystemCoolingWater.coolingCoil().waterInletModelObject().get().nameString(), - idfCoil.getString(Coil_Cooling_WaterFields::WaterInletNodeName).get()); - EXPECT_EQ(coilSystemCoolingWater.coolingCoil().waterOutletModelObject().get().nameString(), - idfCoil.getString(Coil_Cooling_WaterFields::WaterOutletNodeName).get()); - EXPECT_EQ(idfCoilSystem.getString(CoilSystem_Cooling_WaterFields::AirInletNodeName).get(), - idfCoil.getString(Coil_Cooling_WaterFields::AirInletNodeName).get()); - EXPECT_EQ(idfCoilSystem.getString(CoilSystem_Cooling_WaterFields::AirOutletNodeName).get(), - idfCoil.getString(Coil_Cooling_WaterFields::AirOutletNodeName).get()); - EXPECT_EQ("SimpleAnalysis", idfCoil.getString(Coil_Cooling_WaterFields::TypeofAnalysis).get()); - EXPECT_EQ("CrossFlow", idfCoil.getString(Coil_Cooling_WaterFields::HeatExchangerConfiguration).get()); - EXPECT_TRUE(idfCoil.isEmpty(Coil_Cooling_WaterFields::CondensateCollectionWaterStorageTankName)); - EXPECT_TRUE(idfCoil.isEmpty(Coil_Cooling_WaterFields::DesignWaterTemperatureDifference)); - - EXPECT_EQ(0, w.getObjectsByType(IddObjectType::Controller_WaterCoil).size()); - } - - // with companion - { - ForwardTranslator ft; - - Model m; - - CoilSystemCoolingWater coilSystemCoolingWater(m); - - coilSystemCoolingWater.setName("My CoilSystemCoolingWater"); - Schedule availabilitySchedule = m.alwaysOnDiscreteSchedule(); - EXPECT_TRUE(coilSystemCoolingWater.setAvailabilitySchedule(availabilitySchedule)); - EXPECT_TRUE(coilSystemCoolingWater.setDehumidificationControlType("CoolReheat")); - EXPECT_TRUE(coilSystemCoolingWater.setRunonSensibleLoad(false)); // Opposite from IDD default - EXPECT_TRUE(coilSystemCoolingWater.setRunonLatentLoad(true)); // Opposite from IDD default - EXPECT_TRUE(coilSystemCoolingWater.setMinimumAirToWaterTemperatureOffset(1.0)); - EXPECT_TRUE(coilSystemCoolingWater.setEconomizerLockout(false)); // Opposite from IDD default - EXPECT_TRUE(coilSystemCoolingWater.setMinimumWaterLoopTemperatureForHeatRecovery(1.2)); - CoilCoolingWater companionCoilUsedForHeatRecovery(m); - EXPECT_TRUE(coilSystemCoolingWater.setCompanionCoilUsedForHeatRecovery(companionCoilUsedForHeatRecovery)); - - AirLoopHVAC airLoop(m); - Node supplyOutletNode = airLoop.supplyOutletNode(); - ControllerOutdoorAir controller1(m); - AirLoopHVACOutdoorAirSystem oaSystem(m, controller1); - EXPECT_TRUE(oaSystem.addToNode(supplyOutletNode)); - Node oaNode = oaSystem.outboardOANode().get(); - EXPECT_TRUE(coilSystemCoolingWater.addToNode(oaNode)); - Node reliefNode = oaSystem.reliefAirModelObject()->cast(); - EXPECT_TRUE(companionCoilUsedForHeatRecovery.addToNode(reliefNode)); - - // They must be connected to a PlantLoop too - PlantLoop chw_p(m); - chw_p.addDemandBranchForComponent(coilSystemCoolingWater.coolingCoil()); - PipeAdiabatic pipe(m); - Node coolingOutlet = coilSystemCoolingWater.coolingCoil().waterOutletModelObject()->cast(); - EXPECT_TRUE(pipe.addToNode(coolingOutlet)); - Node pipeOutlet = pipe.outletModelObject()->cast(); - EXPECT_TRUE(companionCoilUsedForHeatRecovery.addToNode(pipeOutlet)); - - const Workspace w = ft.translateModel(m); - - const auto idfCoilSystems = w.getObjectsByType(IddObjectType::CoilSystem_Cooling_Water); - ASSERT_EQ(1u, idfCoilSystems.size()); - const auto& idfCoilSystem = idfCoilSystems.front(); - - WorkspaceObjectVector idfCoils(w.getObjectsByType(IddObjectType::Coil_Cooling_Water)); - ASSERT_EQ(2u, idfCoils.size()); - - WorkspaceObjectVector idfControllers(w.getObjectsByType(IddObjectType::Controller_OutdoorAir)); - ASSERT_EQ(1u, idfControllers.size()); - const auto& idfController = idfControllers.front(); - - // Go from AirLoopHVAC to BranchList to Branch - WorkspaceObjectVector idf_airloops = w.getObjectsByType(IddObjectType::AirLoopHVAC); - ASSERT_EQ(1u, idf_airloops.size()); - - WorkspaceObject& idf_airLoopHVAC = idf_airloops.front(); - WorkspaceObject idf_brlist = idf_airLoopHVAC.getTarget(AirLoopHVACFields::BranchListName).get(); - - // Should have one branch only - ASSERT_EQ(1u, idf_brlist.extensibleGroups().size()); - auto w_eg = idf_brlist.extensibleGroups()[0].cast(); - WorkspaceObject idf_branch = w_eg.getTarget(BranchListExtensibleFields::BranchName).get(); - - // There should be only one equipment on the branch - ASSERT_EQ(1u, idf_branch.extensibleGroups().size()); - auto w_eg2 = idf_branch.extensibleGroups()[0].cast(); - - /* EXPECT_EQ(w_eg2.getString(BranchExtensibleFields::ComponentInletNodeName).get(), coilSystemCoolingWater.inletModelObject().get().nameString()); + /* EXPECT_EQ(w_eg2.getString(BranchExtensibleFields::ComponentInletNodeName).get(), coilSystemCoolingWater.inletModelObject().get().nameString()); EXPECT_EQ(w_eg2.getString(BranchExtensibleFields::ComponentOutletNodeName).get(), coilSystemCoolingWater.outletModelObject().get().nameString()); */ - EXPECT_EQ("AirLoopHVAC:OutdoorAirSystem", w_eg2.getString(BranchExtensibleFields::ComponentObjectType).get()); - - EXPECT_EQ(coilSystemCoolingWater.inletModelObject().get().nameString(), - idfCoilSystem.getString(CoilSystem_Cooling_WaterFields::AirInletNodeName).get()); - EXPECT_EQ(coilSystemCoolingWater.outletModelObject().get().nameString(), - idfCoilSystem.getString(CoilSystem_Cooling_WaterFields::AirOutletNodeName).get()); - EXPECT_EQ(availabilitySchedule.nameString(), idfCoilSystem.getString(CoilSystem_Cooling_WaterFields::AvailabilityScheduleName).get()); - EXPECT_EQ("Coil:Cooling:Water", idfCoilSystem.getString(CoilSystem_Cooling_WaterFields::CoolingCoilObjectType).get()); - EXPECT_EQ(coilSystemCoolingWater.coolingCoil().nameString(), idfCoilSystem.getString(CoilSystem_Cooling_WaterFields::CoolingCoilName).get()); - EXPECT_EQ("CoolReheat", idfCoilSystem.getString(CoilSystem_Cooling_WaterFields::DehumidificationControlType).get()); - EXPECT_EQ("No", idfCoilSystem.getString(CoilSystem_Cooling_WaterFields::RunonSensibleLoad).get()); - EXPECT_EQ("Yes", idfCoilSystem.getString(CoilSystem_Cooling_WaterFields::RunonLatentLoad).get()); - EXPECT_EQ(1.0, idfCoilSystem.getDouble(CoilSystem_Cooling_WaterFields::MinimumAirToWaterTemperatureOffset).get()); - EXPECT_EQ("No", idfCoilSystem.getString(CoilSystem_Cooling_WaterFields::EconomizerLockout).get()); - EXPECT_EQ(1.2, idfCoilSystem.getDouble(CoilSystem_Cooling_WaterFields::MinimumWaterLoopTemperatureForHeatRecovery).get()); - EXPECT_EQ(companionCoilUsedForHeatRecovery.nameString(), - idfCoilSystem.getString(CoilSystem_Cooling_WaterFields::CompanionCoilUsedForHeatRecovery).get()); - - WorkspaceObject idf_coolingCoil = idfCoilSystem.getTarget(CoilSystem_Cooling_WaterFields::CoolingCoilName).get(); - WorkspaceObject idf_companionCoil = idfCoilSystem.getTarget(CoilSystem_Cooling_WaterFields::CompanionCoilUsedForHeatRecovery).get(); - - EXPECT_EQ(coilSystemCoolingWater.coolingCoil().nameString(), idf_coolingCoil.getString(Coil_Cooling_WaterFields::Name).get()); - EXPECT_EQ("Always On Discrete", idf_coolingCoil.getString(Coil_Cooling_WaterFields::AvailabilityScheduleName).get()); - EXPECT_EQ("Autosize", idf_coolingCoil.getString(Coil_Cooling_WaterFields::DesignWaterFlowRate).get()); - EXPECT_EQ("Autosize", idf_coolingCoil.getString(Coil_Cooling_WaterFields::DesignAirFlowRate).get()); - EXPECT_EQ("Autosize", idf_coolingCoil.getString(Coil_Cooling_WaterFields::DesignInletWaterTemperature).get()); - EXPECT_EQ("Autosize", idf_coolingCoil.getString(Coil_Cooling_WaterFields::DesignInletAirTemperature).get()); - EXPECT_EQ("Autosize", idf_coolingCoil.getString(Coil_Cooling_WaterFields::DesignInletAirHumidityRatio).get()); - EXPECT_EQ("Autosize", idf_coolingCoil.getString(Coil_Cooling_WaterFields::DesignOutletAirHumidityRatio).get()); - EXPECT_EQ(coilSystemCoolingWater.coolingCoil().waterInletModelObject().get().nameString(), - idf_coolingCoil.getString(Coil_Cooling_WaterFields::WaterInletNodeName).get()); - EXPECT_EQ(coilSystemCoolingWater.coolingCoil().waterOutletModelObject().get().nameString(), - idf_coolingCoil.getString(Coil_Cooling_WaterFields::WaterOutletNodeName).get()); - EXPECT_EQ(idfCoilSystem.getString(CoilSystem_Cooling_WaterFields::AirInletNodeName).get(), - idf_coolingCoil.getString(Coil_Cooling_WaterFields::AirInletNodeName).get()); - EXPECT_EQ(idfCoilSystem.getString(CoilSystem_Cooling_WaterFields::AirOutletNodeName).get(), - idf_coolingCoil.getString(Coil_Cooling_WaterFields::AirOutletNodeName).get()); - EXPECT_EQ("SimpleAnalysis", idf_coolingCoil.getString(Coil_Cooling_WaterFields::TypeofAnalysis).get()); - EXPECT_EQ("CrossFlow", idf_coolingCoil.getString(Coil_Cooling_WaterFields::HeatExchangerConfiguration).get()); - EXPECT_TRUE(idf_coolingCoil.isEmpty(Coil_Cooling_WaterFields::CondensateCollectionWaterStorageTankName)); - EXPECT_TRUE(idf_coolingCoil.isEmpty(Coil_Cooling_WaterFields::DesignWaterTemperatureDifference)); - - EXPECT_EQ(coilSystemCoolingWater.companionCoilUsedForHeatRecovery().get().nameString(), - idf_companionCoil.getString(Coil_Cooling_WaterFields::Name).get()); - EXPECT_EQ("Always On Discrete", idf_companionCoil.getString(Coil_Cooling_WaterFields::AvailabilityScheduleName).get()); - EXPECT_EQ("Autosize", idf_companionCoil.getString(Coil_Cooling_WaterFields::DesignWaterFlowRate).get()); - EXPECT_EQ("Autosize", idf_companionCoil.getString(Coil_Cooling_WaterFields::DesignAirFlowRate).get()); - EXPECT_EQ("Autosize", idf_companionCoil.getString(Coil_Cooling_WaterFields::DesignInletWaterTemperature).get()); - EXPECT_EQ("Autosize", idf_companionCoil.getString(Coil_Cooling_WaterFields::DesignInletAirTemperature).get()); - EXPECT_EQ("Autosize", idf_companionCoil.getString(Coil_Cooling_WaterFields::DesignInletAirHumidityRatio).get()); - EXPECT_EQ("Autosize", idf_companionCoil.getString(Coil_Cooling_WaterFields::DesignOutletAirHumidityRatio).get()); - EXPECT_EQ(coilSystemCoolingWater.companionCoilUsedForHeatRecovery().get().waterInletModelObject().get().nameString(), - idf_companionCoil.getString(Coil_Cooling_WaterFields::WaterInletNodeName).get()); - EXPECT_EQ(coilSystemCoolingWater.companionCoilUsedForHeatRecovery().get().waterOutletModelObject().get().nameString(), - idf_companionCoil.getString(Coil_Cooling_WaterFields::WaterOutletNodeName).get()); - EXPECT_EQ(idfController.getString(Controller_OutdoorAirFields::ReliefAirOutletNodeName).get(), - idf_companionCoil.getString(Coil_Cooling_WaterFields::AirInletNodeName).get()); - EXPECT_EQ(coilSystemCoolingWater.companionCoilUsedForHeatRecovery().get().nameString() + " Exhaust Outlet Node", - idf_companionCoil.getString(Coil_Cooling_WaterFields::AirOutletNodeName).get()); // FIXME - EXPECT_EQ("SimpleAnalysis", idf_companionCoil.getString(Coil_Cooling_WaterFields::TypeofAnalysis).get()); - EXPECT_EQ("CrossFlow", idf_companionCoil.getString(Coil_Cooling_WaterFields::HeatExchangerConfiguration).get()); - EXPECT_TRUE(idf_companionCoil.isEmpty(Coil_Cooling_WaterFields::CondensateCollectionWaterStorageTankName)); - EXPECT_TRUE(idf_companionCoil.isEmpty(Coil_Cooling_WaterFields::DesignWaterTemperatureDifference)); - - EXPECT_EQ(0, w.getObjectsByType(IddObjectType::Controller_WaterCoil).size()); - } + EXPECT_EQ("AirLoopHVAC:OutdoorAirSystem", w_eg2.getString(BranchExtensibleFields::ComponentObjectType).get()); + + EXPECT_EQ(coilSystemCoolingWater.inletModelObject().get().nameString(), + idfCoilSystem.getString(CoilSystem_Cooling_WaterFields::AirInletNodeName).get()); + EXPECT_EQ(coilSystemCoolingWater.outletModelObject().get().nameString(), + idfCoilSystem.getString(CoilSystem_Cooling_WaterFields::AirOutletNodeName).get()); + EXPECT_EQ(availabilitySchedule.nameString(), idfCoilSystem.getString(CoilSystem_Cooling_WaterFields::AvailabilityScheduleName).get()); + EXPECT_EQ("Coil:Cooling:Water", idfCoilSystem.getString(CoilSystem_Cooling_WaterFields::CoolingCoilObjectType).get()); + EXPECT_EQ(coilSystemCoolingWater.coolingCoil().nameString(), idfCoilSystem.getString(CoilSystem_Cooling_WaterFields::CoolingCoilName).get()); + EXPECT_EQ("CoolReheat", idfCoilSystem.getString(CoilSystem_Cooling_WaterFields::DehumidificationControlType).get()); + EXPECT_EQ("No", idfCoilSystem.getString(CoilSystem_Cooling_WaterFields::RunonSensibleLoad).get()); + EXPECT_EQ("Yes", idfCoilSystem.getString(CoilSystem_Cooling_WaterFields::RunonLatentLoad).get()); + EXPECT_EQ(1.0, idfCoilSystem.getDouble(CoilSystem_Cooling_WaterFields::MinimumAirToWaterTemperatureOffset).get()); + EXPECT_EQ("No", idfCoilSystem.getString(CoilSystem_Cooling_WaterFields::EconomizerLockout).get()); + EXPECT_EQ(1.2, idfCoilSystem.getDouble(CoilSystem_Cooling_WaterFields::MinimumWaterLoopTemperatureForHeatRecovery).get()); + EXPECT_EQ(companionCoilUsedForHeatRecovery.nameString(), + idfCoilSystem.getString(CoilSystem_Cooling_WaterFields::CompanionCoilUsedForHeatRecovery).get()); + + WorkspaceObject idf_coolingCoil = idfCoilSystem.getTarget(CoilSystem_Cooling_WaterFields::CoolingCoilName).get(); + WorkspaceObject idf_companionCoil = idfCoilSystem.getTarget(CoilSystem_Cooling_WaterFields::CompanionCoilUsedForHeatRecovery).get(); + + EXPECT_EQ(coilSystemCoolingWater.coolingCoil().nameString(), idf_coolingCoil.getString(Coil_Cooling_WaterFields::Name).get()); + EXPECT_EQ("Always On Discrete", idf_coolingCoil.getString(Coil_Cooling_WaterFields::AvailabilityScheduleName).get()); + EXPECT_EQ("Autosize", idf_coolingCoil.getString(Coil_Cooling_WaterFields::DesignWaterFlowRate).get()); + EXPECT_EQ("Autosize", idf_coolingCoil.getString(Coil_Cooling_WaterFields::DesignAirFlowRate).get()); + EXPECT_EQ("Autosize", idf_coolingCoil.getString(Coil_Cooling_WaterFields::DesignInletWaterTemperature).get()); + EXPECT_EQ("Autosize", idf_coolingCoil.getString(Coil_Cooling_WaterFields::DesignInletAirTemperature).get()); + EXPECT_EQ("Autosize", idf_coolingCoil.getString(Coil_Cooling_WaterFields::DesignInletAirHumidityRatio).get()); + EXPECT_EQ("Autosize", idf_coolingCoil.getString(Coil_Cooling_WaterFields::DesignOutletAirHumidityRatio).get()); + EXPECT_EQ(cc.waterInletModelObject().get().nameString(), idf_coolingCoil.getString(Coil_Cooling_WaterFields::WaterInletNodeName).get()); + EXPECT_EQ(cc.waterOutletModelObject().get().nameString(), idf_coolingCoil.getString(Coil_Cooling_WaterFields::WaterOutletNodeName).get()); + EXPECT_EQ(idfCoilSystem.getString(CoilSystem_Cooling_WaterFields::AirInletNodeName).get(), + idf_coolingCoil.getString(Coil_Cooling_WaterFields::AirInletNodeName).get()); + EXPECT_EQ(idfCoilSystem.getString(CoilSystem_Cooling_WaterFields::AirOutletNodeName).get(), + idf_coolingCoil.getString(Coil_Cooling_WaterFields::AirOutletNodeName).get()); + EXPECT_EQ("SimpleAnalysis", idf_coolingCoil.getString(Coil_Cooling_WaterFields::TypeofAnalysis).get()); + EXPECT_EQ("CrossFlow", idf_coolingCoil.getString(Coil_Cooling_WaterFields::HeatExchangerConfiguration).get()); + EXPECT_TRUE(idf_coolingCoil.isEmpty(Coil_Cooling_WaterFields::CondensateCollectionWaterStorageTankName)); + EXPECT_TRUE(idf_coolingCoil.isEmpty(Coil_Cooling_WaterFields::DesignWaterTemperatureDifference)); + + EXPECT_EQ(coilSystemCoolingWater.companionCoilUsedForHeatRecovery().get().nameString(), + idf_companionCoil.getString(Coil_Cooling_WaterFields::Name).get()); + EXPECT_EQ("Always On Discrete", idf_companionCoil.getString(Coil_Cooling_WaterFields::AvailabilityScheduleName).get()); + EXPECT_EQ("Autosize", idf_companionCoil.getString(Coil_Cooling_WaterFields::DesignWaterFlowRate).get()); + EXPECT_EQ("Autosize", idf_companionCoil.getString(Coil_Cooling_WaterFields::DesignAirFlowRate).get()); + EXPECT_EQ("Autosize", idf_companionCoil.getString(Coil_Cooling_WaterFields::DesignInletWaterTemperature).get()); + EXPECT_EQ("Autosize", idf_companionCoil.getString(Coil_Cooling_WaterFields::DesignInletAirTemperature).get()); + EXPECT_EQ("Autosize", idf_companionCoil.getString(Coil_Cooling_WaterFields::DesignInletAirHumidityRatio).get()); + EXPECT_EQ("Autosize", idf_companionCoil.getString(Coil_Cooling_WaterFields::DesignOutletAirHumidityRatio).get()); + EXPECT_EQ(companionCoilUsedForHeatRecovery.waterInletModelObject().get().nameString(), + idf_companionCoil.getString(Coil_Cooling_WaterFields::WaterInletNodeName).get()); + EXPECT_EQ(companionCoilUsedForHeatRecovery.waterOutletModelObject().get().nameString(), + idf_companionCoil.getString(Coil_Cooling_WaterFields::WaterOutletNodeName).get()); + EXPECT_EQ(idfController.getString(Controller_OutdoorAirFields::ReliefAirOutletNodeName).get(), + idf_companionCoil.getString(Coil_Cooling_WaterFields::AirInletNodeName).get()); + EXPECT_EQ(coilSystemCoolingWater.companionCoilUsedForHeatRecovery().get().nameString() + " Exhaust Outlet Node", + idf_companionCoil.getString(Coil_Cooling_WaterFields::AirOutletNodeName).get()); // FIXME + EXPECT_EQ("SimpleAnalysis", idf_companionCoil.getString(Coil_Cooling_WaterFields::TypeofAnalysis).get()); + EXPECT_EQ("CrossFlow", idf_companionCoil.getString(Coil_Cooling_WaterFields::HeatExchangerConfiguration).get()); + EXPECT_TRUE(idf_companionCoil.isEmpty(Coil_Cooling_WaterFields::CondensateCollectionWaterStorageTankName)); + EXPECT_TRUE(idf_companionCoil.isEmpty(Coil_Cooling_WaterFields::DesignWaterTemperatureDifference)); + + EXPECT_EQ(0, w.getObjectsByType(IddObjectType::Controller_WaterCoil).size()); } From 3d6d14531ac661797387a9b657a9961e3879241a Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Mon, 5 May 2025 15:52:52 +0200 Subject: [PATCH 11/16] Remove unneeded bit in ForwardTranslateAirLoopHVAC --- .../ForwardTranslator/ForwardTranslateAirLoopHVAC.cpp | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/energyplus/ForwardTranslator/ForwardTranslateAirLoopHVAC.cpp b/src/energyplus/ForwardTranslator/ForwardTranslateAirLoopHVAC.cpp index f43861de00f..e40d2ebf483 100644 --- a/src/energyplus/ForwardTranslator/ForwardTranslateAirLoopHVAC.cpp +++ b/src/energyplus/ForwardTranslator/ForwardTranslateAirLoopHVAC.cpp @@ -277,13 +277,6 @@ namespace energyplus { controller = supplyComponent.cast().controllerWaterCoil(); break; } - case openstudio::IddObjectType::OS_CoilSystem_Cooling_Water: { - auto coolingCoil = supplyComponent.cast().coolingCoil(); - if (auto coilCoolingWater = coolingCoil.optionalCast()) { - controller = coilCoolingWater->controllerWaterCoil(); - } - break; - } case openstudio::IddObjectType::OS_CoilSystem_Cooling_Water_HeatExchangerAssisted: { auto coolingCoil = supplyComponent.cast().coolingCoil(); if (auto coilCoolingWater = coolingCoil.optionalCast()) { @@ -291,6 +284,10 @@ namespace energyplus { } break; } + // case openstudio::IddObjectType::OS_CoilSystem_Cooling_Water: { + // // No-op: this has its own controller + // break; + // } default: { break; } From 868633ab378788f7205c73dda7d0432d5c593841 Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Mon, 5 May 2025 16:20:47 +0200 Subject: [PATCH 12/16] Add a private assignEnergyPlusIDDDefaults method to avoid repeating code (and getting them out of sync) in both ctors --- src/model/CoilSystemCoolingWater.cpp | 36 +++++++++++----------------- src/model/CoilSystemCoolingWater.hpp | 2 ++ 2 files changed, 16 insertions(+), 22 deletions(-) diff --git a/src/model/CoilSystemCoolingWater.cpp b/src/model/CoilSystemCoolingWater.cpp index ad77a88ba47..6b85a453e67 100644 --- a/src/model/CoilSystemCoolingWater.cpp +++ b/src/model/CoilSystemCoolingWater.cpp @@ -298,17 +298,12 @@ namespace model { } // namespace detail - CoilSystemCoolingWater::CoilSystemCoolingWater(const Model& model) : StraightComponent(CoilSystemCoolingWater::iddObjectType(), model) { - OS_ASSERT(getImpl()); - + void CoilSystemCoolingWater::assignEnergyPlusIDDDefaults() { bool ok = true; - auto alwaysOn = model.alwaysOnDiscreteSchedule(); + auto alwaysOn = model().alwaysOnDiscreteSchedule(); ok = setAvailabilitySchedule(alwaysOn); OS_ASSERT(ok); - CoilCoolingWater coolingCoil(model); - ok = setCoolingCoil(coolingCoil); - OS_ASSERT(ok); ok = setDehumidificationControlType("None"); OS_ASSERT(ok); ok = setRunonSensibleLoad(true); @@ -323,6 +318,17 @@ namespace model { OS_ASSERT(ok); } + CoilSystemCoolingWater::CoilSystemCoolingWater(const Model& model) : StraightComponent(CoilSystemCoolingWater::iddObjectType(), model) { + OS_ASSERT(getImpl()); + + CoilCoolingWater coolingCoil(model); + coolingCoil.setName(name().get() + " Cooling Coil"); + bool ok = setCoolingCoil(coolingCoil); + OS_ASSERT(ok); + + assignEnergyPlusIDDDefaults(); + } + CoilSystemCoolingWater::CoilSystemCoolingWater(const Model& model, const HVACComponent& coolingCoil) : StraightComponent(CoilSystemCoolingWater::iddObjectType(), model) { OS_ASSERT(getImpl()); @@ -333,22 +339,8 @@ namespace model { remove(); LOG_AND_THROW("Unable to set " << briefDescription() << "'s Cooling Coil " << coolingCoil.briefDescription() << "."); } - auto alwaysOn = model.alwaysOnDiscreteSchedule(); - ok = setAvailabilitySchedule(alwaysOn); - OS_ASSERT(ok); - ok = setDehumidificationControlType("None"); - OS_ASSERT(ok); - ok = setRunonSensibleLoad(true); - OS_ASSERT(ok); - ok = setRunonLatentLoad(false); - OS_ASSERT(ok); - ok = setMinimumAirToWaterTemperatureOffset(0.0); - OS_ASSERT(ok); - ok = setEconomizerLockout(true); - OS_ASSERT(ok); - ok = setMinimumWaterLoopTemperatureForHeatRecovery(0.0); - OS_ASSERT(ok); + assignEnergyPlusIDDDefaults(); } IddObjectType CoilSystemCoolingWater::iddObjectType() { diff --git a/src/model/CoilSystemCoolingWater.hpp b/src/model/CoilSystemCoolingWater.hpp index 2d2b29ce9df..dcc2cebd742 100644 --- a/src/model/CoilSystemCoolingWater.hpp +++ b/src/model/CoilSystemCoolingWater.hpp @@ -111,6 +111,8 @@ namespace model { /// @endcond private: REGISTER_LOGGER("openstudio.model.CoilSystemCoolingWater"); + + void assignEnergyPlusIDDDefaults(); }; /** \relates CoilSystemCoolingWater*/ From 64dd179bf65f886581920ecce1facd139a5778fc Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Mon, 5 May 2025 18:04:40 +0200 Subject: [PATCH 13/16] Adjust FT Test: name the nodes and co, and test the relief node that has a FIXME (it fails) /home/julien/Software/Others/OpenStudio/src/energyplus/Test/CoilSystemCoolingWater_GTest.cpp:297: Failure Expected equality of these values: "Relief Node" idf_companionCoil.getString(Coil_Cooling_WaterFields::AirOutletNodeName).get() Which is: "CompanionCoilUsedForHeatRecovery Exhaust Outlet Node" --- .../Test/CoilSystemCoolingWater_GTest.cpp | 32 +++++++++++++++++-- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/src/energyplus/Test/CoilSystemCoolingWater_GTest.cpp b/src/energyplus/Test/CoilSystemCoolingWater_GTest.cpp index 2e0ce8ae009..d66716170aa 100644 --- a/src/energyplus/Test/CoilSystemCoolingWater_GTest.cpp +++ b/src/energyplus/Test/CoilSystemCoolingWater_GTest.cpp @@ -161,6 +161,7 @@ TEST_F(EnergyPlusFixture, ForwardTranslator_CoilSystemCoolingWater_WithCompanion EXPECT_TRUE(coilSystemCoolingWater.setEconomizerLockout(false)); // Opposite from IDD default EXPECT_TRUE(coilSystemCoolingWater.setMinimumWaterLoopTemperatureForHeatRecovery(1.2)); CoilCoolingWater companionCoilUsedForHeatRecovery(m); + companionCoilUsedForHeatRecovery.setName("CompanionCoilUsedForHeatRecovery"); EXPECT_TRUE(coilSystemCoolingWater.setCompanionCoilUsedForHeatRecovery(companionCoilUsedForHeatRecovery)); AirLoopHVAC airLoop(m); @@ -170,9 +171,17 @@ TEST_F(EnergyPlusFixture, ForwardTranslator_CoilSystemCoolingWater_WithCompanion EXPECT_TRUE(oaSystem.addToNode(supplyOutletNode)); Node oaNode = oaSystem.outboardOANode().get(); EXPECT_TRUE(coilSystemCoolingWater.addToNode(oaNode)); - Node reliefNode = oaSystem.reliefAirModelObject()->cast(); + Node reliefNode = oaSystem.outboardReliefNode().get(); EXPECT_TRUE(companionCoilUsedForHeatRecovery.addToNode(reliefNode)); + oaNode.setName("Outdoor Air Inlet Node"); + reliefNode.setName("Relief Node"); + oaSystem.reliefAirModelObject()->setName("Return to OA System Node"); + oaSystem.outdoorAirModelObject()->setName("OA System Outlet to Mixed Node"); + airLoop.mixedAirNode()->setName("Mixed Air Node"); + airLoop.supplyInletNode().setName("Supply Inlet Node"); + airLoop.supplyOutletNode().setName("Supply Outlet Node"); + // They must be connected to a PlantLoop too PlantLoop chw_p(m); chw_p.addDemandBranchForComponent(cc); @@ -182,6 +191,11 @@ TEST_F(EnergyPlusFixture, ForwardTranslator_CoilSystemCoolingWater_WithCompanion Node pipeOutlet = pipe.outletModelObject()->cast(); EXPECT_TRUE(companionCoilUsedForHeatRecovery.addToNode(pipeOutlet)); + cc.waterInletModelObject()->setName("Cooling Coil Water Inlet Node"); + cc.waterOutletModelObject()->setName("Cooling Coil Water Outlet to Pipe Inlet Node"); + pipe.outletModelObject()->setName("Pipe Outlet to Companion Coil Water Inlet Node"); + companionCoilUsedForHeatRecovery.waterOutletModelObject()->setName("Companion Coil Water Outlet Node"); + const Workspace w = ft.translateModel(m); const auto idfCoilSystems = w.getObjectsByType(IddObjectType::CoilSystem_Cooling_Water); @@ -242,12 +256,19 @@ TEST_F(EnergyPlusFixture, ForwardTranslator_CoilSystemCoolingWater_WithCompanion EXPECT_EQ("Autosize", idf_coolingCoil.getString(Coil_Cooling_WaterFields::DesignInletAirTemperature).get()); EXPECT_EQ("Autosize", idf_coolingCoil.getString(Coil_Cooling_WaterFields::DesignInletAirHumidityRatio).get()); EXPECT_EQ("Autosize", idf_coolingCoil.getString(Coil_Cooling_WaterFields::DesignOutletAirHumidityRatio).get()); + EXPECT_EQ(cc.waterInletModelObject().get().nameString(), idf_coolingCoil.getString(Coil_Cooling_WaterFields::WaterInletNodeName).get()); + EXPECT_EQ("Cooling Coil Water Inlet Node", idf_coolingCoil.getString(Coil_Cooling_WaterFields::WaterInletNodeName).get()); EXPECT_EQ(cc.waterOutletModelObject().get().nameString(), idf_coolingCoil.getString(Coil_Cooling_WaterFields::WaterOutletNodeName).get()); + EXPECT_EQ("Cooling Coil Water Outlet to Pipe Inlet Node", idf_coolingCoil.getString(Coil_Cooling_WaterFields::WaterOutletNodeName).get()); EXPECT_EQ(idfCoilSystem.getString(CoilSystem_Cooling_WaterFields::AirInletNodeName).get(), idf_coolingCoil.getString(Coil_Cooling_WaterFields::AirInletNodeName).get()); + + EXPECT_EQ("Outdoor Air Inlet Node", idf_coolingCoil.getString(Coil_Cooling_WaterFields::AirInletNodeName).get()); EXPECT_EQ(idfCoilSystem.getString(CoilSystem_Cooling_WaterFields::AirOutletNodeName).get(), idf_coolingCoil.getString(Coil_Cooling_WaterFields::AirOutletNodeName).get()); + EXPECT_EQ("OA System Outlet to Mixed Node", idf_coolingCoil.getString(Coil_Cooling_WaterFields::AirOutletNodeName).get()); + EXPECT_EQ("SimpleAnalysis", idf_coolingCoil.getString(Coil_Cooling_WaterFields::TypeofAnalysis).get()); EXPECT_EQ("CrossFlow", idf_coolingCoil.getString(Coil_Cooling_WaterFields::HeatExchangerConfiguration).get()); EXPECT_TRUE(idf_coolingCoil.isEmpty(Coil_Cooling_WaterFields::CondensateCollectionWaterStorageTankName)); @@ -262,14 +283,19 @@ TEST_F(EnergyPlusFixture, ForwardTranslator_CoilSystemCoolingWater_WithCompanion EXPECT_EQ("Autosize", idf_companionCoil.getString(Coil_Cooling_WaterFields::DesignInletAirTemperature).get()); EXPECT_EQ("Autosize", idf_companionCoil.getString(Coil_Cooling_WaterFields::DesignInletAirHumidityRatio).get()); EXPECT_EQ("Autosize", idf_companionCoil.getString(Coil_Cooling_WaterFields::DesignOutletAirHumidityRatio).get()); + EXPECT_EQ(companionCoilUsedForHeatRecovery.waterInletModelObject().get().nameString(), idf_companionCoil.getString(Coil_Cooling_WaterFields::WaterInletNodeName).get()); + EXPECT_EQ("Companion Coil Water Outlet Node", idf_companionCoil.getString(Coil_Cooling_WaterFields::WaterOutletNodeName).get()); EXPECT_EQ(companionCoilUsedForHeatRecovery.waterOutletModelObject().get().nameString(), idf_companionCoil.getString(Coil_Cooling_WaterFields::WaterOutletNodeName).get()); + EXPECT_EQ("Pipe Outlet to Companion Coil Water Inlet Node", idf_companionCoil.getString(Coil_Cooling_WaterFields::WaterInletNodeName).get()); + EXPECT_EQ(idfController.getString(Controller_OutdoorAirFields::ReliefAirOutletNodeName).get(), idf_companionCoil.getString(Coil_Cooling_WaterFields::AirInletNodeName).get()); - EXPECT_EQ(coilSystemCoolingWater.companionCoilUsedForHeatRecovery().get().nameString() + " Exhaust Outlet Node", - idf_companionCoil.getString(Coil_Cooling_WaterFields::AirOutletNodeName).get()); // FIXME + EXPECT_EQ("Return to OA System Node", idf_companionCoil.getString(Coil_Cooling_WaterFields::AirInletNodeName).get()); + EXPECT_EQ("Relief Node", idf_companionCoil.getString(Coil_Cooling_WaterFields::AirOutletNodeName).get()); + EXPECT_EQ("SimpleAnalysis", idf_companionCoil.getString(Coil_Cooling_WaterFields::TypeofAnalysis).get()); EXPECT_EQ("CrossFlow", idf_companionCoil.getString(Coil_Cooling_WaterFields::HeatExchangerConfiguration).get()); EXPECT_TRUE(idf_companionCoil.isEmpty(Coil_Cooling_WaterFields::CondensateCollectionWaterStorageTankName)); From 51a0ae3b0402ec144c643b9255a672e84a464dfb Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Mon, 5 May 2025 18:07:07 +0200 Subject: [PATCH 14/16] Fixup the FIXME --- ...ForwardTranslateCoilSystemCoolingWater.cpp | 51 ++++++++----------- 1 file changed, 22 insertions(+), 29 deletions(-) diff --git a/src/energyplus/ForwardTranslator/ForwardTranslateCoilSystemCoolingWater.cpp b/src/energyplus/ForwardTranslator/ForwardTranslateCoilSystemCoolingWater.cpp index 84d0ca4dfb1..669feab5f06 100644 --- a/src/energyplus/ForwardTranslator/ForwardTranslateCoilSystemCoolingWater.cpp +++ b/src/energyplus/ForwardTranslator/ForwardTranslateCoilSystemCoolingWater.cpp @@ -75,35 +75,27 @@ namespace energyplus { idf->setString(Coil_Cooling_WaterFields::AirOutletNodeName, airOutletNodeName); // Add IddObjectType::Coil_Cooling_Water_DetailedGeometry if implemented } else { - // Shouldn't happen, accepts only Coil:Cooling:Water or Coil:Cooling:Water:DetailedGeometry - // Shouldn't happen, accepts only Coil:Cooling:DX:SingleSpeed or Coil:Cooling:DX:VariableSpeed - LOG(Fatal, modelObject.briefDescription() - << " appears to have a cooling coil that shouldn't have been accepted: " << coolingCoil.briefDescription()); - OS_ASSERT(false); - } - } - if (auto coilCoolingWater = coolingCoil.optionalCast()) { - if (auto controller = coilCoolingWater->controllerWaterCoil()) { - if (auto idf = translateAndMapModelObject(controller.get())) { - idf->setString(Controller_WaterCoilFields::SensorNodeName, airOutletNodeName); - } + // Shouldn't happen, accepts only Coil:Cooling:Water CoilSystem:Cooling:Water:HeatExchangerAssisted (or Coil:Cooling:Water:DetailedGeometry, not wrapped) + LOG_AND_THROW(modelObject.briefDescription() + << " appears to have a cooling coil that shouldn't have been accepted: " << coolingCoil.briefDescription()); } } - // Need a SPM:MixedAir on the Coil:Cooling:Water outlet node (that we **created** just above in IDF directly, so it won't get picked up by the + // TODO: is that true?! + // Need a SPM:MixedAir on the outlet node (that we **created** just above in IDF directly, so it won't get picked up by the // ForwardTranslateAirLoopHVAC method) if (boost::optional airLoop_ = modelObject.airLoopHVAC()) { std::vector fans; std::vector supplyComponents = airLoop_->supplyComponents(); - for (auto it = supplyComponents.begin(); it != supplyComponents.end(); ++it) { - if (auto fan_ = it->optionalCast()) { + for (auto& supplyComponent : supplyComponents) { + if (auto fan_ = supplyComponent.optionalCast()) { fans.insert(fans.begin(), std::move(*fan_)); - } else if (auto fan_ = it->optionalCast()) { + } else if (auto fan_ = supplyComponent.optionalCast()) { fans.insert(fans.begin(), std::move(*fan_)); - } else if (auto fan_ = it->optionalCast()) { + } else if (auto fan_ = supplyComponent.optionalCast()) { fans.insert(fans.begin(), std::move(*fan_)); - } else if (auto fan_ = it->optionalCast()) { + } else if (auto fan_ = supplyComponent.optionalCast()) { fans.insert(fans.begin(), std::move(*fan_)); } } @@ -178,19 +170,20 @@ namespace energyplus { // Companion Coil Used For Heat Recovery: Optional Object if (boost::optional companionCoilUsedForHeatRecovery_ = modelObject.companionCoilUsedForHeatRecovery()) { - if (boost::optional wo_ = translateAndMapModelObject(companionCoilUsedForHeatRecovery_.get())) { - idfObject.setString(CoilSystem_Cooling_WaterFields::CompanionCoilUsedForHeatRecovery, wo_->nameString()); - if (wo_->iddObject().type() == IddObjectType::Coil_Cooling_Water) { - wo_->setString(Coil_Cooling_WaterFields::AirInletNodeName, modelObject.airLoopHVACOutdoorAirSystem()->reliefAirModelObject()->nameString()); - wo_->setString(Coil_Cooling_WaterFields::AirOutletNodeName, wo_->nameString() + " Exhaust Outlet Node"); // FIXME - // Add IddObjectType::Coil_Cooling_Water_DetailedGeometry if implemented + if (auto comp_ = companionCoilUsedForHeatRecovery_->optionalCast()) { + if (comp_->airInletModelObject()) { + if (boost::optional idf_ = translateAndMapModelObject(companionCoilUsedForHeatRecovery_.get())) { + idfObject.setString(CoilSystem_Cooling_WaterFields::CompanionCoilUsedForHeatRecovery, idf_->nameString()); + } } else { - // Shouldn't happen, accepts only Coil:Cooling:Water or Coil:Cooling:Water:DetailedGeometry - // Shouldn't happen, accepts only Coil:Cooling:DX:SingleSpeed or Coil:Cooling:DX:VariableSpeed - LOG(Fatal, modelObject.briefDescription() << " appears to have a companion coil used for heat recovery that shouldn't have been accepted: " - << companionCoilUsedForHeatRecovery_.get().briefDescription()); - OS_ASSERT(false); + LOG(Warn, modelObject.briefDescription() + << " has a companion coil used for heat recovery that is not connected to an AirLoopHVAC / AirLoopHVACOutdoorAirSystem" + << " and will not be translated:" << comp_->briefDescription()); } + } else { + // Shouldn't happen, accepts only Coil:Cooling:Water + LOG_AND_THROW(modelObject.briefDescription() << " appears to have a companion coil used for heat recovery that shouldn't have been accepted: " + << companionCoilUsedForHeatRecovery_.get().briefDescription()); } } From 61ea54de103372086c2c78804a252875ac6c1241 Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Mon, 5 May 2025 18:35:11 +0200 Subject: [PATCH 15/16] Add a FT Gtest for a Case with a CoilSystemWaterHXAssisted inside a CoilSystemWater --- .../Test/CoilSystemCoolingWater_GTest.cpp | 199 +++++++++++++++++- 1 file changed, 198 insertions(+), 1 deletion(-) diff --git a/src/energyplus/Test/CoilSystemCoolingWater_GTest.cpp b/src/energyplus/Test/CoilSystemCoolingWater_GTest.cpp index d66716170aa..3746e2f41f7 100644 --- a/src/energyplus/Test/CoilSystemCoolingWater_GTest.cpp +++ b/src/energyplus/Test/CoilSystemCoolingWater_GTest.cpp @@ -12,11 +12,13 @@ #include "../../model/CoilSystemCoolingWater_Impl.hpp" #include "../../model/Schedule.hpp" #include "../../model/Schedule_Impl.hpp" +#include "../../model/ScheduleConstant.hpp" #include "../../model/CoilCoolingWater.hpp" #include "../../model/CoilCoolingWater_Impl.hpp" #include "../../model/CoilSystemCoolingWaterHeatExchangerAssisted.hpp" #include "../../model/CoilSystemCoolingWaterHeatExchangerAssisted_Impl.hpp" - +#include "../../model/HeatExchangerAirToAirSensibleAndLatent.hpp" +#include "../../model/HeatExchangerAirToAirSensibleAndLatent_Impl.hpp" #include "../../model/AirLoopHVAC.hpp" #include "../../model/AirLoopHVACUnitarySystem.hpp" #include "../../model/Node.hpp" @@ -39,6 +41,8 @@ #include #include #include +#include +#include #include #include #include @@ -303,3 +307,196 @@ TEST_F(EnergyPlusFixture, ForwardTranslator_CoilSystemCoolingWater_WithCompanion EXPECT_EQ(0, w.getObjectsByType(IddObjectType::Controller_WaterCoil).size()); } + +TEST_F(EnergyPlusFixture, ForwardTranslator_CoilSystemCoolingWater_HXAssisted) { + ForwardTranslator ft; + + Model m; + + CoilSystemCoolingWaterHeatExchangerAssisted coilSystemHXAssisted(m); + coilSystemHXAssisted.setName("HX Assisted"); + auto cc = coilSystemHXAssisted.coolingCoil().cast(); + auto hx = coilSystemHXAssisted.heatExchanger().cast(); + cc.setName("Primary Cooling Coil inside HXAssisted"); + hx.setName("HX AirToAir"); + CoilSystemCoolingWater coilSystemCoolingWater(m, coilSystemHXAssisted); + coilSystemCoolingWater.setName("My CoilSystemCoolingWater"); + + ScheduleConstant availabilitySchedule(m); + availabilitySchedule.setName("availabilitySchedule"); + EXPECT_TRUE(coilSystemCoolingWater.setAvailabilitySchedule(availabilitySchedule)); + EXPECT_TRUE(coilSystemCoolingWater.setDehumidificationControlType("CoolReheat")); + EXPECT_TRUE(coilSystemCoolingWater.setRunonSensibleLoad(false)); // Opposite from IDD default + EXPECT_TRUE(coilSystemCoolingWater.setRunonLatentLoad(true)); // Opposite from IDD default + EXPECT_TRUE(coilSystemCoolingWater.setMinimumAirToWaterTemperatureOffset(1.0)); + EXPECT_TRUE(coilSystemCoolingWater.setEconomizerLockout(false)); // Opposite from IDD default + EXPECT_TRUE(coilSystemCoolingWater.setMinimumWaterLoopTemperatureForHeatRecovery(1.2)); + CoilCoolingWater companionCoilUsedForHeatRecovery(m); + companionCoilUsedForHeatRecovery.setName("CompanionCoilUsedForHeatRecovery"); + EXPECT_TRUE(coilSystemCoolingWater.setCompanionCoilUsedForHeatRecovery(companionCoilUsedForHeatRecovery)); + + AirLoopHVAC airLoop(m); + Node supplyOutletNode = airLoop.supplyOutletNode(); + ControllerOutdoorAir controller1(m); + AirLoopHVACOutdoorAirSystem oaSystem(m, controller1); + EXPECT_TRUE(oaSystem.addToNode(supplyOutletNode)); + Node oaNode = oaSystem.outboardOANode().get(); + EXPECT_TRUE(coilSystemCoolingWater.addToNode(oaNode)); + Node reliefNode = oaSystem.outboardReliefNode().get(); + EXPECT_TRUE(companionCoilUsedForHeatRecovery.addToNode(reliefNode)); + + oaNode.setName("Outdoor Air Inlet Node"); + reliefNode.setName("Relief Node"); + oaSystem.reliefAirModelObject()->setName("Return to OA System Node"); + oaSystem.outdoorAirModelObject()->setName("OA System Outlet to Mixed Node"); + airLoop.mixedAirNode()->setName("Mixed Air Node"); + airLoop.supplyInletNode().setName("Supply Inlet Node"); + airLoop.supplyOutletNode().setName("Supply Outlet Node"); + + // They must be connected to a PlantLoop too + PlantLoop chw_p(m); + chw_p.addDemandBranchForComponent(cc); + PipeAdiabatic pipe(m); + Node coolingOutlet = cc.waterOutletModelObject()->cast(); + EXPECT_TRUE(pipe.addToNode(coolingOutlet)); + Node pipeOutlet = pipe.outletModelObject()->cast(); + EXPECT_TRUE(companionCoilUsedForHeatRecovery.addToNode(pipeOutlet)); + + cc.waterInletModelObject()->setName("Cooling Coil Water Inlet Node"); + cc.waterOutletModelObject()->setName("Cooling Coil Water Outlet to Pipe Inlet Node"); + pipe.outletModelObject()->setName("Pipe Outlet to Companion Coil Water Inlet Node"); + companionCoilUsedForHeatRecovery.waterOutletModelObject()->setName("Companion Coil Water Outlet Node"); + + const Workspace w = ft.translateModel(m); + + const auto idfCoilSystems = w.getObjectsByType(IddObjectType::CoilSystem_Cooling_Water); + ASSERT_EQ(1u, idfCoilSystems.size()); + const auto& idfCoilSystem = idfCoilSystems.front(); + + WorkspaceObjectVector idfCoils(w.getObjectsByType(IddObjectType::Coil_Cooling_Water)); + ASSERT_EQ(2u, idfCoils.size()); + + WorkspaceObjectVector idfControllers(w.getObjectsByType(IddObjectType::Controller_OutdoorAir)); + ASSERT_EQ(1u, idfControllers.size()); + const auto& idfController = idfControllers.front(); + + // Go from AirLoopHVAC to BranchList to Branch + WorkspaceObjectVector idf_airloops = w.getObjectsByType(IddObjectType::AirLoopHVAC); + ASSERT_EQ(1u, idf_airloops.size()); + + WorkspaceObject& idf_airLoopHVAC = idf_airloops.front(); + WorkspaceObject idf_brlist = idf_airLoopHVAC.getTarget(AirLoopHVACFields::BranchListName).get(); + + // Should have one branch only + ASSERT_EQ(1u, idf_brlist.extensibleGroups().size()); + auto w_eg = idf_brlist.extensibleGroups()[0].cast(); + WorkspaceObject idf_branch = w_eg.getTarget(BranchListExtensibleFields::BranchName).get(); + + // There should be only one equipment on the branch + ASSERT_EQ(1u, idf_branch.extensibleGroups().size()); + auto w_eg2 = idf_branch.extensibleGroups()[0].cast(); + + /* EXPECT_EQ(w_eg2.getString(BranchExtensibleFields::ComponentInletNodeName).get(), coilSystemCoolingWater.inletModelObject().get().nameString()); + EXPECT_EQ(w_eg2.getString(BranchExtensibleFields::ComponentOutletNodeName).get(), coilSystemCoolingWater.outletModelObject().get().nameString()); */ + EXPECT_EQ("AirLoopHVAC:OutdoorAirSystem", w_eg2.getString(BranchExtensibleFields::ComponentObjectType).get()); + + EXPECT_EQ(coilSystemCoolingWater.inletModelObject().get().nameString(), + idfCoilSystem.getString(CoilSystem_Cooling_WaterFields::AirInletNodeName).get()); + EXPECT_EQ(coilSystemCoolingWater.outletModelObject().get().nameString(), + idfCoilSystem.getString(CoilSystem_Cooling_WaterFields::AirOutletNodeName).get()); + EXPECT_EQ(availabilitySchedule.nameString(), idfCoilSystem.getString(CoilSystem_Cooling_WaterFields::AvailabilityScheduleName).get()); + EXPECT_EQ("CoilSystem:Cooling:Water:HeatExchangerAssisted", idfCoilSystem.getString(CoilSystem_Cooling_WaterFields::CoolingCoilObjectType).get()); + EXPECT_EQ(coilSystemCoolingWater.coolingCoil().nameString(), idfCoilSystem.getString(CoilSystem_Cooling_WaterFields::CoolingCoilName).get()); + EXPECT_EQ("CoolReheat", idfCoilSystem.getString(CoilSystem_Cooling_WaterFields::DehumidificationControlType).get()); + EXPECT_EQ("No", idfCoilSystem.getString(CoilSystem_Cooling_WaterFields::RunonSensibleLoad).get()); + EXPECT_EQ("Yes", idfCoilSystem.getString(CoilSystem_Cooling_WaterFields::RunonLatentLoad).get()); + EXPECT_EQ(1.0, idfCoilSystem.getDouble(CoilSystem_Cooling_WaterFields::MinimumAirToWaterTemperatureOffset).get()); + EXPECT_EQ("No", idfCoilSystem.getString(CoilSystem_Cooling_WaterFields::EconomizerLockout).get()); + EXPECT_EQ(1.2, idfCoilSystem.getDouble(CoilSystem_Cooling_WaterFields::MinimumWaterLoopTemperatureForHeatRecovery).get()); + EXPECT_EQ(companionCoilUsedForHeatRecovery.nameString(), + idfCoilSystem.getString(CoilSystem_Cooling_WaterFields::CompanionCoilUsedForHeatRecovery).get()); + + WorkspaceObject idf_hxAssisted = idfCoilSystem.getTarget(CoilSystem_Cooling_WaterFields::CoolingCoilName).get(); + EXPECT_EQ("HeatExchanger:AirToAir:SensibleAndLatent", + idf_hxAssisted.getString(CoilSystem_Cooling_Water_HeatExchangerAssistedFields::HeatExchangerObjectType).get()); + EXPECT_EQ("HX AirToAir", idf_hxAssisted.getString(CoilSystem_Cooling_Water_HeatExchangerAssistedFields::HeatExchangerName).get()); + EXPECT_EQ("Coil:Cooling:Water", idf_hxAssisted.getString(CoilSystem_Cooling_Water_HeatExchangerAssistedFields::CoolingCoilObjectType).get()); + EXPECT_EQ("Primary Cooling Coil inside HXAssisted", + idf_hxAssisted.getString(CoilSystem_Cooling_Water_HeatExchangerAssistedFields::CoolingCoilName).get()); + + // This fails for some reason? + // auto idf_hx = idf_hxAssisted.getTarget(CoilSystem_Cooling_Water_HeatExchangerAssistedFields::HeatExchangerName).get(); + ASSERT_EQ(1, w.getObjectsByType(IddObjectType::HeatExchanger_AirToAir_SensibleAndLatent).size()); + auto idf_hx = w.getObjectsByType(IddObjectType::HeatExchanger_AirToAir_SensibleAndLatent).front(); + + EXPECT_EQ(hx.nameString(), idf_hx.getString(HeatExchanger_AirToAir_SensibleAndLatentFields::Name).get()); + EXPECT_EQ(idfCoilSystem.getString(CoilSystem_Cooling_WaterFields::AirInletNodeName).get(), + idf_hx.getString(HeatExchanger_AirToAir_SensibleAndLatentFields::SupplyAirInletNodeName).get()); + EXPECT_EQ("Outdoor Air Inlet Node", idf_hx.getString(HeatExchanger_AirToAir_SensibleAndLatentFields::SupplyAirInletNodeName).get()); + + EXPECT_EQ(idfCoilSystem.getString(CoilSystem_Cooling_WaterFields::AirOutletNodeName).get(), + idf_hx.getString(HeatExchanger_AirToAir_SensibleAndLatentFields::ExhaustAirOutletNodeName).get()); + EXPECT_EQ("OA System Outlet to Mixed Node", idf_hx.getString(HeatExchanger_AirToAir_SensibleAndLatentFields::ExhaustAirOutletNodeName).get()); + + EXPECT_EQ("HX Assisted HX Supply Air Outlet - Cooling Inlet Node", + idf_hx.getString(HeatExchanger_AirToAir_SensibleAndLatentFields::SupplyAirOutletNodeName).get()); + EXPECT_EQ("HX Assisted HX Exhaust Air Inlet - Cooling Outlet Node", + idf_hx.getString(HeatExchanger_AirToAir_SensibleAndLatentFields::ExhaustAirInletNodeName).get()); + + WorkspaceObject idf_coolingCoil = idf_hxAssisted.getTarget(CoilSystem_Cooling_Water_HeatExchangerAssistedFields::CoolingCoilName).get(); + WorkspaceObject idf_companionCoil = idfCoilSystem.getTarget(CoilSystem_Cooling_WaterFields::CompanionCoilUsedForHeatRecovery).get(); + + EXPECT_EQ("Primary Cooling Coil inside HXAssisted", idf_coolingCoil.getString(Coil_Cooling_WaterFields::Name).get()); + EXPECT_EQ("Always On Discrete", idf_coolingCoil.getString(Coil_Cooling_WaterFields::AvailabilityScheduleName).get()); + EXPECT_EQ("Autosize", idf_coolingCoil.getString(Coil_Cooling_WaterFields::DesignWaterFlowRate).get()); + EXPECT_EQ("Autosize", idf_coolingCoil.getString(Coil_Cooling_WaterFields::DesignAirFlowRate).get()); + EXPECT_EQ("Autosize", idf_coolingCoil.getString(Coil_Cooling_WaterFields::DesignInletWaterTemperature).get()); + EXPECT_EQ("Autosize", idf_coolingCoil.getString(Coil_Cooling_WaterFields::DesignInletAirTemperature).get()); + EXPECT_EQ("Autosize", idf_coolingCoil.getString(Coil_Cooling_WaterFields::DesignInletAirHumidityRatio).get()); + EXPECT_EQ("Autosize", idf_coolingCoil.getString(Coil_Cooling_WaterFields::DesignOutletAirHumidityRatio).get()); + + EXPECT_EQ(cc.waterInletModelObject().get().nameString(), idf_coolingCoil.getString(Coil_Cooling_WaterFields::WaterInletNodeName).get()); + EXPECT_EQ("Cooling Coil Water Inlet Node", idf_coolingCoil.getString(Coil_Cooling_WaterFields::WaterInletNodeName).get()); + EXPECT_EQ(cc.waterOutletModelObject().get().nameString(), idf_coolingCoil.getString(Coil_Cooling_WaterFields::WaterOutletNodeName).get()); + EXPECT_EQ("Cooling Coil Water Outlet to Pipe Inlet Node", idf_coolingCoil.getString(Coil_Cooling_WaterFields::WaterOutletNodeName).get()); + EXPECT_EQ(idf_hx.getString(HeatExchanger_AirToAir_SensibleAndLatentFields::SupplyAirOutletNodeName).get(), + idf_coolingCoil.getString(Coil_Cooling_WaterFields::AirInletNodeName).get()); + EXPECT_EQ("HX Assisted HX Supply Air Outlet - Cooling Inlet Node", idf_coolingCoil.getString(Coil_Cooling_WaterFields::AirInletNodeName).get()); + EXPECT_EQ(idf_hx.getString(HeatExchanger_AirToAir_SensibleAndLatentFields::ExhaustAirInletNodeName).get(), + idf_coolingCoil.getString(Coil_Cooling_WaterFields::AirOutletNodeName).get()); + EXPECT_EQ("HX Assisted HX Exhaust Air Inlet - Cooling Outlet Node", idf_coolingCoil.getString(Coil_Cooling_WaterFields::AirOutletNodeName).get()); + + EXPECT_EQ("SimpleAnalysis", idf_coolingCoil.getString(Coil_Cooling_WaterFields::TypeofAnalysis).get()); + EXPECT_EQ("CrossFlow", idf_coolingCoil.getString(Coil_Cooling_WaterFields::HeatExchangerConfiguration).get()); + EXPECT_TRUE(idf_coolingCoil.isEmpty(Coil_Cooling_WaterFields::CondensateCollectionWaterStorageTankName)); + EXPECT_TRUE(idf_coolingCoil.isEmpty(Coil_Cooling_WaterFields::DesignWaterTemperatureDifference)); + + EXPECT_EQ(coilSystemCoolingWater.companionCoilUsedForHeatRecovery().get().nameString(), + idf_companionCoil.getString(Coil_Cooling_WaterFields::Name).get()); + EXPECT_EQ("Always On Discrete", idf_companionCoil.getString(Coil_Cooling_WaterFields::AvailabilityScheduleName).get()); + EXPECT_EQ("Autosize", idf_companionCoil.getString(Coil_Cooling_WaterFields::DesignWaterFlowRate).get()); + EXPECT_EQ("Autosize", idf_companionCoil.getString(Coil_Cooling_WaterFields::DesignAirFlowRate).get()); + EXPECT_EQ("Autosize", idf_companionCoil.getString(Coil_Cooling_WaterFields::DesignInletWaterTemperature).get()); + EXPECT_EQ("Autosize", idf_companionCoil.getString(Coil_Cooling_WaterFields::DesignInletAirTemperature).get()); + EXPECT_EQ("Autosize", idf_companionCoil.getString(Coil_Cooling_WaterFields::DesignInletAirHumidityRatio).get()); + EXPECT_EQ("Autosize", idf_companionCoil.getString(Coil_Cooling_WaterFields::DesignOutletAirHumidityRatio).get()); + + EXPECT_EQ(companionCoilUsedForHeatRecovery.waterInletModelObject().get().nameString(), + idf_companionCoil.getString(Coil_Cooling_WaterFields::WaterInletNodeName).get()); + EXPECT_EQ("Companion Coil Water Outlet Node", idf_companionCoil.getString(Coil_Cooling_WaterFields::WaterOutletNodeName).get()); + EXPECT_EQ(companionCoilUsedForHeatRecovery.waterOutletModelObject().get().nameString(), + idf_companionCoil.getString(Coil_Cooling_WaterFields::WaterOutletNodeName).get()); + EXPECT_EQ("Pipe Outlet to Companion Coil Water Inlet Node", idf_companionCoil.getString(Coil_Cooling_WaterFields::WaterInletNodeName).get()); + + EXPECT_EQ(idfController.getString(Controller_OutdoorAirFields::ReliefAirOutletNodeName).get(), + idf_companionCoil.getString(Coil_Cooling_WaterFields::AirInletNodeName).get()); + EXPECT_EQ("Return to OA System Node", idf_companionCoil.getString(Coil_Cooling_WaterFields::AirInletNodeName).get()); + EXPECT_EQ("Relief Node", idf_companionCoil.getString(Coil_Cooling_WaterFields::AirOutletNodeName).get()); + + EXPECT_EQ("SimpleAnalysis", idf_companionCoil.getString(Coil_Cooling_WaterFields::TypeofAnalysis).get()); + EXPECT_EQ("CrossFlow", idf_companionCoil.getString(Coil_Cooling_WaterFields::HeatExchangerConfiguration).get()); + EXPECT_TRUE(idf_companionCoil.isEmpty(Coil_Cooling_WaterFields::CondensateCollectionWaterStorageTankName)); + EXPECT_TRUE(idf_companionCoil.isEmpty(Coil_Cooling_WaterFields::DesignWaterTemperatureDifference)); + + EXPECT_EQ(0, w.getObjectsByType(IddObjectType::Controller_WaterCoil).size()); +} From e1705d9fc1f6126da4df9868e89f6a3ed1d90f84 Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Mon, 5 May 2025 18:35:32 +0200 Subject: [PATCH 16/16] Fixup FT to handle CoilSystemWaterHXAssisted inside CoilSystemWater --- ...ForwardTranslateCoilSystemCoolingWater.cpp | 4 +++ ...ystemCoolingWaterHeatExchangerAssisted.cpp | 28 ++++++++++++++----- 2 files changed, 25 insertions(+), 7 deletions(-) diff --git a/src/energyplus/ForwardTranslator/ForwardTranslateCoilSystemCoolingWater.cpp b/src/energyplus/ForwardTranslator/ForwardTranslateCoilSystemCoolingWater.cpp index 669feab5f06..f167f8bc181 100644 --- a/src/energyplus/ForwardTranslator/ForwardTranslateCoilSystemCoolingWater.cpp +++ b/src/energyplus/ForwardTranslator/ForwardTranslateCoilSystemCoolingWater.cpp @@ -74,6 +74,10 @@ namespace energyplus { idf->setString(Coil_Cooling_WaterFields::AirInletNodeName, airInletNodeName); idf->setString(Coil_Cooling_WaterFields::AirOutletNodeName, airOutletNodeName); // Add IddObjectType::Coil_Cooling_Water_DetailedGeometry if implemented + } else if (idf->iddObject().type() == IddObjectType::CoilSystem_Cooling_Water_HeatExchangerAssisted) { + // The logic is built in the translateCoilSystemCoolingWaterHeatExchangerAssisted method + // Essentially it's the same as getting the HX (CoilSystem_Cooling_Water_HeatExchangerAssistedFields::HeatExchangerName) + // and setting HeatExchanger_AirToAir_SensibleAndLatentFields::SupplyAirInletNodeName & ExhaustAirOutletNodeName } else { // Shouldn't happen, accepts only Coil:Cooling:Water CoilSystem:Cooling:Water:HeatExchangerAssisted (or Coil:Cooling:Water:DetailedGeometry, not wrapped) LOG_AND_THROW(modelObject.briefDescription() diff --git a/src/energyplus/ForwardTranslator/ForwardTranslateCoilSystemCoolingWaterHeatExchangerAssisted.cpp b/src/energyplus/ForwardTranslator/ForwardTranslateCoilSystemCoolingWaterHeatExchangerAssisted.cpp index 3cd67ee19a2..e54247683b6 100644 --- a/src/energyplus/ForwardTranslator/ForwardTranslateCoilSystemCoolingWaterHeatExchangerAssisted.cpp +++ b/src/energyplus/ForwardTranslator/ForwardTranslateCoilSystemCoolingWaterHeatExchangerAssisted.cpp @@ -14,6 +14,8 @@ #include "../../model/CoilCoolingWater_Impl.hpp" #include "../../model/ControllerWaterCoil.hpp" #include "../../model/ControllerWaterCoil_Impl.hpp" +#include "../../model/CoilSystemCoolingWater.hpp" +#include "../../model/CoilSystemCoolingWater_Impl.hpp" #include "../../model/AirLoopHVAC.hpp" #include "../../model/FanVariableVolume.hpp" @@ -47,18 +49,30 @@ namespace energyplus { IdfObject idfObject = createRegisterAndNameIdfObject(openstudio::IddObjectType::CoilSystem_Cooling_Water_HeatExchangerAssisted, modelObject); std::string hxSupplyAirInletNodeName; + std::string hxExhaustAirOutletNodeName; // InletNodeName if (auto mo = modelObject.inletModelObject()) { if (auto node = mo->optionalCast()) { hxSupplyAirInletNodeName = node->nameString(); } - } - - std::string hxExhaustAirOutletNodeName; - // OutletNodeName - if (auto mo = modelObject.outletModelObject()) { - if (auto node = mo->optionalCast()) { - hxExhaustAirOutletNodeName = node->nameString(); + // OutletNodeName + if (auto mo = modelObject.outletModelObject()) { + if (auto node = mo->optionalCast()) { + hxExhaustAirOutletNodeName = node->nameString(); + } + } + } else if (auto comp_ = modelObject.containingHVACComponent()) { + if (auto coilSystem_ = comp_->optionalCast()) { + if (auto mo = coilSystem_->inletModelObject()) { + if (auto node = mo->optionalCast()) { + hxSupplyAirInletNodeName = node->nameString(); + } + } + if (auto mo = coilSystem_->outletModelObject()) { + if (auto node = mo->optionalCast()) { + hxExhaustAirOutletNodeName = node->nameString(); + } + } } }