diff --git a/resources/model/OpenStudio.idd b/resources/model/OpenStudio.idd index 332f335af70..363b72b9d2d 100644 --- a/resources/model/OpenStudio.idd +++ b/resources/model/OpenStudio.idd @@ -18981,6 +18981,104 @@ OS:Coil:Cooling:DX:TwoSpeed, \type object-list \object-list ScheduleNames +OS:CoilSystem:Cooling:Water, + \memo Virtual container component that consists of a water cooling coil + \memo and its associated controls. This control object supports the + \memo available water coil types and may be placed directly on an + \memo air loop branch or in an outdoor air equipment list. + \min-fields 12 + A1, \field Handle + \type handle + \required-field + A2, \field Name + \required-field + \reference ConnectionObject + A3, \field Air Inlet Node Name + \type object-list + \required-field + \object-list ConnectionNames + A4, \field Air Outlet Node Name + \type object-list + \required-field + \object-list ConnectionNames + A5, \field Availability Schedule Name + \note Availability schedule name for this system. Schedule value > 0 + \note means the system is available. + \note If this field is blank, the system is always available. + \type object-list + \object-list ScheduleNames + \required-field + A6, \field Cooling Coil + \required-field + \type object-list + \object-list CoolingCoilsWater + A7, \field Dehumidification Control Type + \type choice + \key None + \key Multimode + \key CoolReheat + \required-field + \note None = meet sensible load only. Valid with all cooling coil types. When a heat + \note exchanger assisted cooling coil is used, the heat exchanger is locked on at all times. + \note Multimode = activate water coil and meet sensible load. If no sensible load exists, + \note and Run on Latent Load = Yes, and a latent load exists, the coil will operate to meet + \note the latent load. If the latent load cannot be met the heat exchanger will be activated. + \note Valid only with cooling coil type CoilSystem:Cooling:Water:HeatExchangerAssisted. + \note CoolReheat = cool beyond the dry-bulb setpoint as required to meet the humidity setpoint. + \note Valid with all cooling coil types. When a heat exchanger assisted cooling coil is used, + \note the heat exchanger is locked on at all times. + \note For all dehumidification controls, the max humidity setpoint on the Sensor Node is used. + \note SetpointManager:SingleZone:Humidity:Maximum, + \note SetpointManager:MultiZone:Humidity:Maximum, or + \note SetpointManager:MultiZone:MaximumHumidity:Average, and + \note SetpointManager:OutdoorAirPretreat (optional) objects. + A8, \field Run on Sensible Load + \type choice + \key Yes + \key No + \required-field + \note If Yes, unit will run if there is a sensible load. + \note If No, unit will not run if there is only a sensible load. + \note Dehumidification controls will be active if specified. + A9, \field Run on Latent Load + \type choice + \key Yes + \key No + \required-field + \note If Yes, unit will run if there is a latent load. + \note even if there is no sensible load. + \note If No, unit will not run if there is only a latent load. + \note Dehumidification controls will be active if specified. + N1, \field Minimum Air To Water Temperature Offset + \note Coil will turn on as required when inlet air temperature is above + \note water temperature by amount of offset. To model a waterside + \note economizer connect to condenser loop and increase offset as desired. + \type real + \units deltaC + \minimum 0.0 + \required-field + A10, \field Economizer Lockout + \type choice + \key Yes + \key No + \required-field + \note Yes means that the heat exchanger will be locked out (off) + N2, \field Minimum Water Loop Temperature For Heat Recovery + \note Only used for heat recovery loops. + \note Loop will turn off below this temperature. + \type real + \units C + \required-field + A11; \field Companion Coil Used For Heat Recovery + \note Only used for heat recovery loops. + \note Entering a coil name indicates a heat recovery loop is specified. + \note Coil listed is connected in series with this objects coil on demand side + \note branch of a plant loop. A dedicated plant loop with no supply side + \note equipment, other than a pump, is currently required. + \note Only Coil:Cooling:Water coil type is currently allowed for heat recovery loops. + \type object-list + \object-list CoolingCoilsWater + OS:CoilSystem:Cooling:Water:HeatExchangerAssisted, \memo Virtual component consisting of a chilled-water cooling coil and an air-to-air heat \memo exchanger. The air-to-air heat exchanger precools the air entering the cooling coil diff --git a/src/energyplus/CMakeLists.txt b/src/energyplus/CMakeLists.txt index 388722f2c10..a23df24d945 100644 --- a/src/energyplus/CMakeLists.txt +++ b/src/energyplus/CMakeLists.txt @@ -98,6 +98,7 @@ set(${target_name}_src ForwardTranslator/ForwardTranslateCoilHeatingWaterToAirHeatPumpVariableSpeedEquationFit.cpp ForwardTranslator/ForwardTranslateCoilPerformanceDXCooling.cpp ForwardTranslator/ForwardTranslateCoilSystemCoolingDXHeatExchangerAssisted.cpp + ForwardTranslator/ForwardTranslateCoilSystemCoolingWater.cpp ForwardTranslator/ForwardTranslateCoilSystemCoolingWaterHeatExchangerAssisted.cpp ForwardTranslator/ForwardTranslateCoilSystemIntegratedHeatPumpAirSource.cpp ForwardTranslator/ForwardTranslateCoilUserDefined.cpp @@ -710,6 +711,7 @@ set(${target_name}_test_src Test/CoilCoolingDXTwoSpeed_GTest.cpp Test/CoilCoolingDXTwoStageWithHumidityControlMode_GTest.cpp Test/CoilCoolingDXVariableSpeed_GTest.cpp + Test/CoilCoolingWater_GTest.cpp Test/CoilCoolingWaterToAirHeatPumpEquationFit_GTest.cpp Test/CoilCoolingWaterToAirHeatPumpVariableSpeedEquationFit_GTest.cpp Test/CoilHeatingDXMultiSpeed_GTest.cpp @@ -720,6 +722,8 @@ set(${target_name}_test_src Test/CoilHeatingGasMultiStage_GTest.cpp Test/CoilHeatingWaterToAirHeatPumpEquationFit_GTest.cpp Test/CoilHeatingWaterToAirHeatPumpVariableSpeedEquationFit_GTest.cpp + Test/CoilSystemCoolingWater_GTest.cpp + Test/CoilSystemCoolingWaterHeatExchangerAssisted_GTest.cpp Test/CoilSystemIntegratedHeatPumpAirSource_GTest.cpp Test/CoilUserDefined_GTest.cpp Test/CoilWaterHeatingAirToWaterHeatPump_GTest.cpp diff --git a/src/energyplus/ForwardTranslator.cpp b/src/energyplus/ForwardTranslator.cpp index 4ab2a0b7b93..4f4db90c98f 100644 --- a/src/energyplus/ForwardTranslator.cpp +++ b/src/energyplus/ForwardTranslator.cpp @@ -1207,6 +1207,11 @@ namespace energyplus { retVal = translateCoilPerformanceDXCooling(mo); break; } + case openstudio::IddObjectType::OS_CoilSystem_Cooling_Water: { + auto mo = modelObject.cast(); + retVal = translateCoilSystemCoolingWater(mo); + break; + } case openstudio::IddObjectType::OS_CoilSystem_Cooling_Water_HeatExchangerAssisted: { auto mo = modelObject.cast(); retVal = translateCoilSystemCoolingWaterHeatExchangerAssisted(mo); diff --git a/src/energyplus/ForwardTranslator.hpp b/src/energyplus/ForwardTranslator.hpp index 3752765324e..7b83e9b8d7e 100644 --- a/src/energyplus/ForwardTranslator.hpp +++ b/src/energyplus/ForwardTranslator.hpp @@ -134,6 +134,7 @@ namespace model { class CoilHeatingWaterToAirHeatPumpVariableSpeedEquationFit; class CoilPerformanceDXCooling; class CoilSystemCoolingDXHeatExchangerAssisted; + class CoilSystemCoolingWater; class CoilSystemCoolingWaterHeatExchangerAssisted; class CoilSystemIntegratedHeatPumpAirSource; class CoilUserDefined; @@ -844,6 +845,8 @@ namespace energyplus { boost::optional translateCoilSystemCoolingDXHeatExchangerAssisted(model::CoilSystemCoolingDXHeatExchangerAssisted& modelObject); + boost::optional translateCoilSystemCoolingWater(model::CoilSystemCoolingWater& modelObject); + boost::optional translateCoilSystemCoolingWaterHeatExchangerAssisted(model::CoilSystemCoolingWaterHeatExchangerAssisted& modelObject); boost::optional translateCoilSystemIntegratedHeatPumpAirSource(model::CoilSystemIntegratedHeatPumpAirSource& modelObject); diff --git a/src/energyplus/ForwardTranslator/ForwardTranslateAirLoopHVAC.cpp b/src/energyplus/ForwardTranslator/ForwardTranslateAirLoopHVAC.cpp index d30a7f4925e..e40d2ebf483 100644 --- a/src/energyplus/ForwardTranslator/ForwardTranslateAirLoopHVAC.cpp +++ b/src/energyplus/ForwardTranslator/ForwardTranslateAirLoopHVAC.cpp @@ -18,6 +18,8 @@ #include "../../model/SizingSystem_Impl.hpp" #include "../../model/CoilCoolingWater.hpp" #include "../../model/CoilCoolingWater_Impl.hpp" +#include "../../model/CoilSystemCoolingWater.hpp" +#include "../../model/CoilSystemCoolingWater_Impl.hpp" #include "../../model/CoilSystemCoolingWaterHeatExchangerAssisted.hpp" #include "../../model/CoilSystemCoolingWaterHeatExchangerAssisted_Impl.hpp" #include "../../model/CoilHeatingWater.hpp" @@ -282,6 +284,10 @@ namespace energyplus { } break; } + // case openstudio::IddObjectType::OS_CoilSystem_Cooling_Water: { + // // No-op: this has its own controller + // break; + // } default: { break; } diff --git a/src/energyplus/ForwardTranslator/ForwardTranslateCoilSystemCoolingWater.cpp b/src/energyplus/ForwardTranslator/ForwardTranslateCoilSystemCoolingWater.cpp new file mode 100644 index 00000000000..48d64844462 --- /dev/null +++ b/src/energyplus/ForwardTranslator/ForwardTranslateCoilSystemCoolingWater.cpp @@ -0,0 +1,152 @@ +/*********************************************************************************************************************** +* OpenStudio(R), Copyright (c) Alliance for Sustainable Energy, LLC. +* See also https://openstudio.net/license +***********************************************************************************************************************/ + +#include "../ForwardTranslator.hpp" +#include "../../model/Model.hpp" +#include "../../model/CoilSystemCoolingWater.hpp" +#include "../../model/CoilSystemCoolingWater_Impl.hpp" +#include "../../model/Node.hpp" +#include "../../model/Node_Impl.hpp" +#include "../../model/Schedule.hpp" +#include "../../model/Schedule_Impl.hpp" +#include "../../model/CoilCoolingWater.hpp" +#include "../../model/CoilCoolingWater_Impl.hpp" +#include "../../model/ControllerWaterCoil.hpp" +#include "../../model/ControllerWaterCoil_Impl.hpp" + +#include "../../model/AirLoopHVAC.hpp" +#include "../../model/FanVariableVolume.hpp" +#include "../../model/FanVariableVolume_Impl.hpp" +#include "../../model/FanConstantVolume.hpp" +#include "../../model/FanConstantVolume_Impl.hpp" +#include "../../model/FanOnOff.hpp" +#include "../../model/FanOnOff_Impl.hpp" +#include "../../model/FanSystemModel.hpp" +#include "../../model/FanSystemModel_Impl.hpp" +#include "../../model/AirLoopHVACOutdoorAirSystem.hpp" +#include "../../model/AirLoopHVACOutdoorAirSystem_Impl.hpp" + +#include +#include +#include +#include +#include + +using namespace openstudio::model; + +namespace openstudio { + +namespace energyplus { + + boost::optional ForwardTranslator::translateCoilSystemCoolingWater(model::CoilSystemCoolingWater& modelObject) { + + IdfObject idfObject = createRegisterAndNameIdfObject(openstudio::IddObjectType::CoilSystem_Cooling_Water, modelObject); + + std::string airInletNodeName; + // InletNodeName + if (auto mo = modelObject.inletModelObject()) { + if (auto node = mo->optionalCast()) { + airInletNodeName = node->nameString(); + } + } + + std::string airOutletNodeName; + // OutletNodeName + if (auto mo = modelObject.outletModelObject()) { + if (auto node = mo->optionalCast()) { + airOutletNodeName = node->nameString(); + } + } + + idfObject.setString(CoilSystem_Cooling_WaterFields::AirInletNodeName, airInletNodeName); + idfObject.setString(CoilSystem_Cooling_WaterFields::AirOutletNodeName, airOutletNodeName); + + // CoolingCoilObjectType + // CoolingCoilName + { + auto coolingCoil = modelObject.coolingCoil(); + if (auto idf = translateAndMapModelObject(coolingCoil)) { + idfObject.setString(CoilSystem_Cooling_WaterFields::CoolingCoilObjectType, idf->iddObject().name()); + idfObject.setString(CoilSystem_Cooling_WaterFields::CoolingCoilName, idf->nameString()); + if (idf->iddObject().type() == IddObjectType::Coil_Cooling_Water) { + 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() + << " appears to have a cooling coil that shouldn't have been accepted: " << coolingCoil.briefDescription()); + } + } + } + + // Availability Schedule Name: Optional Object + if (boost::optional availabilitySchedule_ = modelObject.availabilitySchedule()) { + if (boost::optional wo_ = translateAndMapModelObject(availabilitySchedule_.get())) { + idfObject.setString(CoilSystem_Cooling_WaterFields::AvailabilityScheduleName, wo_->nameString()); + } + } + + // Dehumidification Control Type: Optional String + const std::string dehumidificationControlType = modelObject.dehumidificationControlType(); + idfObject.setString(CoilSystem_Cooling_WaterFields::DehumidificationControlType, dehumidificationControlType); + + // Run on Sensible Load: Optional Boolean + if (modelObject.runonSensibleLoad()) { + idfObject.setString(CoilSystem_Cooling_WaterFields::RunonSensibleLoad, "Yes"); + } else { + idfObject.setString(CoilSystem_Cooling_WaterFields::RunonSensibleLoad, "No"); + } + + // Run on Latent Load: Optional Boolean + if (modelObject.runonLatentLoad()) { + idfObject.setString(CoilSystem_Cooling_WaterFields::RunonLatentLoad, "Yes"); + } else { + idfObject.setString(CoilSystem_Cooling_WaterFields::RunonLatentLoad, "No"); + } + + // Minimum Air To Water Temperature Offset: Optional Double + const double minimumAirToWaterTemperatureOffset = modelObject.minimumAirToWaterTemperatureOffset(); + idfObject.setDouble(CoilSystem_Cooling_WaterFields::MinimumAirToWaterTemperatureOffset, minimumAirToWaterTemperatureOffset); + + // Economizer Lockout: Optional Boolean + if (modelObject.economizerLockout()) { + idfObject.setString(CoilSystem_Cooling_WaterFields::EconomizerLockout, "Yes"); + } else { + idfObject.setString(CoilSystem_Cooling_WaterFields::EconomizerLockout, "No"); + } + + // Minimum Water Loop Temperature For Heat Recovery: Optional Double + const double minimumWaterLoopTemperatureForHeatRecovery = modelObject.minimumWaterLoopTemperatureForHeatRecovery(); + idfObject.setDouble(CoilSystem_Cooling_WaterFields::MinimumWaterLoopTemperatureForHeatRecovery, minimumWaterLoopTemperatureForHeatRecovery); + + // Companion Coil Used For Heat Recovery: Optional Object + if (boost::optional companionCoilUsedForHeatRecovery_ = modelObject.companionCoilUsedForHeatRecovery()) { + if (auto comp_ = companionCoilUsedForHeatRecovery_->optionalCast()) { + if (comp_->airInletModelObject()) { + if (boost::optional idf_ = translateAndMapModelObject(companionCoilUsedForHeatRecovery_.get())) { + idfObject.setString(CoilSystem_Cooling_WaterFields::CompanionCoilUsedForHeatRecovery, idf_->nameString()); + } + } else { + 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()); + } + } + + return idfObject; + } // End of translate function + +} // end namespace energyplus +} // end namespace openstudio 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(); + } + } } } diff --git a/src/energyplus/ForwardTranslator/ForwardTranslatePlantEquipmentOperationSchemes.cpp b/src/energyplus/ForwardTranslator/ForwardTranslatePlantEquipmentOperationSchemes.cpp index 2ae937c92c1..64e7c034962 100644 --- a/src/energyplus/ForwardTranslator/ForwardTranslatePlantEquipmentOperationSchemes.cpp +++ b/src/energyplus/ForwardTranslator/ForwardTranslatePlantEquipmentOperationSchemes.cpp @@ -91,6 +91,10 @@ #include "../../model/HeatPumpPlantLoopEIRHeating_Impl.hpp" #include "../../model/HeatPumpPlantLoopEIRCooling.hpp" #include "../../model/HeatPumpPlantLoopEIRCooling_Impl.hpp" +#include "../../model/CoilCoolingWater.hpp" +#include "../../model/CoilCoolingWater_Impl.hpp" +#include "../../model/CoilSystemCoolingWater.hpp" +#include "../../model/CoilSystemCoolingWater_Impl.hpp" #include "../../utilities/idf/IdfExtensibleGroup.hpp" #include "../../utilities/idf/Workspace.hpp" #include "../../utilities/idf/WorkspaceObjectOrder.hpp" @@ -470,6 +474,18 @@ namespace energyplus { } } + // Special case for CoilSystemCoolingWater (Wrap Around Water Coil Heat Recovery Mode) + for (const auto& comp : subsetCastVector(plantLoop.demandComponents())) { + if (comp.iddObject().type().value() == openstudio::IddObjectType::OS_Coil_Cooling_Water) { + auto coil = comp.cast(); + if (auto containingHVACComponent = coil.containingHVACComponent()) { + if (auto coilSystemCoolingWater = containingHVACComponent->optionalCast()) { + result.push_back(operationSchemeComponent(*coilSystemCoolingWater)); + } + } + } + } + return result; } @@ -679,7 +695,9 @@ namespace energyplus { eg.setString(PlantEquipmentOperationSchemesExtensibleFields::ControlSchemeScheduleName, _alwaysOn->nameString()); for (auto& uncontrolledComponent : t_uncontrolledComponents) { - if (auto idfObject_ = translateAndMapModelObject(uncontrolledComponent)) { + if (uncontrolledComponent.optionalCast()) { + // no-op + } else if (auto idfObject_ = translateAndMapModelObject(uncontrolledComponent)) { IdfExtensibleGroup eg = plantEquipmentList.pushExtensibleGroup(); eg.setString(PlantEquipmentListExtensibleFields::EquipmentObjectType, idfObject_->iddObject().name()); eg.setString(PlantEquipmentListExtensibleFields::EquipmentName, idfObject_->nameString()); diff --git a/src/energyplus/Test/CoilCoolingWater_GTest.cpp b/src/energyplus/Test/CoilCoolingWater_GTest.cpp new file mode 100644 index 00000000000..e4c570555ce --- /dev/null +++ b/src/energyplus/Test/CoilCoolingWater_GTest.cpp @@ -0,0 +1,162 @@ +/*********************************************************************************************************************** +* OpenStudio(R), Copyright (c) Alliance for Sustainable Energy, LLC. +* See also https://openstudio.net/license +***********************************************************************************************************************/ + +#include +#include "EnergyPlusFixture.hpp" + +#include "../ForwardTranslator.hpp" + +#include "../../model/Model.hpp" +#include "../../model/CoilCoolingWater.hpp" +#include "../../model/CoilCoolingWater_Impl.hpp" + +#include "../../model/AirLoopHVAC.hpp" +#include "../../model/AirLoopHVACUnitarySystem.hpp" +#include "../../model/Node.hpp" + +#include "../../utilities/idf/IdfObject.hpp" +#include "../../utilities/idf/IdfObject_Impl.hpp" +#include "../../utilities/idf/WorkspaceObject.hpp" +#include "../../utilities/idf/WorkspaceObject_Impl.hpp" +#include "../../utilities/idf/IdfExtensibleGroup.hpp" +#include "../../utilities/idf/WorkspaceExtensibleGroup.hpp" + +#include +#include +#include +#include +#include +#include +#include + +using namespace openstudio::energyplus; +using namespace openstudio::model; +using namespace openstudio; + +TEST_F(EnergyPlusFixture, ForwardTranslator_CoilCoolingWater_Unitary) { + Model m; + CoilCoolingWater coil(m); + coil.setName("My CoilCoolingWater"); + + // put it inside a Unitary, and put Unitary on an AirLoopHVAC so it gets translated + AirLoopHVACUnitarySystem unitary(m); + unitary.setCoolingCoil(coil); + 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")); + + // They must be connected to a PlantLoop too + PlantLoop chw_p(m); + chw_p.addDemandBranchForComponent(coil); + coil.waterInletModelObject()->setName("Coil Water Inlet Node"); + coil.waterOutletModelObject()->setName("Coil Water Outlet Node"); + + ForwardTranslator ft; + Workspace w = ft.translateModel(m); + + WorkspaceObjectVector idfUnitarys(w.getObjectsByType(IddObjectType::AirLoopHVAC_UnitarySystem)); + ASSERT_EQ(1u, idfUnitarys.size()); + WorkspaceObject idfUnitary(idfUnitarys[0]); + + WorkspaceObjectVector idfCoils(w.getObjectsByType(IddObjectType::Coil_Cooling_Water)); + ASSERT_EQ(1u, idfCoils.size()); + WorkspaceObject idfCoil(idfCoils[0]); + + // Check that the Unitary ends up with the CoilCoolingWater + EXPECT_EQ("Coil:Cooling:Water", idfUnitary.getString(AirLoopHVAC_UnitarySystemFields::CoolingCoilObjectType).get()); + EXPECT_EQ(idfCoil.nameString(), idfUnitary.getString(AirLoopHVAC_UnitarySystemFields::CoolingCoilName).get()); + + // Since the Unitary only has a Cooling Coil (no fan, not HC / Suppl HC), the nodes should match + EXPECT_EQ(idfUnitary.getString(AirLoopHVAC_UnitarySystemFields::AirInletNodeName).get(), + idfCoil.getString(Coil_Cooling_WaterFields::AirInletNodeName).get()); + + EXPECT_EQ(idfUnitary.getString(AirLoopHVAC_UnitarySystemFields::AirOutletNodeName).get(), + idfCoil.getString(Coil_Cooling_WaterFields::AirOutletNodeName).get()); + + EXPECT_EQ("My CoilCoolingWater", 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("Coil Water Inlet Node", idfCoil.getString(Coil_Cooling_WaterFields::WaterInletNodeName).get()); + EXPECT_EQ("Coil Water Outlet Node", idfCoil.getString(Coil_Cooling_WaterFields::WaterOutletNodeName).get()); + EXPECT_EQ("Coil Air Inlet Node", idfCoil.getString(Coil_Cooling_WaterFields::AirInletNodeName).get()); + EXPECT_EQ("Coil Air Outlet Node", 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)); + + // Check Controller:WaterCoil created by coil.addToNode + EXPECT_EQ(0, w.getObjectsByType(IddObjectType::Controller_WaterCoil).size()); +} + +TEST_F(EnergyPlusFixture, ForwardTranslator_CoilCoolingWater_AirLoopHVAC) { + Model m; + CoilCoolingWater coil(m); + + // put it on an AirLoopHVAC + AirLoopHVAC airLoop(m); + Node supplyOutletNode = airLoop.supplyOutletNode(); + EXPECT_TRUE(coil.addToNode(supplyOutletNode)); + + // They must be connected to a PlantLoop too + PlantLoop chw_p(m); + chw_p.addDemandBranchForComponent(coil); + + ForwardTranslator ft; + Workspace w = ft.translateModel(m); + + 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(), coil.airInletModelObject().get().nameString()); + EXPECT_EQ(w_eg2.getString(BranchExtensibleFields::ComponentOutletNodeName).get(), coil.airOutletModelObject().get().nameString()); + EXPECT_EQ("Coil:Cooling:Water", w_eg2.getString(BranchExtensibleFields::ComponentObjectType).get()); + + EXPECT_EQ(coil.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(coil.waterInletModelObject().get().nameString(), idfCoil.getString(Coil_Cooling_WaterFields::WaterInletNodeName).get()); + EXPECT_EQ(coil.waterOutletModelObject().get().nameString(), idfCoil.getString(Coil_Cooling_WaterFields::WaterOutletNodeName).get()); + EXPECT_EQ(coil.airInletModelObject().get().nameString(), idfCoil.getString(Coil_Cooling_WaterFields::AirInletNodeName).get()); + EXPECT_EQ(coil.airOutletModelObject().get().nameString(), 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)); + + // Check Controller:WaterCoil created by coil.addToNode + EXPECT_EQ(1, w.getObjectsByType(IddObjectType::Controller_WaterCoil).size()); +} diff --git a/src/energyplus/Test/CoilSystemCoolingWaterHeatExchangerAssisted_GTest.cpp b/src/energyplus/Test/CoilSystemCoolingWaterHeatExchangerAssisted_GTest.cpp new file mode 100644 index 00000000000..99c93a0d770 --- /dev/null +++ b/src/energyplus/Test/CoilSystemCoolingWaterHeatExchangerAssisted_GTest.cpp @@ -0,0 +1,292 @@ +/*********************************************************************************************************************** +* OpenStudio(R), Copyright (c) Alliance for Sustainable Energy, LLC. +* See also https://openstudio.net/license +***********************************************************************************************************************/ + +#include +#include "EnergyPlusFixture.hpp" + +#include "../ForwardTranslator.hpp" + +#include "../../model/Model.hpp" +#include "../../model/CoilSystemCoolingWaterHeatExchangerAssisted.hpp" +#include "../../model/CoilSystemCoolingWaterHeatExchangerAssisted_Impl.hpp" +#include "../../model/CoilCoolingWater.hpp" +#include "../../model/CoilCoolingWater_Impl.hpp" +#include "../../model/HeatExchangerAirToAirSensibleAndLatent.hpp" +#include "../../model/HeatExchangerAirToAirSensibleAndLatent_Impl.hpp" + +#include "../../model/AirLoopHVAC.hpp" +#include "../../model/AirLoopHVACUnitarySystem.hpp" +#include "../../model/Node.hpp" + +#include "../../utilities/idf/IdfObject.hpp" +#include "../../utilities/idf/IdfObject_Impl.hpp" +#include "../../utilities/idf/WorkspaceObject.hpp" +#include "../../utilities/idf/WorkspaceObject_Impl.hpp" +#include "../../utilities/idf/IdfExtensibleGroup.hpp" +#include "../../utilities/idf/WorkspaceExtensibleGroup.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace openstudio::energyplus; +using namespace openstudio::model; +using namespace openstudio; + +TEST_F(EnergyPlusFixture, ForwardTranslator_CoilSystemCoolingWaterHeatExchangerAssisted_Unitary) { + Model m; + CoilSystemCoolingWaterHeatExchangerAssisted coil(m); + coil.setName("My CoilSystemCoolingWaterHeatExchangerAssisted"); + + // put it inside a Unitary, and put Unitary on an AirLoopHVAC so it gets translated + AirLoopHVACUnitarySystem unitary(m); + unitary.setCoolingCoil(coil); + 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")); + + // They must be connected to a PlantLoop too + PlantLoop chw_p(m); + chw_p.addDemandBranchForComponent(coil.coolingCoil()); + + ForwardTranslator ft; + Workspace w = ft.translateModel(m); + + WorkspaceObjectVector idfUnitarys(w.getObjectsByType(IddObjectType::AirLoopHVAC_UnitarySystem)); + ASSERT_EQ(1u, idfUnitarys.size()); + WorkspaceObject idfUnitary(idfUnitarys[0]); + + WorkspaceObjectVector idfCoilSystems(w.getObjectsByType(IddObjectType::CoilSystem_Cooling_Water_HeatExchangerAssisted)); + ASSERT_EQ(1u, idfCoilSystems.size()); + WorkspaceObject idfCoilSystem(idfCoilSystems[0]); + + WorkspaceObjectVector idfHXs(w.getObjectsByType(IddObjectType::HeatExchanger_AirToAir_SensibleAndLatent)); + ASSERT_EQ(1u, idfHXs.size()); + WorkspaceObject idfHX(idfHXs[0]); + + WorkspaceObjectVector idfCoils(w.getObjectsByType(IddObjectType::Coil_Cooling_Water)); + ASSERT_EQ(1u, idfCoils.size()); + WorkspaceObject idfCoil(idfCoils[0]); + + // Check that the Unitary ends up with the CoilSystemCoolingWaterHeatExchangerAssisted + EXPECT_EQ("CoilSystem:Cooling:Water:HeatExchangerAssisted", idfUnitary.getString(AirLoopHVAC_UnitarySystemFields::CoolingCoilObjectType).get()); + EXPECT_EQ(idfCoilSystem.nameString(), idfUnitary.getString(AirLoopHVAC_UnitarySystemFields::CoolingCoilName).get()); + + // Since the Unitary only has a Cooling Coil (no fan, not HC / Suppl HC), the nodes should match + EXPECT_EQ(idfUnitary.getString(AirLoopHVAC_UnitarySystemFields::AirInletNodeName).get(), + idfHX.getString(HeatExchanger_AirToAir_SensibleAndLatentFields::SupplyAirInletNodeName).get()); + + EXPECT_EQ(idfUnitary.getString(AirLoopHVAC_UnitarySystemFields::AirOutletNodeName).get(), + idfHX.getString(HeatExchanger_AirToAir_SensibleAndLatentFields::ExhaustAirOutletNodeName).get()); + + EXPECT_EQ("My CoilSystemCoolingWaterHeatExchangerAssisted", + idfCoilSystem.getString(CoilSystem_Cooling_Water_HeatExchangerAssistedFields::Name).get()); + EXPECT_EQ("HeatExchanger:AirToAir:SensibleAndLatent", + idfCoilSystem.getString(CoilSystem_Cooling_Water_HeatExchangerAssistedFields::HeatExchangerObjectType).get()); + EXPECT_EQ(coil.heatExchanger().nameString(), + idfCoilSystem.getString(CoilSystem_Cooling_Water_HeatExchangerAssistedFields::HeatExchangerName).get()); + EXPECT_EQ("Coil:Cooling:Water", idfCoilSystem.getString(CoilSystem_Cooling_Water_HeatExchangerAssistedFields::CoolingCoilObjectType).get()); + EXPECT_EQ(coil.coolingCoil().nameString(), idfCoilSystem.getString(CoilSystem_Cooling_Water_HeatExchangerAssistedFields::CoolingCoilName).get()); + + EXPECT_EQ(coil.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(coil.coolingCoil().waterInletModelObject().get().nameString(), idfCoil.getString(Coil_Cooling_WaterFields::WaterInletNodeName).get()); + EXPECT_EQ(coil.coolingCoil().waterOutletModelObject().get().nameString(), idfCoil.getString(Coil_Cooling_WaterFields::WaterOutletNodeName).get()); + EXPECT_EQ(idfHX.getString(HeatExchanger_AirToAir_SensibleAndLatentFields::SupplyAirOutletNodeName).get(), + idfCoil.getString(Coil_Cooling_WaterFields::AirInletNodeName).get()); + EXPECT_EQ(idfHX.getString(HeatExchanger_AirToAir_SensibleAndLatentFields::ExhaustAirInletNodeName).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)); + + // Check Controller:WaterCoil created by coil.addToNode + EXPECT_EQ(1, w.getObjectsByType(IddObjectType::Controller_WaterCoil).size()); +} + +TEST_F(EnergyPlusFixture, ForwardTranslator_CoilSystemCoolingWaterHeatExchangerAssisted_AirLoopHVAC) { + // connect coil system to air loop, and then connect coil to plant + { + Model m; + CoilSystemCoolingWaterHeatExchangerAssisted coil(m); + EXPECT_FALSE(coil.coolingCoil().waterInletModelObject()); + + // put it on an AirLoopHVAC + AirLoopHVAC airLoop(m); + Node supplyOutletNode = airLoop.supplyOutletNode(); + EXPECT_TRUE(coil.addToNode(supplyOutletNode)); + EXPECT_FALSE(coil.coolingCoil().waterInletModelObject()); + + // They must be connected to a PlantLoop too + PlantLoop chw_p(m); + chw_p.addDemandBranchForComponent(coil.coolingCoil()); + EXPECT_TRUE(coil.coolingCoil().waterInletModelObject()); + + ForwardTranslator ft; + Workspace w = ft.translateModel(m); + + WorkspaceObjectVector idfCoilSystems(w.getObjectsByType(IddObjectType::CoilSystem_Cooling_Water_HeatExchangerAssisted)); + ASSERT_EQ(1u, idfCoilSystems.size()); + WorkspaceObject idfCoilSystem(idfCoilSystems[0]); + + WorkspaceObjectVector idfCoils(w.getObjectsByType(IddObjectType::Coil_Cooling_Water)); + ASSERT_EQ(1u, idfCoils.size()); + WorkspaceObject idfCoil(idfCoils[0]); + + WorkspaceObjectVector idfHXs(w.getObjectsByType(IddObjectType::HeatExchanger_AirToAir_SensibleAndLatent)); + ASSERT_EQ(1u, idfHXs.size()); + WorkspaceObject idfHX(idfHXs[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(), coil.inletModelObject().get().nameString()); + EXPECT_EQ(w_eg2.getString(BranchExtensibleFields::ComponentOutletNodeName).get(), coil.outletModelObject().get().nameString()); + EXPECT_EQ("CoilSystem:Cooling:Water:HeatExchangerAssisted", w_eg2.getString(BranchExtensibleFields::ComponentObjectType).get()); + + EXPECT_EQ(coil.nameString(), idfCoilSystem.getString(CoilSystem_Cooling_Water_HeatExchangerAssistedFields::Name).get()); + EXPECT_EQ("HeatExchanger:AirToAir:SensibleAndLatent", + idfCoilSystem.getString(CoilSystem_Cooling_Water_HeatExchangerAssistedFields::HeatExchangerObjectType).get()); + EXPECT_EQ(coil.heatExchanger().nameString(), + idfCoilSystem.getString(CoilSystem_Cooling_Water_HeatExchangerAssistedFields::HeatExchangerName).get()); + EXPECT_EQ("Coil:Cooling:Water", idfCoilSystem.getString(CoilSystem_Cooling_Water_HeatExchangerAssistedFields::CoolingCoilObjectType).get()); + EXPECT_EQ(coil.coolingCoil().nameString(), idfCoilSystem.getString(CoilSystem_Cooling_Water_HeatExchangerAssistedFields::CoolingCoilName).get()); + + EXPECT_EQ(coil.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(coil.coolingCoil().waterInletModelObject().get().nameString(), idfCoil.getString(Coil_Cooling_WaterFields::WaterInletNodeName).get()); + EXPECT_EQ(coil.coolingCoil().waterOutletModelObject().get().nameString(), idfCoil.getString(Coil_Cooling_WaterFields::WaterOutletNodeName).get()); + EXPECT_EQ(idfHX.getString(HeatExchanger_AirToAir_SensibleAndLatentFields::SupplyAirOutletNodeName).get(), + idfCoil.getString(Coil_Cooling_WaterFields::AirInletNodeName).get()); + EXPECT_EQ(idfHX.getString(HeatExchanger_AirToAir_SensibleAndLatentFields::ExhaustAirInletNodeName).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)); + + // Check Controller:WaterCoil created by coil.addToNode + EXPECT_EQ(1, w.getObjectsByType(IddObjectType::Controller_WaterCoil).size()); + } + + // Test for #4895: connect coil to plant, and then connect coil system to air loop + { + Model m; + CoilSystemCoolingWaterHeatExchangerAssisted coil(m); + EXPECT_FALSE(coil.coolingCoil().waterInletModelObject()); + + // They must be connected to a PlantLoop too + PlantLoop chw_p(m); + chw_p.addDemandBranchForComponent(coil.coolingCoil()); + EXPECT_TRUE(coil.coolingCoil().waterInletModelObject()); + + // put it on an AirLoopHVAC + AirLoopHVAC airLoop(m); + Node supplyOutletNode = airLoop.supplyOutletNode(); + EXPECT_TRUE(coil.addToNode(supplyOutletNode)); + EXPECT_TRUE(coil.coolingCoil().waterInletModelObject()); + + ForwardTranslator ft; + Workspace w = ft.translateModel(m); + + WorkspaceObjectVector idfCoilSystems(w.getObjectsByType(IddObjectType::CoilSystem_Cooling_Water_HeatExchangerAssisted)); + ASSERT_EQ(1u, idfCoilSystems.size()); + WorkspaceObject idfCoilSystem(idfCoilSystems[0]); + + WorkspaceObjectVector idfCoils(w.getObjectsByType(IddObjectType::Coil_Cooling_Water)); + ASSERT_EQ(1u, idfCoils.size()); + WorkspaceObject idfCoil(idfCoils[0]); + + WorkspaceObjectVector idfHXs(w.getObjectsByType(IddObjectType::HeatExchanger_AirToAir_SensibleAndLatent)); + ASSERT_EQ(1u, idfHXs.size()); + WorkspaceObject idfHX(idfHXs[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(), coil.inletModelObject().get().nameString()); + EXPECT_EQ(w_eg2.getString(BranchExtensibleFields::ComponentOutletNodeName).get(), coil.outletModelObject().get().nameString()); + EXPECT_EQ("CoilSystem:Cooling:Water:HeatExchangerAssisted", w_eg2.getString(BranchExtensibleFields::ComponentObjectType).get()); + + EXPECT_EQ(coil.nameString(), idfCoilSystem.getString(CoilSystem_Cooling_Water_HeatExchangerAssistedFields::Name).get()); + EXPECT_EQ("HeatExchanger:AirToAir:SensibleAndLatent", + idfCoilSystem.getString(CoilSystem_Cooling_Water_HeatExchangerAssistedFields::HeatExchangerObjectType).get()); + EXPECT_EQ(coil.heatExchanger().nameString(), + idfCoilSystem.getString(CoilSystem_Cooling_Water_HeatExchangerAssistedFields::HeatExchangerName).get()); + EXPECT_EQ("Coil:Cooling:Water", idfCoilSystem.getString(CoilSystem_Cooling_Water_HeatExchangerAssistedFields::CoolingCoilObjectType).get()); + EXPECT_EQ(coil.coolingCoil().nameString(), idfCoilSystem.getString(CoilSystem_Cooling_Water_HeatExchangerAssistedFields::CoolingCoilName).get()); + + EXPECT_EQ(coil.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(coil.coolingCoil().waterInletModelObject().get().nameString(), idfCoil.getString(Coil_Cooling_WaterFields::WaterInletNodeName).get()); + EXPECT_EQ(coil.coolingCoil().waterOutletModelObject().get().nameString(), idfCoil.getString(Coil_Cooling_WaterFields::WaterOutletNodeName).get()); + EXPECT_EQ(idfHX.getString(HeatExchanger_AirToAir_SensibleAndLatentFields::SupplyAirOutletNodeName).get(), + idfCoil.getString(Coil_Cooling_WaterFields::AirInletNodeName).get()); + EXPECT_EQ(idfHX.getString(HeatExchanger_AirToAir_SensibleAndLatentFields::ExhaustAirInletNodeName).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)); + + // Check Controller:WaterCoil created by coil.addToNode + EXPECT_EQ(1, w.getObjectsByType(IddObjectType::Controller_WaterCoil).size()); + } +} diff --git a/src/energyplus/Test/CoilSystemCoolingWater_GTest.cpp b/src/energyplus/Test/CoilSystemCoolingWater_GTest.cpp new file mode 100644 index 00000000000..3746e2f41f7 --- /dev/null +++ b/src/energyplus/Test/CoilSystemCoolingWater_GTest.cpp @@ -0,0 +1,502 @@ +/*********************************************************************************************************************** +* OpenStudio(R), Copyright (c) Alliance for Sustainable Energy, LLC. +* See also https://openstudio.net/license +***********************************************************************************************************************/ + +#include +#include "EnergyPlusFixture.hpp" + +#include "../ForwardTranslator.hpp" + +#include "../../model/CoilSystemCoolingWater.hpp" +#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" +#include "../../model/Node_Impl.hpp" +#include "../../model/AirLoopHVACOutdoorAirSystem.hpp" +#include "../../model/AirLoopHVACOutdoorAirSystem_Impl.hpp" +#include "../../model/ControllerOutdoorAir.hpp" +#include "../../model/ControllerOutdoorAir_Impl.hpp" +#include "../../model/PipeAdiabatic.hpp" +#include "../../model/PipeAdiabatic_Impl.hpp" + +#include "../../utilities/idf/Workspace.hpp" +#include "../../utilities/idf/IdfObject.hpp" +#include "../../utilities/idf/WorkspaceObject.hpp" +#include "../../utilities/idf/IdfExtensibleGroup.hpp" +#include "../../utilities/idf/WorkspaceExtensibleGroup.hpp" + +// E+ FieldEnums +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace openstudio::energyplus; +using namespace openstudio::model; +using namespace openstudio; + +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(); + 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("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("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)); + 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()); +} + +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()); +} diff --git a/src/model/CMakeLists.txt b/src/model/CMakeLists.txt index e11982d4107..0cd8abff911 100644 --- a/src/model/CMakeLists.txt +++ b/src/model/CMakeLists.txt @@ -443,6 +443,9 @@ set(${target_name}_src CoilPerformanceDXCooling.hpp CoilPerformanceDXCooling_Impl.hpp CoilPerformanceDXCooling.cpp + CoilSystemCoolingWater.hpp + CoilSystemCoolingWater_Impl.hpp + CoilSystemCoolingWater.cpp CoilSystemCoolingWaterHeatExchangerAssisted.hpp CoilSystemCoolingWaterHeatExchangerAssisted_Impl.hpp CoilSystemCoolingWaterHeatExchangerAssisted.cpp @@ -2014,6 +2017,7 @@ set(${target_name}_test_src test/CoilHeatingWaterBaseboardRadiant_GTest.cpp test/CoilPerformanceDXCooling_GTest.cpp test/CoilSystemCoolingDXHeatExchangerAssisted_GTest.cpp + test/CoilSystemCoolingWater_GTest.cpp test/CoilSystemCoolingWaterHeatExchangerAssisted_GTest.cpp test/CoilSystemIntegratedHeatPumpAirSource_GTest.cpp test/CoilUserDefined_GTest.cpp diff --git a/src/model/CoilCoolingWater.cpp b/src/model/CoilCoolingWater.cpp index 905a674035d..4be5cc9b666 100644 --- a/src/model/CoilCoolingWater.cpp +++ b/src/model/CoilCoolingWater.cpp @@ -7,6 +7,8 @@ #include "CoilCoolingWater_Impl.hpp" #include "ControllerWaterCoil.hpp" #include "ControllerWaterCoil_Impl.hpp" +#include "CoilSystemCoolingWater.hpp" +#include "CoilSystemCoolingWater_Impl.hpp" #include "CoilSystemCoolingWaterHeatExchangerAssisted.hpp" #include "CoilSystemCoolingWaterHeatExchangerAssisted_Impl.hpp" #include "Node.hpp" @@ -271,38 +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 { - - success = WaterToAirComponent_Impl::addToNode(node); - auto t_containingZoneHVACComponent = containingZoneHVACComponent(); - - 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 { - Model t_model = model(); - ControllerWaterCoil controller(t_model); - controller.getImpl()->setWaterCoil(getObject()); - controller.setAction("Reverse"); + // 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; + } + + 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); + 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; + } + + // 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; } @@ -370,6 +402,21 @@ namespace model { } } + // CoilSystemCoolingWater + { + auto coilSystems = model().getConcreteModelObjects(); + for (const auto& coilSystem : coilSystems) { + if (coilSystem.coolingCoil().handle() == handle()) { + return coilSystem; + } + if (boost::optional companionCoilUsedForHeatRecovery = coilSystem.companionCoilUsedForHeatRecovery()) { + if (companionCoilUsedForHeatRecovery->handle() == this->handle()) { + return coilSystem; + } + } + } + } + // CoilSystemCoolingWaterHeatExchangerAssisted { auto coilSystems = model().getConcreteModelObjects(); diff --git a/src/model/CoilSystemCoolingWater.cpp b/src/model/CoilSystemCoolingWater.cpp new file mode 100644 index 00000000000..6b85a453e67 --- /dev/null +++ b/src/model/CoilSystemCoolingWater.cpp @@ -0,0 +1,435 @@ +/*********************************************************************************************************************** +* OpenStudio(R), Copyright (c) Alliance for Sustainable Energy, LLC. +* See also https://openstudio.net/license +***********************************************************************************************************************/ + +#include "CoilSystemCoolingWater.hpp" +#include "CoilSystemCoolingWater_Impl.hpp" + +#include "Schedule.hpp" +#include "Schedule_Impl.hpp" +#include "HVACComponent.hpp" +#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" +#include "Node_Impl.hpp" +#include "AirLoopHVAC.hpp" +#include "AirLoopHVAC_Impl.hpp" + +#include "AirLoopHVACUnitarySystem.hpp" +#include "AirLoopHVACUnitarySystem_Impl.hpp" +#include "ZoneHVACFourPipeFanCoil.hpp" +#include "ZoneHVACFourPipeFanCoil_Impl.hpp" +#include "ZoneHVACUnitVentilator.hpp" +#include "ZoneHVACUnitVentilator_Impl.hpp" + +#include "AirLoopHVACOutdoorAirSystem.hpp" +#include "AirLoopHVACOutdoorAirSystem_Impl.hpp" +#include "AirLoopHVACDedicatedOutdoorAirSystem.hpp" +#include "AirLoopHVACDedicatedOutdoorAirSystem_Impl.hpp" + +#include "../utilities/core/Assert.hpp" +#include "../utilities/data/DataEnums.hpp" + +#include +#include +#include + +namespace openstudio { +namespace model { + + namespace detail { + + CoilSystemCoolingWater_Impl::CoilSystemCoolingWater_Impl(const IdfObject& idfObject, Model_Impl* model, bool keepHandle) + : StraightComponent_Impl(idfObject, model, keepHandle) { + OS_ASSERT(idfObject.iddObject().type() == CoilSystemCoolingWater::iddObjectType()); + } + + CoilSystemCoolingWater_Impl::CoilSystemCoolingWater_Impl(const openstudio::detail::WorkspaceObject_Impl& other, Model_Impl* model, + bool keepHandle) + : StraightComponent_Impl(other, model, keepHandle) { + OS_ASSERT(other.iddObject().type() == CoilSystemCoolingWater::iddObjectType()); + } + + CoilSystemCoolingWater_Impl::CoilSystemCoolingWater_Impl(const CoilSystemCoolingWater_Impl& other, Model_Impl* model, bool keepHandle) + : StraightComponent_Impl(other, model, keepHandle) {} + + const std::vector& CoilSystemCoolingWater_Impl::outputVariableNames() const { + static std::vector result; + if (result.empty()) { + } + return result; + } + + IddObjectType CoilSystemCoolingWater_Impl::iddObjectType() const { + return CoilSystemCoolingWater::iddObjectType(); + } + + std::vector CoilSystemCoolingWater_Impl::getScheduleTypeKeys(const Schedule& schedule) const { + std::vector result; + const UnsignedVector fieldIndices = getSourceIndices(schedule.handle()); + if (std::find(fieldIndices.cbegin(), fieldIndices.cend(), OS_CoilSystem_Cooling_WaterFields::AvailabilityScheduleName) != fieldIndices.cend()) { + result.emplace_back("CoilSystemCoolingWater", "Availability"); + } + return result; + } + + unsigned CoilSystemCoolingWater_Impl::inletPort() const { + return OS_CoilSystem_Cooling_WaterFields::AirInletNodeName; + } + + unsigned CoilSystemCoolingWater_Impl::outletPort() const { + return OS_CoilSystem_Cooling_WaterFields::AirOutletNodeName; + } + + std::vector CoilSystemCoolingWater_Impl::children() const { + std::vector result; + if (OptionalHVACComponent intermediate = optionalCoolingCoil()) { + result.push_back(*intermediate); + } + if (OptionalHVACComponent intermediate = companionCoilUsedForHeatRecovery()) { + result.push_back(*intermediate); + } + + 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(); + + if (OptionalHVACComponent intermediate = optionalCoolingCoil()) { + coilSystemClone.setCoolingCoil(intermediate->clone(model).cast()); + } + if (OptionalHVACComponent intermediate = companionCoilUsedForHeatRecovery()) { + coilSystemClone.setCompanionCoilUsedForHeatRecovery(intermediate->clone(model).cast()); + } + + return std::move(coilSystemClone); + } + + bool CoilSystemCoolingWater_Impl::addToNode(Node& node) { + bool result = false; + + if (boost::optional airLoop = node.airLoopHVAC()) { + if (!airLoop->demandComponent(node.handle())) { + return StraightComponent_Impl::addToNode(node); + } + } else if (boost::optional oas = node.airLoopHVACOutdoorAirSystem()) { + if (oas->airLoopHVACDedicatedOutdoorAirSystem()) { + return StraightComponent_Impl::addToNode(node); + } + } + + return result; + } + + boost::optional CoilSystemCoolingWater_Impl::containingHVACComponent() const { + return boost::none; + } + + boost::optional CoilSystemCoolingWater_Impl::containingZoneHVACComponent() const { + return boost::none; + } + + ComponentType CoilSystemCoolingWater_Impl::componentType() const { + return ComponentType::Cooling; + } + + std::vector CoilSystemCoolingWater_Impl::coolingFuelTypes() const { + return coolingCoil().coolingFuelTypes(); + } + + std::vector CoilSystemCoolingWater_Impl::heatingFuelTypes() const { + return {}; + } + + std::vector CoilSystemCoolingWater_Impl::appGHeatingFuelTypes() const { + return {}; + } + + Schedule CoilSystemCoolingWater_Impl::availabilitySchedule() const { + boost::optional value = optionalAvailabilitySchedule(); + if (!value) { + LOG_AND_THROW(briefDescription() << " does not have an Availability Schedule attached."); + } + return value.get(); + } + + HVACComponent CoilSystemCoolingWater_Impl::coolingCoil() const { + boost::optional value = optionalCoolingCoil(); + if (!value) { + LOG_AND_THROW(briefDescription() << " does not have an Cooling Coil attached."); + } + return value.get(); + } + + std::string CoilSystemCoolingWater_Impl::dehumidificationControlType() const { + boost::optional value = getString(OS_CoilSystem_Cooling_WaterFields::DehumidificationControlType, true); + OS_ASSERT(value); + return value.get(); + } + + bool CoilSystemCoolingWater_Impl::runonSensibleLoad() const { + return getBooleanFieldValue(OS_CoilSystem_Cooling_WaterFields::RunonSensibleLoad); + } + + bool CoilSystemCoolingWater_Impl::runonLatentLoad() const { + return getBooleanFieldValue(OS_CoilSystem_Cooling_WaterFields::RunonLatentLoad); + } + + double CoilSystemCoolingWater_Impl::minimumAirToWaterTemperatureOffset() const { + boost::optional value = getDouble(OS_CoilSystem_Cooling_WaterFields::MinimumAirToWaterTemperatureOffset, true); + OS_ASSERT(value); + return value.get(); + } + + bool CoilSystemCoolingWater_Impl::economizerLockout() const { + return getBooleanFieldValue(OS_CoilSystem_Cooling_WaterFields::EconomizerLockout); + } + + double CoilSystemCoolingWater_Impl::minimumWaterLoopTemperatureForHeatRecovery() const { + boost::optional value = getDouble(OS_CoilSystem_Cooling_WaterFields::MinimumWaterLoopTemperatureForHeatRecovery, true); + OS_ASSERT(value); + return value.get(); + } + + boost::optional CoilSystemCoolingWater_Impl::companionCoilUsedForHeatRecovery() const { + return getObject().getModelObjectTarget(OS_CoilSystem_Cooling_WaterFields::CompanionCoilUsedForHeatRecovery); + } + + bool CoilSystemCoolingWater_Impl::setAvailabilitySchedule(Schedule& schedule) { + const bool result = + setSchedule(OS_CoilSystem_Cooling_WaterFields::AvailabilityScheduleName, "CoilSystemCoolingWater", "Availability", schedule); + return result; + } + + bool CoilSystemCoolingWater_Impl::setCoolingCoil(const HVACComponent& coolingCoil) { + const bool result = setPointer(OS_CoilSystem_Cooling_WaterFields::CoolingCoil, coolingCoil.handle()); + return result; + } + + bool CoilSystemCoolingWater_Impl::setDehumidificationControlType(const std::string& dehumidificationControlType) { + const bool result = setString(OS_CoilSystem_Cooling_WaterFields::DehumidificationControlType, dehumidificationControlType); + return result; + } + + bool CoilSystemCoolingWater_Impl::setRunonSensibleLoad(bool runonSensibleLoad) { + const bool result = setBooleanFieldValue(OS_CoilSystem_Cooling_WaterFields::RunonSensibleLoad, runonSensibleLoad); + OS_ASSERT(result); + return result; + } + + bool CoilSystemCoolingWater_Impl::setRunonLatentLoad(bool runonLatentLoad) { + const bool result = setBooleanFieldValue(OS_CoilSystem_Cooling_WaterFields::RunonLatentLoad, runonLatentLoad); + OS_ASSERT(result); + return result; + } + + bool CoilSystemCoolingWater_Impl::setMinimumAirToWaterTemperatureOffset(double minimumAirToWaterTemperatureOffset) { + const bool result = setDouble(OS_CoilSystem_Cooling_WaterFields::MinimumAirToWaterTemperatureOffset, minimumAirToWaterTemperatureOffset); + return result; + } + + bool CoilSystemCoolingWater_Impl::setEconomizerLockout(bool economizerLockout) { + const bool result = setBooleanFieldValue(OS_CoilSystem_Cooling_WaterFields::EconomizerLockout, economizerLockout); + OS_ASSERT(result); + return result; + } + + bool CoilSystemCoolingWater_Impl::setMinimumWaterLoopTemperatureForHeatRecovery(double minimumWaterLoopTemperatureForHeatRecovery) { + const bool result = + setDouble(OS_CoilSystem_Cooling_WaterFields::MinimumWaterLoopTemperatureForHeatRecovery, minimumWaterLoopTemperatureForHeatRecovery); + OS_ASSERT(result); + 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() { + const bool result = setString(OS_CoilSystem_Cooling_WaterFields::CompanionCoilUsedForHeatRecovery, ""); + OS_ASSERT(result); + } + + boost::optional CoilSystemCoolingWater_Impl::optionalAvailabilitySchedule() const { + return getObject().getModelObjectTarget(OS_CoilSystem_Cooling_WaterFields::AvailabilityScheduleName); + } + + boost::optional CoilSystemCoolingWater_Impl::optionalCoolingCoil() const { + return getObject().getModelObjectTarget(OS_CoilSystem_Cooling_WaterFields::CoolingCoil); + } + + } // namespace detail + + void CoilSystemCoolingWater::assignEnergyPlusIDDDefaults() { + bool ok = true; + 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); + } + + 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()); + + bool ok = true; + ok = setCoolingCoil(coolingCoil); + if (!ok) { + remove(); + LOG_AND_THROW("Unable to set " << briefDescription() << "'s Cooling Coil " << coolingCoil.briefDescription() << "."); + } + + assignEnergyPlusIDDDefaults(); + } + + IddObjectType CoilSystemCoolingWater::iddObjectType() { + return {IddObjectType::OS_CoilSystem_Cooling_Water}; + } + + std::vector CoilSystemCoolingWater::dehumidificationControlTypeValues() { + return getIddKeyNames(IddFactory::instance().getObject(iddObjectType()).get(), OS_CoilSystem_Cooling_WaterFields::DehumidificationControlType); + } + + Schedule CoilSystemCoolingWater::availabilitySchedule() const { + return getImpl()->availabilitySchedule(); + } + + HVACComponent CoilSystemCoolingWater::coolingCoil() const { + return getImpl()->coolingCoil(); + } + + std::string CoilSystemCoolingWater::dehumidificationControlType() const { + return getImpl()->dehumidificationControlType(); + } + + bool CoilSystemCoolingWater::runonSensibleLoad() const { + return getImpl()->runonSensibleLoad(); + } + + bool CoilSystemCoolingWater::runonLatentLoad() const { + return getImpl()->runonLatentLoad(); + } + + double CoilSystemCoolingWater::minimumAirToWaterTemperatureOffset() const { + return getImpl()->minimumAirToWaterTemperatureOffset(); + } + + bool CoilSystemCoolingWater::economizerLockout() const { + return getImpl()->economizerLockout(); + } + + double CoilSystemCoolingWater::minimumWaterLoopTemperatureForHeatRecovery() const { + return getImpl()->minimumWaterLoopTemperatureForHeatRecovery(); + } + + boost::optional CoilSystemCoolingWater::companionCoilUsedForHeatRecovery() const { + return getImpl()->companionCoilUsedForHeatRecovery(); + } + + bool CoilSystemCoolingWater::setAvailabilitySchedule(Schedule& schedule) { + return getImpl()->setAvailabilitySchedule(schedule); + } + + bool CoilSystemCoolingWater::setCoolingCoil(const HVACComponent& coolingCoil) { + return getImpl()->setCoolingCoil(coolingCoil); + } + + bool CoilSystemCoolingWater::setDehumidificationControlType(const std::string& dehumidificationControlType) { + return getImpl()->setDehumidificationControlType(dehumidificationControlType); + } + + bool CoilSystemCoolingWater::setRunonSensibleLoad(bool runonSensibleLoad) { + return getImpl()->setRunonSensibleLoad(runonSensibleLoad); + } + + bool CoilSystemCoolingWater::setRunonLatentLoad(bool runonLatentLoad) { + return getImpl()->setRunonLatentLoad(runonLatentLoad); + } + + bool CoilSystemCoolingWater::setMinimumAirToWaterTemperatureOffset(double minimumAirToWaterTemperatureOffset) { + return getImpl()->setMinimumAirToWaterTemperatureOffset(minimumAirToWaterTemperatureOffset); + } + + bool CoilSystemCoolingWater::setEconomizerLockout(bool economizerLockout) { + return getImpl()->setEconomizerLockout(economizerLockout); + } + + bool CoilSystemCoolingWater::setMinimumWaterLoopTemperatureForHeatRecovery(double minimumWaterLoopTemperatureForHeatRecovery) { + return getImpl()->setMinimumWaterLoopTemperatureForHeatRecovery(minimumWaterLoopTemperatureForHeatRecovery); + } + + bool CoilSystemCoolingWater::setCompanionCoilUsedForHeatRecovery(const HVACComponent& companionCoilUsedForHeatRecovery) { + return getImpl()->setCompanionCoilUsedForHeatRecovery(companionCoilUsedForHeatRecovery); + } + + void CoilSystemCoolingWater::resetCompanionCoilUsedForHeatRecovery() { + getImpl()->resetCompanionCoilUsedForHeatRecovery(); + } + + /// @cond + CoilSystemCoolingWater::CoilSystemCoolingWater(std::shared_ptr impl) : StraightComponent(std::move(impl)) {} + /// @endcond + +} // namespace model +} // namespace openstudio diff --git a/src/model/CoilSystemCoolingWater.hpp b/src/model/CoilSystemCoolingWater.hpp new file mode 100644 index 00000000000..dcc2cebd742 --- /dev/null +++ b/src/model/CoilSystemCoolingWater.hpp @@ -0,0 +1,127 @@ +/*********************************************************************************************************************** +* OpenStudio(R), Copyright (c) Alliance for Sustainable Energy, LLC. +* See also https://openstudio.net/license +***********************************************************************************************************************/ + +#ifndef MODEL_COILSYSTEMCOOLINGWATER_HPP +#define MODEL_COILSYSTEMCOOLINGWATER_HPP + +#include +#include "StraightComponent.hpp" + +namespace openstudio { + +namespace model { + + class Schedule; + class HVACComponent; + + namespace detail { + + class CoilSystemCoolingWater_Impl; + + } // namespace detail + + /** CoilSystemCoolingWater is a StraightComponent that wraps the OpenStudio IDD object 'OS:CoilSystem:Cooling:Water'. */ + class MODEL_API CoilSystemCoolingWater : public StraightComponent + { + public: + /** @name Constructors and Destructors */ + //@{ + + explicit CoilSystemCoolingWater(const Model& model); + + explicit CoilSystemCoolingWater(const Model& model, const HVACComponent& coolingCoil); + + virtual ~CoilSystemCoolingWater() = default; + // Default the copy and move operators because the virtual dtor is explicit + CoilSystemCoolingWater(const CoilSystemCoolingWater& other) = default; + CoilSystemCoolingWater(CoilSystemCoolingWater&& other) = default; + CoilSystemCoolingWater& operator=(const CoilSystemCoolingWater&) = default; + CoilSystemCoolingWater& operator=(CoilSystemCoolingWater&&) = default; + + //@} + + static IddObjectType iddObjectType(); + + static std::vector dehumidificationControlTypeValues(); + + /** @name Getters */ + //@{ + + Schedule availabilitySchedule() const; + + HVACComponent coolingCoil() const; + + std::string dehumidificationControlType() const; + + bool runonSensibleLoad() const; + + bool runonLatentLoad() const; + + double minimumAirToWaterTemperatureOffset() const; + + bool economizerLockout() const; + + double minimumWaterLoopTemperatureForHeatRecovery() const; + + boost::optional companionCoilUsedForHeatRecovery() const; + + //@} + /** @name Setters */ + //@{ + + bool setAvailabilitySchedule(Schedule& schedule); + + // CoilCoolingWater or CoilSystemCoolingWaterHeatExchangerAssisted + bool setCoolingCoil(const HVACComponent& coolingCoil); + + bool setDehumidificationControlType(const std::string& dehumidificationControlType); + + bool setRunonSensibleLoad(bool runonSensibleLoad); + + bool setRunonLatentLoad(bool runonLatentLoad); + + bool setMinimumAirToWaterTemperatureOffset(double minimumAirToWaterTemperatureOffset); + + bool setEconomizerLockout(bool economizerLockout); + + bool setMinimumWaterLoopTemperatureForHeatRecovery(double minimumWaterLoopTemperatureForHeatRecovery); + + // Only CoilCoolingWater is supported by E+ as a companion coil right now + bool setCompanionCoilUsedForHeatRecovery(const HVACComponent& companionCoilUsedForHeatRecovery); + + void resetCompanionCoilUsedForHeatRecovery(); + + //@} + /** @name Other */ + //@{ + + //@} + protected: + /// @cond + using ImplType = detail::CoilSystemCoolingWater_Impl; + + explicit CoilSystemCoolingWater(std::shared_ptr impl); + + friend class detail::CoilSystemCoolingWater_Impl; + friend class Model; + friend class IdfObject; + friend class openstudio::detail::IdfObject_Impl; + /// @endcond + private: + REGISTER_LOGGER("openstudio.model.CoilSystemCoolingWater"); + + void assignEnergyPlusIDDDefaults(); + }; + + /** \relates CoilSystemCoolingWater*/ + using OptionalCoilSystemCoolingWater = boost::optional; + + /** \relates CoilSystemCoolingWater*/ + using CoilSystemCoolingWaterVector = std::vector; + +} // namespace model +} // namespace openstudio + +#endif // MODEL_COILSYSTEMCOOLINGWATER_HPP diff --git a/src/model/CoilSystemCoolingWaterHeatExchangerAssisted.cpp b/src/model/CoilSystemCoolingWaterHeatExchangerAssisted.cpp index 9b634fce884..4c47c420be9 100644 --- a/src/model/CoilSystemCoolingWaterHeatExchangerAssisted.cpp +++ b/src/model/CoilSystemCoolingWaterHeatExchangerAssisted.cpp @@ -7,12 +7,12 @@ #include "CoilSystemCoolingWaterHeatExchangerAssisted_Impl.hpp" #include "AirToAirComponent.hpp" #include "AirToAirComponent_Impl.hpp" -#include "ControllerWaterCoil.hpp" -#include "ControllerWaterCoil_Impl.hpp" #include "WaterToAirComponent.hpp" #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" @@ -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(); @@ -107,6 +119,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; } @@ -190,29 +213,7 @@ namespace model { if (boost::optional airLoop = node.airLoopHVAC()) { if (!airLoop->demandComponent(node.handle())) { - result = StraightComponent_Impl::addToNode(node); - if (result) { - auto t_coolingCoil = coolingCoil(); - if (auto waterInletModelObject = t_coolingCoil.waterInletModelObject()) { - - // TODO: why aren't we setting the water coil in this case? @kbenne thoughts please - if (auto coilCoolingWater = t_coolingCoil.optionalCast()) { - if (auto oldController = coilCoolingWater->controllerWaterCoil()) { - oldController->remove(); - } - } - - auto t_model = model(); - ControllerWaterCoil controller(t_model); - - auto coilWaterInletNode = waterInletModelObject->optionalCast(); - OS_ASSERT(coilWaterInletNode); - controller.setActuatorNode(coilWaterInletNode.get()); - // sensor node will be established in translator since that node does not yet exist - - controller.setAction("Reverse"); - } - } + return StraightComponent_Impl::addToNode(node); } } else if (boost::optional oas = node.airLoopHVACOutdoorAirSystem()) { if (oas->airLoopHVACDedicatedOutdoorAirSystem()) { @@ -259,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 new file mode 100644 index 00000000000..8793eba87ef --- /dev/null +++ b/src/model/CoilSystemCoolingWater_Impl.hpp @@ -0,0 +1,134 @@ +/*********************************************************************************************************************** +* OpenStudio(R), Copyright (c) Alliance for Sustainable Energy, LLC. +* See also https://openstudio.net/license +***********************************************************************************************************************/ + +#ifndef MODEL_COILSYSTEMCOOLINGWATER_IMPL_HPP +#define MODEL_COILSYSTEMCOOLINGWATER_IMPL_HPP + +#include +#include "StraightComponent_Impl.hpp" + +namespace openstudio { +namespace model { + + class Schedule; + class HVACComponent; + + namespace detail { + + /** CoilSystemCoolingWater_Impl is a StraightComponent_Impl that is the implementation class for CoilSystemCoolingWater.*/ + class MODEL_API CoilSystemCoolingWater_Impl : public StraightComponent_Impl + { + public: + /** @name Constructors and Destructors */ + //@{ + + CoilSystemCoolingWater_Impl(const IdfObject& idfObject, Model_Impl* model, bool keepHandle); + + CoilSystemCoolingWater_Impl(const openstudio::detail::WorkspaceObject_Impl& other, Model_Impl* model, bool keepHandle); + + CoilSystemCoolingWater_Impl(const CoilSystemCoolingWater_Impl& other, Model_Impl* model, bool keepHandle); + + virtual ~CoilSystemCoolingWater_Impl() = default; + + //@} + /** @name Virtual Methods */ + //@{ + + virtual const std::vector& outputVariableNames() const override; + + virtual IddObjectType iddObjectType() const override; + + virtual std::vector getScheduleTypeKeys(const Schedule& schedule) const override; + + virtual unsigned inletPort() const override; + + virtual unsigned outletPort() const override; + + // will return the coolingCoil and companionCoilUsedForHeatRecovery + virtual std::vector children() const override; + + // Will also clone the coolingCoil and companionCoilUsedForHeatRecovery + virtual ModelObject clone(Model model) const override; + + // 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; + + virtual ComponentType componentType() const override; + virtual std::vector coolingFuelTypes() const override; + virtual std::vector heatingFuelTypes() const override; + virtual std::vector appGHeatingFuelTypes() const override; + + //@} + /** @name Getters */ + //@{ + + Schedule availabilitySchedule() const; + + HVACComponent coolingCoil() const; + + std::string dehumidificationControlType() const; + + bool runonSensibleLoad() const; + + bool runonLatentLoad() const; + + double minimumAirToWaterTemperatureOffset() const; + + bool economizerLockout() const; + + double minimumWaterLoopTemperatureForHeatRecovery() const; + + boost::optional companionCoilUsedForHeatRecovery() const; + + //@} + /** @name Setters */ + //@{ + + bool setAvailabilitySchedule(Schedule& schedule); + + bool setCoolingCoil(const HVACComponent& coolingCoil); + + bool setDehumidificationControlType(const std::string& dehumidificationControlType); + + bool setRunonSensibleLoad(bool runonSensibleLoad); + + bool setRunonLatentLoad(bool runonLatentLoad); + + bool setMinimumAirToWaterTemperatureOffset(double minimumAirToWaterTemperatureOffset); + + bool setEconomizerLockout(bool economizerLockout); + + bool setMinimumWaterLoopTemperatureForHeatRecovery(double minimumWaterLoopTemperatureForHeatRecovery); + + bool setCompanionCoilUsedForHeatRecovery(const HVACComponent& companionCoilUsedForHeatRecovery); + + void resetCompanionCoilUsedForHeatRecovery(); + + //@} + /** @name Other */ + //@{ + + //@} + protected: + private: + REGISTER_LOGGER("openstudio.model.CoilSystemCoolingWater"); + + boost::optional optionalAvailabilitySchedule() const; + boost::optional optionalCoolingCoil() const; + }; + + } // namespace detail + +} // namespace model +} // namespace openstudio + +#endif // MODEL_COILSYSTEMCOOLINGWATER_IMPL_HPP diff --git a/src/model/ConcreteModelObjects.hpp b/src/model/ConcreteModelObjects.hpp index df0cba1e357..57e043e49b2 100644 --- a/src/model/ConcreteModelObjects.hpp +++ b/src/model/ConcreteModelObjects.hpp @@ -135,6 +135,7 @@ #include "CoilHeatingWaterBaseboard.hpp" #include "CoilHeatingWaterBaseboardRadiant.hpp" #include "CoilPerformanceDXCooling.hpp" +#include "CoilSystemCoolingWater.hpp" #include "CoilSystemCoolingWaterHeatExchangerAssisted.hpp" #include "CoilSystemCoolingDXHeatExchangerAssisted.hpp" #include "CoilSystemIntegratedHeatPumpAirSource.hpp" @@ -693,6 +694,7 @@ #include "CoilHeatingWaterBaseboard_Impl.hpp" #include "CoilHeatingWaterBaseboardRadiant_Impl.hpp" #include "CoilPerformanceDXCooling_Impl.hpp" +#include "CoilSystemCoolingWater_Impl.hpp" #include "CoilSystemCoolingWaterHeatExchangerAssisted_Impl.hpp" #include "CoilSystemCoolingDXHeatExchangerAssisted_Impl.hpp" #include "CoilSystemIntegratedHeatPumpAirSource_Impl.hpp" diff --git a/src/model/ControllerWaterCoil.hpp b/src/model/ControllerWaterCoil.hpp index b1e99d71d6f..bab927a4393 100644 --- a/src/model/ControllerWaterCoil.hpp +++ b/src/model/ControllerWaterCoil.hpp @@ -20,6 +20,7 @@ namespace model { class ControllerWaterCoil_Impl; class CoilCoolingWater_Impl; class CoilHeatingWater_Impl; + class CoilSystemCoolingWater_Impl; class CoilSystemCoolingWaterHeatExchangerAssisted_Impl; } // namespace detail @@ -141,6 +142,7 @@ namespace model { // Classes that need to instantiate it friend class openstudio::model::detail::CoilCoolingWater_Impl; friend class openstudio::model::detail::CoilHeatingWater_Impl; + friend class openstudio::model::detail::CoilSystemCoolingWater_Impl; friend class openstudio::model::detail::CoilSystemCoolingWaterHeatExchangerAssisted_Impl; explicit ControllerWaterCoil(const Model& model); diff --git a/src/model/Model.cpp b/src/model/Model.cpp index 7d43bf68abc..657761bf567 100644 --- a/src/model/Model.cpp +++ b/src/model/Model.cpp @@ -4034,6 +4034,7 @@ namespace model { REGISTER_CONSTRUCTOR(CoilHeatingWaterBaseboard); REGISTER_CONSTRUCTOR(CoilHeatingWaterBaseboardRadiant); REGISTER_CONSTRUCTOR(CoilPerformanceDXCooling); + REGISTER_CONSTRUCTOR(CoilSystemCoolingWater); REGISTER_CONSTRUCTOR(CoilSystemCoolingWaterHeatExchangerAssisted); REGISTER_CONSTRUCTOR(CoilSystemCoolingDXHeatExchangerAssisted); REGISTER_CONSTRUCTOR(CoilSystemIntegratedHeatPumpAirSource); @@ -4612,6 +4613,7 @@ namespace model { REGISTER_COPYCONSTRUCTORS(CoilHeatingWaterBaseboard); REGISTER_COPYCONSTRUCTORS(CoilHeatingWaterBaseboardRadiant); REGISTER_COPYCONSTRUCTORS(CoilPerformanceDXCooling); + REGISTER_COPYCONSTRUCTORS(CoilSystemCoolingWater); REGISTER_COPYCONSTRUCTORS(CoilSystemCoolingWaterHeatExchangerAssisted); REGISTER_COPYCONSTRUCTORS(CoilSystemCoolingDXHeatExchangerAssisted); REGISTER_COPYCONSTRUCTORS(CoilSystemIntegratedHeatPumpAirSource); diff --git a/src/model/ModelStraightComponent.i b/src/model/ModelStraightComponent.i index 3c835cceadf..c7c77775645 100644 --- a/src/model/ModelStraightComponent.i +++ b/src/model/ModelStraightComponent.i @@ -141,6 +141,7 @@ MODELOBJECT_TEMPLATES(CoilHeatingLowTempRadiantVarFlow); MODELOBJECT_TEMPLATES(CoilHeatingWaterBaseboard); MODELOBJECT_TEMPLATES(CoilHeatingWaterBaseboardRadiant); MODELOBJECT_TEMPLATES(CoilSystemCoolingDXHeatExchangerAssisted); +MODELOBJECT_TEMPLATES(CoilSystemCoolingWater); MODELOBJECT_TEMPLATES(CoilSystemCoolingWaterHeatExchangerAssisted); MODELOBJECT_TEMPLATES(CoilSystemIntegratedHeatPumpAirSource); MODELOBJECT_TEMPLATES(CoilWaterHeatingDesuperheater); @@ -229,6 +230,7 @@ SWIG_MODELOBJECT(CoilHeatingLowTempRadiantVarFlow,1); SWIG_MODELOBJECT(CoilHeatingWaterBaseboard,1); SWIG_MODELOBJECT(CoilHeatingWaterBaseboardRadiant,1); SWIG_MODELOBJECT(CoilSystemCoolingDXHeatExchangerAssisted,1); +SWIG_MODELOBJECT(CoilSystemCoolingWater,1); SWIG_MODELOBJECT(CoilSystemCoolingWaterHeatExchangerAssisted,1); SWIG_MODELOBJECT(CoilSystemIntegratedHeatPumpAirSource,1); SWIG_MODELOBJECT(CoilWaterHeatingDesuperheater,1); diff --git a/src/model/ScheduleTypeRegistry.cpp b/src/model/ScheduleTypeRegistry.cpp index 123b45cfa31..fd2ac0ce654 100644 --- a/src/model/ScheduleTypeRegistry.cpp +++ b/src/model/ScheduleTypeRegistry.cpp @@ -224,6 +224,7 @@ namespace model { OptionalDouble()}, {"CoilHeatingWater", "Availability", "availabilitySchedule", false, "Availability", 0.0, 1.0}, {"CoilHeatingWaterBaseboard", "Availability", "availabilitySchedule", false, "Availability", 0.0, 1.0}, + {"CoilSystemCoolingWater", "Availability", "availabilitySchedule", false, "Availability", 0.0, 1.0}, {"CoilWaterHeatingDesuperheater", "Availability", "availabilitySchedule", false, "Availability", 0.0, 1.0}, {"CoilWaterHeatingDesuperheater", "Setpoint Temperature", "setpointTemperatureSchedule", true, "Temperature", OptionalDouble(), OptionalDouble()}, diff --git a/src/model/test/CoilSystemCoolingWaterHeatExchangerAssisted_GTest.cpp b/src/model/test/CoilSystemCoolingWaterHeatExchangerAssisted_GTest.cpp index acd6840f8c9..78bf7460a02 100644 --- a/src/model/test/CoilSystemCoolingWaterHeatExchangerAssisted_GTest.cpp +++ b/src/model/test/CoilSystemCoolingWaterHeatExchangerAssisted_GTest.cpp @@ -21,7 +21,6 @@ #include "../ZoneHVACFourPipeFanCoil.hpp" #include "../FanConstantVolume.hpp" #include "../CoilHeatingWater.hpp" -#include "../CoilCoolingWater.hpp" #include "../Schedule.hpp" using namespace openstudio; @@ -99,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()); @@ -119,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()); @@ -129,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 new file mode 100644 index 00000000000..964dfa57a8b --- /dev/null +++ b/src/model/test/CoilSystemCoolingWater_GTest.cpp @@ -0,0 +1,405 @@ +/*********************************************************************************************************************** +* OpenStudio(R), Copyright (c) Alliance for Sustainable Energy, LLC. +* See also https://openstudio.net/license +***********************************************************************************************************************/ + +#include "ModelFixture.hpp" + +#include "../CoilSystemCoolingWater.hpp" +#include "../CoilSystemCoolingWater_Impl.hpp" + +#include "../CoilCoolingWater.hpp" +#include "../CoilCoolingWater_Impl.hpp" +#include "../CoilSystemCoolingWaterHeatExchangerAssisted.hpp" +#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" +#include "../ScheduleTypeLimits.hpp" + +using namespace openstudio; +using namespace openstudio::model; + +TEST_F(ModelFixture, CoilSystemCoolingWater_GettersSetters) { + Model m; + + CoilSystemCoolingWater coilSystemCoolingWater(m); + + coilSystemCoolingWater.setName("My CoilSystemCoolingWater"); + + // Availability Schedule Name: Required Object + // Ctor default + Schedule availabilitySchedule = m.alwaysOnDiscreteSchedule(); + 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 + // 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("CoolReheat", coilSystemCoolingWater.dehumidificationControlType()); + + // Run on Sensible Load: Required Boolean + // 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 + EXPECT_FALSE(coilSystemCoolingWater.setMinimumAirToWaterTemperatureOffset(-10.0)); + EXPECT_EQ(1.0, coilSystemCoolingWater.minimumAirToWaterTemperatureOffset()); + + // Economizer Lockout: Required Boolean + // 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_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; + + 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_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 + { + Model m; + CoilSystemCoolingWater coilSystem(m); + + AirLoopHVAC a(m); + Node n = a.supplyOutletNode(); + + auto cc = coilSystem.coolingCoil().cast(); + + EXPECT_EQ(2u, a.supplyComponents().size()); + + EXPECT_FALSE(cc.addToNode(n)); + EXPECT_EQ(2u, a.supplyComponents().size()); + + EXPECT_TRUE(coilSystem.addToNode(n)); + EXPECT_EQ(3u, a.supplyComponents().size()); + + { + auto containingHVACComponent = cc.containingHVACComponent(); + ASSERT_TRUE(containingHVACComponent); + EXPECT_EQ(containingHVACComponent->handle(), coilSystem.handle()); + } + + // BUT, we need to be able to connect the water side of the Coil... + PlantLoop p(m); + EXPECT_TRUE(p.addDemandBranchForComponent(cc)); + } + + // Wrap Around Water Coil Heat Recovery Mode + { + Model m; + CoilSystemCoolingWater coilSystem(m); + CoilCoolingWater companionCoil(m); + coilSystem.setCompanionCoilUsedForHeatRecovery(companionCoil); + + AirLoopHVAC a(m); + Node n = a.supplyOutletNode(); + + auto cc = coilSystem.coolingCoil().cast(); + auto hr = coilSystem.companionCoilUsedForHeatRecovery().get().cast(); + + EXPECT_EQ(2u, a.supplyComponents().size()); + + EXPECT_FALSE(cc.addToNode(n)); + EXPECT_EQ(2, a.supplyComponents().size()); + + EXPECT_TRUE(hr.addToNode(n)); + EXPECT_EQ(3, a.supplyComponents().size()); + + EXPECT_TRUE(coilSystem.addToNode(n)); + EXPECT_EQ(5, a.supplyComponents().size()); + + { + auto containingHVACComponent = cc.containingHVACComponent(); + ASSERT_TRUE(containingHVACComponent); + EXPECT_EQ(containingHVACComponent->handle(), coilSystem.handle()); + } + + { + auto containingHVACComponent = hr.containingHVACComponent(); + ASSERT_TRUE(containingHVACComponent); + EXPECT_EQ(containingHVACComponent->handle(), coilSystem.handle()); + } + + // BUT, we need to be able to connect the water side of the Coil... + PlantLoop p(m); + EXPECT_TRUE(p.addDemandBranchForComponent(cc)); + } +}