-
Notifications
You must be signed in to change notification settings - Fork 222
#5350 - Changes to Support CoilSystem:Cooling:Water #5404
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
…companion to CoilCoolingWater per IDD/IO ref CoilSystem:Cooling:Water:HeatExchangerAssisted isn't WaterToAirComponent
…t ctor to remove() and LOG_AND_THROW
…lSystemCoolingWater
…nnect the water side for the children coils
… 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.
…wed anywhere in a ZoneHVAC/HVACComponent
…t (not supported AFAIK)
| Schedule availabilitySchedule() const; | ||
|
|
||
| WaterToAirComponent coolingCoil() const; | ||
| HVACComponent coolingCoil() const; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- CoilCoolingWater > WaterToAirComponent > HVACComponent
- CoilSystemCoolingWaterHeatExchangerAssisted > StraightComponent > HVACComponent
| double minimumWaterLoopTemperatureForHeatRecovery() const; | ||
|
|
||
| boost::optional<WaterToAirComponent> companionCoilUsedForHeatRecovery() const; | ||
| boost::optional<HVACComponent> companionCoilUsedForHeatRecovery() const; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Technically the return type could stay WaterToAirComponent because the only supported type "AT THE MOMENT" is CoilCoolingWater. And I don't want to have to break API later on, so I also changed it.
| // This function will connect the air side only, for water side, use the coils directly | ||
| virtual bool addToNode(Node& node) override; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Comment was misleading.
| // Need to disconnect the Water side of the coil(s) before removing | ||
| virtual std::vector<IdfObject> remove() override; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Override remove.
| // This function will connect the air side only, for water side, use the coils directly | ||
| virtual bool addToNode(Node& node) override; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Comment was misleading (copied)
| TEST_F(ModelFixture, CoilSystemCoolingWater_ExplicitCtor) { | ||
| { | ||
| Model m; | ||
| CoilCoolingWater coolingCoil(m); | ||
| CoilSystemCoolingWater coilSystem(m, coolingCoil); | ||
| EXPECT_EQ(coolingCoil, coilSystem.coolingCoil()); | ||
| EXPECT_EQ(1, m.getConcreteModelObjects<CoilSystemCoolingWater>().size()); | ||
| EXPECT_EQ(1, m.getConcreteModelObjects<CoilCoolingWater>().size()); | ||
| } | ||
|
|
||
| { | ||
| Model m; | ||
| CoilSystemCoolingWaterHeatExchangerAssisted coolingCoil(m); | ||
| CoilSystemCoolingWater coilSystem(m, coolingCoil); | ||
| EXPECT_EQ(coolingCoil, coilSystem.coolingCoil()); | ||
| EXPECT_EQ(1, m.getConcreteModelObjects<CoilSystemCoolingWater>().size()); | ||
| EXPECT_EQ(1, m.getConcreteModelObjects<CoilSystemCoolingWaterHeatExchangerAssisted>().size()); | ||
| } | ||
|
|
||
| { | ||
| Model m; | ||
| CoilCoolingDXSingleSpeed coolingCoil(m); | ||
| EXPECT_ANY_THROW(CoilSystemCoolingWater coilSystem(m, coolingCoil)); | ||
| EXPECT_EQ(0, m.getConcreteModelObjects<CoilSystemCoolingWater>().size()); | ||
| EXPECT_EQ(1, m.getConcreteModelObjects<CoilCoolingDXSingleSpeed>().size()); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Test the explicit ctor, which highlighted that we weren't handling the last case where you pass a wrogn coil type.
| boost::optional<HVACComponent> CoilSystemCoolingWater_Impl::containingHVACComponent() const { | ||
| // AirLoopHVACUnitarySystem | ||
| std::vector<AirLoopHVACUnitarySystem> airLoopHVACUnitarySystems = this->model().getConcreteModelObjects<AirLoopHVACUnitarySystem>(); | ||
|
|
||
| for (const auto& airLoopHVACUnitarySystem : airLoopHVACUnitarySystems) { | ||
| if (boost::optional<HVACComponent> coolingCoil = airLoopHVACUnitarySystem.coolingCoil()) { | ||
| if (coolingCoil->handle() == this->handle()) { | ||
| return airLoopHVACUnitarySystem; | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
CANNOT be part of a containing HVACComponent AFAIK
| boost::optional<ZoneHVACComponent> CoilSystemCoolingWater_Impl::containingZoneHVACComponent() const { | ||
|
|
||
| // ZoneHVACFourPipeFanCoil | ||
| std::vector<ZoneHVACFourPipeFanCoil> zoneHVACFourPipeFanCoils; | ||
|
|
||
| zoneHVACFourPipeFanCoils = this->model().getConcreteModelObjects<ZoneHVACFourPipeFanCoil>(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
CANNOT be part of a containing ZoneHVACComponent AFAIK
This is from the E+ IDD, there are ZERO possible references.
| bool ok = true; | ||
| ok = setCoolingCoil(coolingCoil); | ||
| if (!ok) { | ||
| remove(); | ||
| LOG_AND_THROW("Unable to set " << briefDescription() << "'s Cooling Coil " << coolingCoil.briefDescription() << "."); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is how you handle the explicit ctor being passed the wrong object, which can definitely happen.
| 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<CoilCoolingWater>(); | ||
| 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<Node>(); | ||
| 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<CoilSystemCoolingWater>().size()); | ||
| EXPECT_EQ(1, m.getConcreteModelObjects<CoilSystemCoolingWaterHeatExchangerAssisted>().size()); | ||
| EXPECT_EQ(2, m.getConcreteModelObjects<CoilCoolingWater>().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<ControllerWaterCoil>().size()); | ||
|
|
||
| auto coilSystemClone = coilSystem.clone(m).cast<CoilSystemCoolingWater>(); | ||
|
|
||
| EXPECT_EQ(2, m.getConcreteModelObjects<CoilSystemCoolingWater>().size()); | ||
| EXPECT_EQ(2, m.getConcreteModelObjects<CoilSystemCoolingWaterHeatExchangerAssisted>().size()); | ||
| EXPECT_EQ(4, m.getConcreteModelObjects<CoilCoolingWater>().size()); | ||
| EXPECT_EQ(0, m.getConcreteModelObjects<ControllerWaterCoil>().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<CoilSystemCoolingWaterHeatExchangerAssisted>()); | ||
| EXPECT_NE(coilSystemHXAssisted.handle(), coilSystemClone.coolingCoil().handle()); | ||
|
|
||
| ASSERT_TRUE(coilSystemClone.companionCoilUsedForHeatRecovery()); | ||
| EXPECT_TRUE(coilSystemClone.companionCoilUsedForHeatRecovery()->optionalCast<CoilCoolingWater>()); | ||
| EXPECT_NE(companionCoil.handle(), coilSystemClone.companionCoilUsedForHeatRecovery()->handle()); | ||
|
|
||
| // Clone into another Model | ||
| { | ||
| Model m2; | ||
| auto coilSystemClone = coilSystem.clone(m2).cast<CoilSystemCoolingWater>(); | ||
|
|
||
| EXPECT_EQ(1, m2.getConcreteModelObjects<CoilSystemCoolingWater>().size()); | ||
| EXPECT_EQ(1, m2.getConcreteModelObjects<CoilSystemCoolingWaterHeatExchangerAssisted>().size()); | ||
| EXPECT_EQ(2, m2.getConcreteModelObjects<CoilCoolingWater>().size()); | ||
| EXPECT_EQ(0, m2.getConcreteModelObjects<ControllerWaterCoil>().size()); | ||
|
|
||
| EXPECT_FALSE(coilSystemClone.airLoopHVAC()); | ||
| EXPECT_FALSE(coilSystemClone.inletModelObject()); | ||
| EXPECT_FALSE(coilSystemClone.outletModelObject()); | ||
|
|
||
| EXPECT_TRUE(coilSystemClone.coolingCoil().optionalCast<CoilSystemCoolingWaterHeatExchangerAssisted>()); | ||
| EXPECT_NE(coilSystemHXAssisted.handle(), coilSystemClone.coolingCoil().handle()); | ||
|
|
||
| ASSERT_TRUE(coilSystemClone.companionCoilUsedForHeatRecovery()); | ||
| EXPECT_TRUE(coilSystemClone.companionCoilUsedForHeatRecovery()->optionalCast<CoilCoolingWater>()); | ||
| EXPECT_NE(companionCoil.handle(), coilSystemClone.companionCoilUsedForHeatRecovery()->handle()); | ||
|
|
||
| // Remove | ||
| coilSystemClone.remove(); | ||
| EXPECT_EQ(0, m2.getConcreteModelObjects<CoilSystemCoolingWater>().size()); | ||
| EXPECT_EQ(0, m2.getConcreteModelObjects<CoilSystemCoolingWaterHeatExchangerAssisted>().size()); | ||
| EXPECT_EQ(0, m2.getConcreteModelObjects<CoilCoolingWater>().size()); | ||
| } | ||
|
|
||
| coilSystemClone.remove(); | ||
| EXPECT_EQ(1, m.getConcreteModelObjects<CoilSystemCoolingWater>().size()); | ||
| EXPECT_EQ(1, m.getConcreteModelObjects<CoilSystemCoolingWaterHeatExchangerAssisted>().size()); | ||
| EXPECT_EQ(2, m.getConcreteModelObjects<CoilCoolingWater>().size()); | ||
| EXPECT_EQ(0, m.getConcreteModelObjects<ControllerWaterCoil>().size()); | ||
|
|
||
| coilSystem.remove(); | ||
| EXPECT_EQ(0, m.getConcreteModelObjects<CoilSystemCoolingWater>().size()); | ||
| EXPECT_EQ(0, m.getConcreteModelObjects<CoilSystemCoolingWaterHeatExchangerAssisted>().size()); | ||
| EXPECT_EQ(0, m.getConcreteModelObjects<CoilCoolingWater>().size()); | ||
| EXPECT_EQ(0, m.getConcreteModelObjects<ControllerWaterCoil>().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 | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Extensive test for clone and remove. Lots were going wrong, and p.demandComponents was 0 because the connections were broken.
…ode (and getting them out of sync) in both ctors
…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"
| } 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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We gotta handle the CoilSystem_Cooling_Water_HeatExchangerAssisted case. I'm not doing it here because it's too complicated/awkward because we work with IdfObject, not WorkspaceObject, so theree isn't a getTarget available to set the HX Nodes
| std::string hxExhaustAirOutletNodeName; | ||
| // InletNodeName | ||
| if (auto mo = modelObject.inletModelObject()) { | ||
| if (auto node = mo->optionalCast<Node>()) { | ||
| hxSupplyAirInletNodeName = node->nameString(); | ||
| } | ||
| } | ||
|
|
||
| std::string hxExhaustAirOutletNodeName; | ||
| // OutletNodeName | ||
| if (auto mo = modelObject.outletModelObject()) { | ||
| if (auto node = mo->optionalCast<Node>()) { | ||
| hxExhaustAirOutletNodeName = node->nameString(); | ||
| // OutletNodeName | ||
| if (auto mo = modelObject.outletModelObject()) { | ||
| if (auto node = mo->optionalCast<Node>()) { | ||
| hxExhaustAirOutletNodeName = node->nameString(); | ||
| } | ||
| } | ||
| } else if (auto comp_ = modelObject.containingHVACComponent()) { | ||
| if (auto coilSystem_ = comp_->optionalCast<CoilSystemCoolingWater>()) { | ||
| if (auto mo = coilSystem_->inletModelObject()) { | ||
| if (auto node = mo->optionalCast<Node>()) { | ||
| hxSupplyAirInletNodeName = node->nameString(); | ||
| } | ||
| } | ||
| if (auto mo = coilSystem_->outletModelObject()) { | ||
| if (auto node = mo->optionalCast<Node>()) { | ||
| hxExhaustAirOutletNodeName = node->nameString(); | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
CoilSystem_Cooling_Water_HeatExchangerAssisted: when translating it, we check if there's a inletModelobject, in which case this is directly added onto an AirLoopHVAC.
Otherwise, get the inlet/outlet air nodes from the parent CoilSystemCoolingWater object if any
| // 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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@joseph-robertson not sure if that's just copy pasted fromCoilSystem_Cooling_Water_HeatExchangerAssisted or actually required.
| if (boost::optional<HVACComponent> companionCoilUsedForHeatRecovery_ = modelObject.companionCoilUsedForHeatRecovery()) { | ||
| if (auto comp_ = companionCoilUsedForHeatRecovery_->optionalCast<CoilCoolingWater>()) { | ||
| if (comp_->airInletModelObject()) { | ||
| if (boost::optional<IdfObject> 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()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No translating the companion coil if it's not connected by itself on an AirLoopHVAC/AirLoopHVACOutdoorAIrSystem
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I improved the testing for the cases you wrote, and I added a new one for CoilSystem_Cooling_Water_HeatExchangerAssisted
|
@joseph-robertson I'm going to merge this one back into yours so I can get (one) CI to run on it, but please review carefully. |
Pull request overview
Pull Request Author
src/model/test)src/energyplus/Test)src/osversion/VersionTranslator.cpp)Labels:
IDDChangeAPIChangePull Request - Ready for CIso that CI builds your PRReview Checklist
This will not be exhaustively relevant to every PR.