diff --git a/developer/doc/ReleaseNotes/OpenStudio_Release_Notes_3_9_0_TBD.md b/developer/doc/ReleaseNotes/OpenStudio_Release_Notes_3_9_1_TDB.md similarity index 77% rename from developer/doc/ReleaseNotes/OpenStudio_Release_Notes_3_9_0_TBD.md rename to developer/doc/ReleaseNotes/OpenStudio_Release_Notes_3_9_1_TDB.md index 6cded456f3..9b78ef62b7 100644 --- a/developer/doc/ReleaseNotes/OpenStudio_Release_Notes_3_9_0_TBD.md +++ b/developer/doc/ReleaseNotes/OpenStudio_Release_Notes_3_9_1_TDB.md @@ -1,8 +1,8 @@ -# OpenStudio Version 3.9.0 +# OpenStudio Version 3.9.1 _Release Notes_ - _TDB_ -These release notes describe version 3.9.0 of the OpenStudio SDK developed by the National Renewable Energy Laboratory (NREL), Buildings and Thermal Sciences Center, Commercial Buildings Research Group, Tools Development Section, and associated collaborators. The notes are organized into the following sections: +These release notes describe version 3.9.1 of the OpenStudio SDK developed by the National Renewable Energy Laboratory (NREL), Buildings and Thermal Sciences Center, Commercial Buildings Research Group, Tools Development Section, and associated collaborators. The notes are organized into the following sections: - Overview - Where to Find OpenStudio Documentation @@ -15,7 +15,7 @@ As of April 2020, development and distribution of the OpenStudioApplication and Below is the list of components that is included in this SDK installer: -__**OpenStudio SDK 3.9.0**__ +__**OpenStudio SDK 3.9.1**__ - EnergyPlus - Command Line Interface (CLI) - Radiance @@ -34,16 +34,16 @@ __**OpenStudio SDK 3.9.0**__ # Installation Notes -OpenStudio SDK 3.9.0 is supported on: +OpenStudio SDK 3.9.1 is supported on: * 64-bit Windows 7 – 11 * macOS: 11.6+ x86_64, 12.1+ arm64 * Ubuntu: 20.04 x86_64, 22.04 x86_64, 22.04 arm64 * Centos7 -OpenStudio SDK 3.9.0 supports [EnergyPlus Release 24.2.0a](https://github.com/NREL/EnergyPlus/releases/tag/24.2.0a), which is bundled with the OpenStudio installer. It is no longer necessary to download and install EnergyPlus separately. Other builds of EnergyPlus are not supported by OpenStudio SDK 3.9.0. +OpenStudio SDK 3.9.1 supports [EnergyPlus Release @EP_VERSION@](https://github.com/NREL/EnergyPlus/releases/tag/v@EP_VERSION@), which is bundled with the OpenStudio installer. It is no longer necessary to download and install EnergyPlus separately. Other builds of EnergyPlus are not supported by OpenStudio SDK 3.9.1. -OpenStudio SDK 3.9.0 supports Radiance 5.0.a.12, which is bundled with the OpenStudio installer; users no longer must install Radiance separately, and OpenStudio will use the included Radiance version regardless of any other versions that may be installed on the system. Other builds of Radiance are not supported by OpenStudio SDK 3.9.0. +OpenStudio SDK 3.9.1 supports Radiance 5.0.a.12, which is bundled with the OpenStudio installer; users no longer must install Radiance separately, and OpenStudio will use the included Radiance version regardless of any other versions that may be installed on the system. Other builds of Radiance are not supported by OpenStudio SDK 3.9.1. As usual, you can refer to the **[OpenStudio SDK Compatibility Matrix](https://github.com/NREL/OpenStudio/wiki/OpenStudio-SDK-Version-Compatibility-Matrix)** for more information. @@ -57,7 +57,7 @@ For help with common installation problems please visit [Getting Started](http:/ # OpenStudio SDK: Changelog -The 3.9.0 is a **major** release. This update includes several new features, performance improvements, and bug fixes. +The 3.9.1 is a **** release. This update includes several new features, performance improvements, and bug fixes. ## C++ Workflow code @@ -67,7 +67,7 @@ As of OpenStudio SDK 3.7.0 a re-written workflow written in C++ is used by defau As of OpenStudio SDK 3.2.0, Python bindings are officially supported and distributed through Python Package Index (PyPI). To install, users will need to have Python3 installed along with pip and simply run the following command in a terminal window. -`pip install openstudio==3.9.0` +`pip install openstudio==3.9.1` Please see [openstudio on PyPi](https://pypi.org/project/openstudio/) for further instructions on how to install. Users can also visit the test channel at [openstudio on TestPyPi](https://test.pypi.org/project/openstudio/) to install development bindings. @@ -75,20 +75,15 @@ You can also refer to the [OpenStudio SDK Python Binding Version Compatibility M ## New Features, Major Fixes and API-breaking changes -* [#5242](https://github.com/NREL/OpenStudio/pull/5242) - Update to EnergyPlus v24.2.0a - * To see the full list of additions and changes, refer to the issue [#5240](https://github.com/NREL/OpenStudio/issues/5240) - -* [#5237](https://github.com/NREL/OpenStudio/pull/5237) - Updates to Controller:OutdoorAir - * This PR implements the fields `Humidistat Control Zone Name` and `Electronic Enthalpy Limit Curve` - * `ControllerOutdoorAir` has two API-breaking changes for `High Humidity Outdoor Air Flow Ratio` and `Control High Indoor Humidity Based on Outdoor Humidity Ratio`. These fields are now-required, so the getters no longer return an optional - * `getHighHumidityOutdoorAirFlowRatio` (`boost::optional` to `double`) - * `getControlHighIndoorHumidityBasedOnOutdoorHumidityRatio` (`boost::optional` to `bool`) +* [#5326](https://github.com/NREL/OpenStudio/pull/5326) - Wrap ZoneHVAC:EvaporativeCoolerUnit + * The object was wrapped in the SDK. + * Note: in EnergyPlus 24.2.0, the `Zone Relief Air Node Name` is an optional field. The OpenStudio SDK always fills with the connected zone's Exhaust Air Node, meaning the airflow is always being balanced by EnergyPlus: the object will extract air from the zone to balance the air supplied to the zone by the cooler outlet node. ## Minor changes and bug fixes -Refer to the changelog on the release page at [v3.9.0](https://github.com/NREL/OpenStudio/releases/v3.9.0) +Refer to the changelog on the release page at [v3.9.1](https://github.com/NREL/OpenStudio/releases/v3.9.1) -**Full Changelog**: https://github.com/NREL/OpenStudio/compare/v3.9.0...v3.9.0 +**Full Changelog**: https://github.com/NREL/OpenStudio/compare/v3.9.0...v3.9.1 **New Contributors**: @@ -106,7 +101,7 @@ In addition to some refactoring, this release also included conversion of 90.1 d --- # This YAML header controls the pandoc (via TeX) to PDF settings # To convert the markdown to pdf, do `pandoc release_notes.md -o release_notes.pdf` -title: 'OpenStudio Release Notes - 3.9.0' +title: 'OpenStudio Release Notes - 3.9.1' author: - National Renewable Energy Laboratory colorlinks: true diff --git a/resources/model/OpenStudio.idd b/resources/model/OpenStudio.idd index 3c3f6568f7..8ed098072d 100644 --- a/resources/model/OpenStudio.idd +++ b/resources/model/OpenStudio.idd @@ -24507,6 +24507,7 @@ OS:EvaporativeCooler:Indirect:ResearchSpecial, \type alpha \required-field \reference ConnectionObject + \reference EvapCoolerNames A3 , \field Availability Schedule Name \note Availability schedule name for this system. Schedule value > 0 means the system is available. \note If this field is blank, the system is always available. @@ -24657,6 +24658,7 @@ OS:EvaporativeCooler:Direct:ResearchSpecial, \type alpha \required-field \reference ConnectionObject + \reference EvapCoolerNames A3, \field Availability Schedule Name \type object-list \required-field @@ -31192,13 +31194,100 @@ OS:ZoneHVAC:CoolingPanel:RadiantConvective:Water, \minimum 0 \maximum 1 +OS:ZoneHVAC:EvaporativeCoolerUnit, + \memo Zone evaporative cooler. Forced-convection cooling-only unit with supply fan, + \memo 100% outdoor air supply. Optional relief exhaust node + \min-fields 17 + A1, \field Handle + \type handle + \required-field + A2, \field Name + \required-field + \type alpha + \reference ConnectionObject + A3, \field Availability Schedule Name + \note Availability schedule name for this system. Schedule value > 0 means the system is available. + \note If this field is blank, the system is always available. + \required-field + \type object-list + \object-list ScheduleNames + A4, \field Availability Manager List Name + \note Enter the name of an AvailabilityManagerAssignmentList object. + \type object-list + \object-list SystemAvailabilityManagerLists + A5, \field Outdoor Air Inlet Node Name + \required-field + \type object-list + \object-list ConnectionNames + \note this is an outdoor air node + A6, \field Cooler Outlet Node Name + \required-field + \type object-list + \object-list ConnectionNames + \note this is a zone inlet node + A7, \field Zone Relief Air Node Name + \type object-list + \object-list ConnectionNames + \note this is a zone exhaust node, optional if flow is being balanced elsewhere + A8, \field Supply Air Fan Name + \required-field + \type object-list + \object-list Fans + N1, \field Design Supply Air Flow Rate + \required-field + \units m3/s + \minimum> 0 + \autosizable + A9, \field Fan Placement + \required-field + \type choice + \key BlowThrough + \key DrawThrough + A10, \field Cooler Unit Control Method + \required-field + \type choice + \key ZoneTemperatureDeadbandOnOffCycling + \key ZoneCoolingLoadOnOffCycling + \key ZoneCoolingLoadVariableSpeedFan + N2, \field Throttling Range Temperature Difference + \note used for ZoneTemperatureDeadbandOnOffCycling hysteresis range for thermostatic control + \type real + \units deltaC + \required-field + \minimum> 0.0 + N3, \field Cooling Load Control Threshold Heat Transfer Rate + \type real + \units W + \required-field + \note Sign convention is that positive values indicate a cooling load + \minimum> 0.0 + A11, \field First Evaporative Cooler + \required-field + \type object-list + \object-list EvapCoolerNames + A12, \field Second Evaporative Cooler + \note optional, used for direct/indirect configurations + \type object-list + \object-list EvapCoolerNames + A13, \field Design Specification ZoneHVAC Sizing + \note Enter the name of a DesignSpecificationZoneHVACSizing object. + \type object-list + \object-list DesignSpecificationZoneHVACSizingName + N4; \field Shut Off Relative Humidity + \note Zone relative humidity above which the evap cooler is shut off. + \required-field + \type real + \minimum 0.00 + \maximum 100.00 + \units percent + OS:ZoneMixing, \memo ZoneMixing is a simple air exchange from one zone or space to another. Note that this statement \memo only affects the energy balance of the "receiving" zone or space and will not produce \memo any effect on the "source" zone. Mixing statements can be complementary and include \memo multiple zones, but the balancing of flows between zones is left to the user's \memo discretion. - \min-fields 18 + \min-fields 18 A1, \field Handle \type handle \required-field diff --git a/src/energyplus/CMakeLists.txt b/src/energyplus/CMakeLists.txt index eaafa6c3fb..e7d407a525 100644 --- a/src/energyplus/CMakeLists.txt +++ b/src/energyplus/CMakeLists.txt @@ -444,6 +444,7 @@ set(${target_name}_src ForwardTranslator/ForwardTranslateZoneHVACEnergyRecoveryVentilator.cpp ForwardTranslator/ForwardTranslateZoneHVACEnergyRecoveryVentilatorController.cpp ForwardTranslator/ForwardTranslateZoneHVACEquipmentList.cpp + ForwardTranslator/ForwardTranslateZoneHVACEvaporativeCoolerUnit.cpp ForwardTranslator/ForwardTranslateZoneHVACFourPipeFanCoil.cpp ForwardTranslator/ForwardTranslateZoneHVACHighTemperatureRadiant.cpp ForwardTranslator/ForwardTranslateZoneHVACIdealLoadsAirSystem.cpp @@ -840,6 +841,7 @@ set(${target_name}_test_src Test/WaterUseConnections_GTest.cpp Test/ZoneAirHeatBalanceAlgorithm_GTest.cpp Test/ZoneHVACBaseboardRadiantConvectiveWater_GTest.cpp + Test/ZoneHVACEvaporativeCoolerUnit_GTest.cpp Test/ZoneHVACLowTemperatureRadiantElectric_GTest.cpp Test/ZoneHVACLowTempRadiantConstFlow_GTest.cpp Test/ZoneHVACLowTempRadiantVarFlow_GTest.cpp diff --git a/src/energyplus/ForwardTranslator.cpp b/src/energyplus/ForwardTranslator.cpp index 10432603e3..ec486ebb35 100644 --- a/src/energyplus/ForwardTranslator.cpp +++ b/src/energyplus/ForwardTranslator.cpp @@ -3204,6 +3204,11 @@ namespace energyplus { retVal = translateZoneHVACEquipmentList(mo); break; } + case openstudio::IddObjectType::OS_ZoneHVAC_EvaporativeCoolerUnit: { + auto mo = modelObject.cast(); + retVal = translateZoneHVACEvaporativeCoolerUnit(mo); + break; + } case openstudio::IddObjectType::OS_ZoneHVAC_FourPipeFanCoil: { auto mo = modelObject.cast(); retVal = translateZoneHVACFourPipeFanCoil(mo); diff --git a/src/energyplus/ForwardTranslator.hpp b/src/energyplus/ForwardTranslator.hpp index b14c46dabe..f007e98448 100644 --- a/src/energyplus/ForwardTranslator.hpp +++ b/src/energyplus/ForwardTranslator.hpp @@ -481,6 +481,7 @@ namespace model { class ZoneHVACEnergyRecoveryVentilator; class ZoneHVACEnergyRecoveryVentilatorController; class ZoneHVACEquipmentList; + class ZoneHVACEvaporativeCoolerUnit; class ZoneHVACFourPipeFanCoil; class ZoneHVACHighTemperatureRadiant; class ZoneHVACIdealLoadsAirSystem; @@ -1551,6 +1552,8 @@ namespace energyplus { boost::optional translateZoneHVACEquipmentList(model::ZoneHVACEquipmentList& modelObject); + boost::optional translateZoneHVACEvaporativeCoolerUnit(model::ZoneHVACEvaporativeCoolerUnit& modelObject); + boost::optional translateZoneHVACFourPipeFanCoil(model::ZoneHVACFourPipeFanCoil& modelObject); boost::optional translateZoneHVACHighTemperatureRadiant(model::ZoneHVACHighTemperatureRadiant& modelObject); diff --git a/src/energyplus/ForwardTranslator/ForwardTranslateZoneHVACEvaporativeCoolerUnit.cpp b/src/energyplus/ForwardTranslator/ForwardTranslateZoneHVACEvaporativeCoolerUnit.cpp new file mode 100644 index 0000000000..e877912dae --- /dev/null +++ b/src/energyplus/ForwardTranslator/ForwardTranslateZoneHVACEvaporativeCoolerUnit.cpp @@ -0,0 +1,262 @@ +/*********************************************************************************************************************** +* OpenStudio(R), Copyright (c) 2008-2023, Alliance for Sustainable Energy, LLC, and other contributors. All rights reserved. +* +* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +* following conditions are met: +* +* (1) Redistributions of source code must retain the above copyright notice, this list of conditions and the following +* disclaimer. +* +* (2) Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following +* disclaimer in the documentation and/or other materials provided with the distribution. +* +* (3) Neither the name of the copyright holder nor the names of any contributors may be used to endorse or promote products +* derived from this software without specific prior written permission from the respective party. +* +* (4) Other than as required in clauses (1) and (2), distributions in any form of modifications or other derivative works +* may not use the "OpenStudio" trademark, "OS", "os", or any other confusingly similar designation without specific prior +* written permission from Alliance for Sustainable Energy, LLC. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND ANY CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S), ANY CONTRIBUTORS, THE UNITED STATES GOVERNMENT, OR THE UNITED +* STATES DEPARTMENT OF ENERGY, NOR ANY OF THEIR EMPLOYEES, BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF +* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***********************************************************************************************************************/ + +#include "../ForwardTranslator.hpp" +#include "../../model/Model.hpp" +#include "../../model/ZoneHVACEvaporativeCoolerUnit.hpp" +#include "../../model/ZoneHVACEvaporativeCoolerUnit_Impl.hpp" +#include "../../model/Schedule.hpp" +#include "../../model/Schedule_Impl.hpp" +#include "../../model/Node.hpp" +#include "../../model/Node_Impl.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace openstudio::model; + +namespace openstudio { + +namespace energyplus { + + boost::optional ForwardTranslator::translateZoneHVACEvaporativeCoolerUnit(model::ZoneHVACEvaporativeCoolerUnit& modelObject) { + + boost::optional i_firstEvaporativeCooler_; + { + HVACComponent firstEvaporativeCooler = modelObject.firstEvaporativeCooler(); + i_firstEvaporativeCooler_ = translateAndMapModelObject(firstEvaporativeCooler); + if (!i_firstEvaporativeCooler_) { + LOG(Error, "ZoneHVACEvaporativeCoolerUnit '" << modelObject.nameString() << "', could not translate required First Evaporative Cooler:" + << firstEvaporativeCooler.briefDescription()); + return boost::none; + } + } + + boost::optional i_fan_; + { + HVACComponent supplyAirFan = modelObject.supplyAirFan(); + i_fan_ = translateAndMapModelObject(supplyAirFan); + if (!i_fan_) { + LOG(Error, "ZoneHVACEvaporativeCoolerUnit '" << modelObject.nameString() + << "', could not translate required Supply Air Fan:" << supplyAirFan.briefDescription()); + return boost::none; + } + } + + // i_firstEvaporativeCooler_ and i_fan_ are always initialized now + + // Instantiate an IdfObject of the class to store the values + IdfObject idfObject = createRegisterAndNameIdfObject(openstudio::IddObjectType::ZoneHVAC_EvaporativeCoolerUnit, modelObject); + + // Availability Schedule Name: Required Object + Schedule availabilitySchedule_ = modelObject.availabilitySchedule(); + if (boost::optional wo_ = translateAndMapModelObject(availabilitySchedule_)) { + idfObject.setString(ZoneHVAC_EvaporativeCoolerUnitFields::AvailabilityScheduleName, wo_->nameString()); + } + + const auto outdoorAirInletNodeName = modelObject.nameString() + " Outdoor Air Node"; + std::string coolerOutletNodeName; + + // Outdoor Air Inlet Node Name: Required Node - This is an Outdoor Air Node + { + IdfObject oaNodeListIdf(openstudio::IddObjectType::OutdoorAir_NodeList); + oaNodeListIdf.setString(0, outdoorAirInletNodeName); + m_idfObjects.emplace_back(std::move(oaNodeListIdf)); + idfObject.setString(ZoneHVAC_EvaporativeCoolerUnitFields::OutdoorAirInletNodeName, outdoorAirInletNodeName); + } + + // Technically outletNode and inletNode are always initialized, since they are connected via addToThermalZone and this FT routine is called by + // thermalZone::equipment + + // Cooler Outlet Node Name: Required Node, a zone air inlet node + if (boost::optional node = modelObject.outletNode()) { + coolerOutletNodeName = node->nameString(); + idfObject.setString(ZoneHVAC_EvaporativeCoolerUnitFields::CoolerOutletNodeName, node->nameString()); + } + + // Zone Relief Air Node Name: optional Node, filled to a Zone Exhaust Node if the flow is being balanced here and not elsewhere + // NOTE: we always assume the airflow is balanced + if (boost::optional node = modelObject.inletNode()) { + idfObject.setString(ZoneHVAC_EvaporativeCoolerUnitFields::ZoneReliefAirNodeName, node->nameString()); + } + + // Supply Air Fan Object Type + // Supply Air Fan Name + idfObject.setString(ZoneHVAC_EvaporativeCoolerUnitFields::SupplyAirFanObjectType, i_fan_->iddObject().name()); + idfObject.setString(ZoneHVAC_EvaporativeCoolerUnitFields::SupplyAirFanName, i_fan_->nameString()); + + if (modelObject.isDesignSupplyAirFlowRateAutosized()) { + idfObject.setString(ZoneHVAC_EvaporativeCoolerUnitFields::DesignSupplyAirFlowRate, "Autosize"); + } else { + // Design Supply Air Flow Rate: boost::optional + idfObject.setDouble(ZoneHVAC_EvaporativeCoolerUnitFields::DesignSupplyAirFlowRate, modelObject.designSupplyAirFlowRate().get()); + } + + // Fan Placement: Required String + const std::string fanPlacement = modelObject.fanPlacement(); + const bool blowThroughFan = istringEqual(fanPlacement, "BlowThrough"); + idfObject.setString(ZoneHVAC_EvaporativeCoolerUnitFields::FanPlacement, fanPlacement); + + // Cooler Unit Control Method: Required String + const std::string coolerUnitControlMethod = modelObject.coolerUnitControlMethod(); + idfObject.setString(ZoneHVAC_EvaporativeCoolerUnitFields::CoolerUnitControlMethod, coolerUnitControlMethod); + + // Throttling Range Temperature Difference: Optional Double + const double throttlingRangeTemperatureDifference = modelObject.throttlingRangeTemperatureDifference(); + idfObject.setDouble(ZoneHVAC_EvaporativeCoolerUnitFields::ThrottlingRangeTemperatureDifference, throttlingRangeTemperatureDifference); + + // Cooling Load Control Threshold Heat Transfer Rate: Optional Double + const double coolingLoadControlThresholdHeatTransferRate = modelObject.coolingLoadControlThresholdHeatTransferRate(); + idfObject.setDouble(ZoneHVAC_EvaporativeCoolerUnitFields::CoolingLoadControlThresholdHeatTransferRate, + coolingLoadControlThresholdHeatTransferRate); + + // First Evaporative Cooler Object Type: Required String + // First Evaporative Cooler Object Name: Required Object + idfObject.setString(ZoneHVAC_EvaporativeCoolerUnitFields::FirstEvaporativeCoolerObjectType, i_firstEvaporativeCooler_->iddObject().name()); + idfObject.setString(ZoneHVAC_EvaporativeCoolerUnitFields::FirstEvaporativeCoolerObjectName, i_firstEvaporativeCooler_->nameString()); + + // Second Evaporative Cooler Object Type: boost::optional + // Second Evaporative Cooler Name: Optional Object + boost::optional i_secondEvaporativeCooler_; + if (boost::optional secondEvaporativeCooler_ = modelObject.secondEvaporativeCooler()) { + i_secondEvaporativeCooler_ = translateAndMapModelObject(*secondEvaporativeCooler_); + if (i_secondEvaporativeCooler_) { + idfObject.setString(ZoneHVAC_EvaporativeCoolerUnitFields::SecondEvaporativeCoolerObjectType, i_secondEvaporativeCooler_->iddObject().name()); + idfObject.setString(ZoneHVAC_EvaporativeCoolerUnitFields::SecondEvaporativeCoolerName, i_secondEvaporativeCooler_->nameString()); + } + } + + // If BlowThrough: o---- Fan ---- E1 (---- E2) ----o + // If DrawThrough: o---- E1 (---- E2) ---- Fan ----o + std::string baseName = modelObject.nameString(); + + // if (i_fan_) is always true + { + std::string inletNodeName; + std::string outletNodeName; + if (blowThroughFan) { + inletNodeName = outdoorAirInletNodeName; + outletNodeName = baseName + " Fan - First Evaporative Cooler Node"; + } else { + if (i_secondEvaporativeCooler_) { + inletNodeName = baseName + " Second Evaporative Cooler - Fan Node"; + } else { + inletNodeName = baseName + " First Evaporative Cooler - Fan Node"; + } + outletNodeName = coolerOutletNodeName; + } + + if (i_fan_->iddObject().type() == IddObjectType::Fan_ConstantVolume) { + i_fan_->setString(Fan_ConstantVolumeFields::AirInletNodeName, inletNodeName); + i_fan_->setString(Fan_ConstantVolumeFields::AirOutletNodeName, outletNodeName); + } else if (i_fan_->iddObject().type() == IddObjectType::Fan_VariableVolume) { + i_fan_->setString(Fan_VariableVolumeFields::AirInletNodeName, inletNodeName); + i_fan_->setString(Fan_VariableVolumeFields::AirOutletNodeName, outletNodeName); + } else if (i_fan_->iddObject().type() == IddObjectType::Fan_OnOff) { + i_fan_->setString(Fan_OnOffFields::AirInletNodeName, inletNodeName); + i_fan_->setString(Fan_OnOffFields::AirOutletNodeName, outletNodeName); + } else if (i_fan_->iddObject().type() == IddObjectType::Fan_SystemModel) { + i_fan_->setString(Fan_SystemModelFields::AirInletNodeName, inletNodeName); + i_fan_->setString(Fan_SystemModelFields::AirOutletNodeName, outletNodeName); + } else if (i_fan_->iddObject().type() == IddObjectType::Fan_ComponentModel) { + i_fan_->setString(Fan_ComponentModelFields::AirInletNodeName, inletNodeName); + i_fan_->setString(Fan_ComponentModelFields::AirOutletNodeName, outletNodeName); + } + } + + // if (i_firstEvaporativeCooler_) is always true + { + std::string inletNodeName; + std::string outletNodeName; + if (blowThroughFan) { + inletNodeName = baseName + " Fan - First Evaporative Cooler Node"; + if (i_secondEvaporativeCooler_) { + outletNodeName = baseName + " First Evaporative Cooler - Second Evaporative Cooler Node"; + } else { + outletNodeName = coolerOutletNodeName; + } + } else { + inletNodeName = outdoorAirInletNodeName; + if (i_secondEvaporativeCooler_) { + outletNodeName = baseName + " First Evaporative Cooler - Second Evaporative Cooler Node"; + } else { + outletNodeName = baseName + " First Evaporative Cooler - Fan Node"; + } + } + + if (i_firstEvaporativeCooler_->iddObject().type() == IddObjectType::EvaporativeCooler_Direct_ResearchSpecial) { + i_firstEvaporativeCooler_->setString(EvaporativeCooler_Direct_ResearchSpecialFields::AirInletNodeName, inletNodeName); + i_firstEvaporativeCooler_->setString(EvaporativeCooler_Direct_ResearchSpecialFields::AirOutletNodeName, outletNodeName); + i_firstEvaporativeCooler_->setString(EvaporativeCooler_Direct_ResearchSpecialFields::SensorNodeName, coolerOutletNodeName); + } else if (i_firstEvaporativeCooler_->iddObject().type() == IddObjectType::EvaporativeCooler_Indirect_ResearchSpecial) { + i_firstEvaporativeCooler_->setString(EvaporativeCooler_Indirect_ResearchSpecialFields::PrimaryAirInletNodeName, inletNodeName); + i_firstEvaporativeCooler_->setString(EvaporativeCooler_Indirect_ResearchSpecialFields::PrimaryAirOutletNodeName, outletNodeName); + i_firstEvaporativeCooler_->setString(EvaporativeCooler_Indirect_ResearchSpecialFields::SensorNodeName, coolerOutletNodeName); + } else { + LOG(Warn, modelObject.briefDescription() << ": Contains an unsupported type " << i_firstEvaporativeCooler_->iddObject().type() << "."); + } + } + + if (i_secondEvaporativeCooler_) { + const std::string inletNodeName = baseName + " First Evaporative Cooler - Second Evaporative Cooler Node"; + std::string outletNodeName; + if (blowThroughFan) { + outletNodeName = coolerOutletNodeName; + } else { + outletNodeName = baseName + " Second Evaporative Cooler - Fan Node"; + } + + if (i_secondEvaporativeCooler_->iddObject().type() == IddObjectType::EvaporativeCooler_Direct_ResearchSpecial) { + i_secondEvaporativeCooler_->setString(EvaporativeCooler_Direct_ResearchSpecialFields::AirInletNodeName, inletNodeName); + i_secondEvaporativeCooler_->setString(EvaporativeCooler_Direct_ResearchSpecialFields::AirOutletNodeName, outletNodeName); + i_secondEvaporativeCooler_->setString(EvaporativeCooler_Direct_ResearchSpecialFields::SensorNodeName, coolerOutletNodeName); + } else if (i_secondEvaporativeCooler_->iddObject().type() == IddObjectType::EvaporativeCooler_Indirect_ResearchSpecial) { + i_secondEvaporativeCooler_->setString(EvaporativeCooler_Indirect_ResearchSpecialFields::PrimaryAirInletNodeName, inletNodeName); + i_secondEvaporativeCooler_->setString(EvaporativeCooler_Indirect_ResearchSpecialFields::PrimaryAirOutletNodeName, outletNodeName); + i_secondEvaporativeCooler_->setString(EvaporativeCooler_Indirect_ResearchSpecialFields::SensorNodeName, coolerOutletNodeName); + } else { + LOG(Warn, modelObject.briefDescription() << ": Contains an unsupported type " << i_secondEvaporativeCooler_->iddObject().type() << "."); + } + } + + // Shut Off Relative Humidity: boost::optional + idfObject.setDouble(ZoneHVAC_EvaporativeCoolerUnitFields::ShutOffRelativeHumidity, modelObject.shutOffRelativeHumidity()); + + return idfObject; + } // End of translate function + +} // end namespace energyplus +} // end namespace openstudio diff --git a/src/energyplus/Test/ZoneHVACEvaporativeCoolerUnit_GTest.cpp b/src/energyplus/Test/ZoneHVACEvaporativeCoolerUnit_GTest.cpp new file mode 100644 index 0000000000..a0e13d3bcf --- /dev/null +++ b/src/energyplus/Test/ZoneHVACEvaporativeCoolerUnit_GTest.cpp @@ -0,0 +1,278 @@ +/*********************************************************************************************************************** +* OpenStudio(R), Copyright (c) 2008-2023, Alliance for Sustainable Energy, LLC, and other contributors. All rights reserved. +* +* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +* following conditions are met: +* +* (1) Redistributions of source code must retain the above copyright notice, this list of conditions and the following +* disclaimer. +* +* (2) Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following +* disclaimer in the documentation and/or other materials provided with the distribution. +* +* (3) Neither the name of the copyright holder nor the names of any contributors may be used to endorse or promote products +* derived from this software without specific prior written permission from the respective party. +* +* (4) Other than as required in clauses (1) and (2), distributions in any form of modifications or other derivative works +* may not use the "OpenStudio" trademark, "OS", "os", or any other confusingly similar designation without specific prior +* written permission from Alliance for Sustainable Energy, LLC. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND ANY CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S), ANY CONTRIBUTORS, THE UNITED STATES GOVERNMENT, OR THE UNITED +* STATES DEPARTMENT OF ENERGY, NOR ANY OF THEIR EMPLOYEES, BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF +* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***********************************************************************************************************************/ + +#include +#include "EnergyPlusFixture.hpp" + +#include "../ForwardTranslator.hpp" + +#include "../../model/ZoneHVACEvaporativeCoolerUnit.hpp" +#include "../../model/ZoneHVACEvaporativeCoolerUnit_Impl.hpp" +#include "../../model/Schedule.hpp" +#include "../../model/Schedule_Impl.hpp" +#include "../../model/FanComponentModel.hpp" +#include "../../model/FanComponentModel_Impl.hpp" +#include "../../model/FanSystemModel.hpp" +#include "../../model/FanSystemModel_Impl.hpp" +#include "../../model/EvaporativeCoolerDirectResearchSpecial.hpp" +#include "../../model/EvaporativeCoolerDirectResearchSpecial_Impl.hpp" +#include "../../model/EvaporativeCoolerIndirectResearchSpecial.hpp" +#include "../../model/EvaporativeCoolerIndirectResearchSpecial_Impl.hpp" +#include "../../model/Node.hpp" +#include "../../model/PortList.hpp" +#include "../../model/ThermalZone.hpp" +#include "../../model/Space.hpp" + +#include "../../utilities/idf/Workspace.hpp" +#include "../../utilities/idf/IdfObject.hpp" +#include "../../utilities/idf/WorkspaceObject.hpp" +// E+ FieldEnums +#include +#include +#include +#include +#include +#include +#include + +using namespace openstudio::energyplus; +using namespace openstudio::model; +using namespace openstudio; + +TEST_F(EnergyPlusFixture, ForwardTranslator_ZoneHVACEvaporativeCoolerUnit) { + + ForwardTranslator ft; + + Model m; + + ZoneHVACEvaporativeCoolerUnit zoneHVACEvaporativeCoolerUnit(m); + + zoneHVACEvaporativeCoolerUnit.setName("My ZoneHVACEvaporativeCoolerUnit"); + Schedule availabilitySchedule = m.alwaysOnDiscreteSchedule(); + EXPECT_TRUE(zoneHVACEvaporativeCoolerUnit.setAvailabilitySchedule(availabilitySchedule)); + FanComponentModel supplyAirFan(m); + EXPECT_TRUE(zoneHVACEvaporativeCoolerUnit.setSupplyAirFan(supplyAirFan)); + EXPECT_TRUE(zoneHVACEvaporativeCoolerUnit.setDesignSupplyAirFlowRate(0.9)); + EXPECT_TRUE(zoneHVACEvaporativeCoolerUnit.setFanPlacement("BlowThrough")); + EXPECT_TRUE(zoneHVACEvaporativeCoolerUnit.setCoolerUnitControlMethod("ZoneTemperatureDeadbandOnOffCycling")); + EXPECT_TRUE(zoneHVACEvaporativeCoolerUnit.setThrottlingRangeTemperatureDifference(1.2)); + EXPECT_TRUE(zoneHVACEvaporativeCoolerUnit.setCoolingLoadControlThresholdHeatTransferRate(1.3)); + EvaporativeCoolerDirectResearchSpecial firstEvaporativeCooler(m, availabilitySchedule); + EXPECT_TRUE(zoneHVACEvaporativeCoolerUnit.setFirstEvaporativeCooler(firstEvaporativeCooler)); + EvaporativeCoolerIndirectResearchSpecial secondEvaporativeCooler(m); + EXPECT_TRUE(zoneHVACEvaporativeCoolerUnit.setSecondEvaporativeCooler(secondEvaporativeCooler)); + EXPECT_TRUE(zoneHVACEvaporativeCoolerUnit.setShutOffRelativeHumidity(95.0)); + + // Need to be in a thermal zone to be translated, with at least one space + ThermalZone z(m); + zoneHVACEvaporativeCoolerUnit.addToThermalZone(z); + Space s(m); + s.setThermalZone(z); + + z.inletPortList().modelObjects()[0].setName("Zone Air Inlet Node"); + z.exhaustPortList().modelObjects()[0].setName("Zone Air Exhaust Node"); + z.zoneAirNode().setName("Zone Air Node"); + + const Workspace w = ft.translateModel(m); + const auto idfObjs = w.getObjectsByType(IddObjectType::ZoneHVAC_EvaporativeCoolerUnit); + ASSERT_EQ(1u, idfObjs.size()); + const auto& idfObject = idfObjs.front(); + + EXPECT_EQ(zoneHVACEvaporativeCoolerUnit.nameString(), idfObject.getString(ZoneHVAC_EvaporativeCoolerUnitFields::Name).get()); + EXPECT_EQ(availabilitySchedule.nameString(), idfObject.getString(ZoneHVAC_EvaporativeCoolerUnitFields::AvailabilityScheduleName).get()); + EXPECT_TRUE(idfObject.isEmpty(ZoneHVAC_EvaporativeCoolerUnitFields::AvailabilityManagerListName)); + EXPECT_EQ("My ZoneHVACEvaporativeCoolerUnit Outdoor Air Node", + idfObject.getString(ZoneHVAC_EvaporativeCoolerUnitFields::OutdoorAirInletNodeName).get()); + EXPECT_EQ("Zone Air Inlet Node", idfObject.getString(ZoneHVAC_EvaporativeCoolerUnitFields::CoolerOutletNodeName).get()); + EXPECT_EQ("Zone Air Exhaust Node", idfObject.getString(ZoneHVAC_EvaporativeCoolerUnitFields::ZoneReliefAirNodeName).get()); + EXPECT_EQ("Fan:ComponentModel", idfObject.getString(ZoneHVAC_EvaporativeCoolerUnitFields::SupplyAirFanObjectType).get()); + EXPECT_EQ(supplyAirFan.nameString(), idfObject.getString(ZoneHVAC_EvaporativeCoolerUnitFields::SupplyAirFanName).get()); + EXPECT_EQ(0.9, idfObject.getDouble(ZoneHVAC_EvaporativeCoolerUnitFields::DesignSupplyAirFlowRate).get()); + EXPECT_EQ("BlowThrough", idfObject.getString(ZoneHVAC_EvaporativeCoolerUnitFields::FanPlacement).get()); + EXPECT_EQ("ZoneTemperatureDeadbandOnOffCycling", idfObject.getString(ZoneHVAC_EvaporativeCoolerUnitFields::CoolerUnitControlMethod).get()); + EXPECT_EQ(1.2, idfObject.getDouble(ZoneHVAC_EvaporativeCoolerUnitFields::ThrottlingRangeTemperatureDifference).get()); + EXPECT_EQ(1.3, idfObject.getDouble(ZoneHVAC_EvaporativeCoolerUnitFields::CoolingLoadControlThresholdHeatTransferRate).get()); + EXPECT_EQ("EvaporativeCooler:Direct:ResearchSpecial", + idfObject.getString(ZoneHVAC_EvaporativeCoolerUnitFields::FirstEvaporativeCoolerObjectType).get()); + EXPECT_EQ(firstEvaporativeCooler.nameString(), idfObject.getString(ZoneHVAC_EvaporativeCoolerUnitFields::FirstEvaporativeCoolerObjectName).get()); + EXPECT_EQ("EvaporativeCooler:Indirect:ResearchSpecial", + idfObject.getString(ZoneHVAC_EvaporativeCoolerUnitFields::SecondEvaporativeCoolerObjectType).get()); + EXPECT_EQ(secondEvaporativeCooler.nameString(), idfObject.getString(ZoneHVAC_EvaporativeCoolerUnitFields::SecondEvaporativeCoolerName).get()); + EXPECT_TRUE(idfObject.isEmpty(ZoneHVAC_EvaporativeCoolerUnitFields::DesignSpecificationZoneHVACSizingObjectName)); + EXPECT_EQ(95.0, idfObject.getDouble(ZoneHVAC_EvaporativeCoolerUnitFields::ShutOffRelativeHumidity).get()); + + auto idf_supplyAirFan = idfObject.getTarget(ZoneHVAC_EvaporativeCoolerUnitFields::SupplyAirFanName).get(); + EXPECT_EQ(idf_supplyAirFan.iddObject().type(), IddObjectType::Fan_ComponentModel); + + auto idf_firstEvaporativeCooler = idfObject.getTarget(ZoneHVAC_EvaporativeCoolerUnitFields::FirstEvaporativeCoolerObjectName).get(); + EXPECT_EQ(idf_firstEvaporativeCooler.iddObject().type(), IddObjectType::EvaporativeCooler_Direct_ResearchSpecial); + + auto idf_secondEvaporativeCooler = idfObject.getTarget(ZoneHVAC_EvaporativeCoolerUnitFields::SecondEvaporativeCoolerName).get(); + EXPECT_EQ(idf_secondEvaporativeCooler.iddObject().type(), IddObjectType::EvaporativeCooler_Indirect_ResearchSpecial); + + EXPECT_EQ("My ZoneHVACEvaporativeCoolerUnit Outdoor Air Node", idf_supplyAirFan.getString(Fan_ComponentModelFields::AirInletNodeName).get()); + EXPECT_EQ(zoneHVACEvaporativeCoolerUnit.nameString() + " Fan - First Evaporative Cooler Node", + idf_supplyAirFan.getString(Fan_ComponentModelFields::AirOutletNodeName).get()); + + EXPECT_EQ(zoneHVACEvaporativeCoolerUnit.nameString() + " Fan - First Evaporative Cooler Node", + idf_firstEvaporativeCooler.getString(EvaporativeCooler_Direct_ResearchSpecialFields::AirInletNodeName).get()); + EXPECT_EQ(zoneHVACEvaporativeCoolerUnit.nameString() + " First Evaporative Cooler - Second Evaporative Cooler Node", + idf_firstEvaporativeCooler.getString(EvaporativeCooler_Direct_ResearchSpecialFields::AirOutletNodeName).get()); + + EXPECT_EQ(zoneHVACEvaporativeCoolerUnit.nameString() + " First Evaporative Cooler - Second Evaporative Cooler Node", + idf_secondEvaporativeCooler.getString(EvaporativeCooler_Indirect_ResearchSpecialFields::PrimaryAirInletNodeName).get()); + EXPECT_EQ(zoneHVACEvaporativeCoolerUnit.airOutletModelObject()->nameString(), + idf_secondEvaporativeCooler.getString(EvaporativeCooler_Indirect_ResearchSpecialFields::PrimaryAirOutletNodeName).get()); +} + +std::vector getEvaporativeCoolerUnitNodes(const Workspace& workspace) { + WorkspaceObjectVector idfEvaporativeCoolerUnits(workspace.getObjectsByType(IddObjectType::ZoneHVAC_EvaporativeCoolerUnit)); + if (idfEvaporativeCoolerUnits.empty()) { + return {}; + } + + auto& idfEvaporativeCoolerUnit = idfEvaporativeCoolerUnits[0]; + + return { + idfEvaporativeCoolerUnit.getString(ZoneHVAC_EvaporativeCoolerUnitFields::OutdoorAirInletNodeName).get(), + idfEvaporativeCoolerUnit.getString(ZoneHVAC_EvaporativeCoolerUnitFields::CoolerOutletNodeName).get(), + }; +} + +std::vector getSupplyAirFanNodes(const Workspace& workspace) { + WorkspaceObjectVector idfFans(workspace.getObjectsByType(IddObjectType::Fan_SystemModel)); + if (idfFans.empty()) { + return {}; + } + + auto& idfFan = idfFans[0]; + + return { + idfFan.getString(Fan_SystemModelFields::AirInletNodeName).get(), + idfFan.getString(Fan_SystemModelFields::AirOutletNodeName).get(), + }; +} + +std::vector getFirstEvaporativeCoolerNodes(const Workspace& workspace) { + WorkspaceObjectVector idfFirstEvaporativeCoolers(workspace.getObjectsByType(IddObjectType::EvaporativeCooler_Direct_ResearchSpecial)); + if (idfFirstEvaporativeCoolers.empty()) { + return {}; + } + + auto& idfFirstEvaporativeCooler = idfFirstEvaporativeCoolers[0]; + + return { + idfFirstEvaporativeCooler.getString(EvaporativeCooler_Direct_ResearchSpecialFields::AirInletNodeName).get(), + idfFirstEvaporativeCooler.getString(EvaporativeCooler_Direct_ResearchSpecialFields::AirOutletNodeName).get(), + }; +} + +std::vector getSecondEvaporativeCoolerNodes(const Workspace& workspace) { + WorkspaceObjectVector idfSecondEvaporativeCoolers(workspace.getObjectsByType(IddObjectType::EvaporativeCooler_Indirect_ResearchSpecial)); + if (idfSecondEvaporativeCoolers.empty()) { + return {}; + } + + auto& idfSecondEvaporativeCooler = idfSecondEvaporativeCoolers[0]; + + return { + idfSecondEvaporativeCooler.getString(EvaporativeCooler_Indirect_ResearchSpecialFields::PrimaryAirInletNodeName).get(), + idfSecondEvaporativeCooler.getString(EvaporativeCooler_Indirect_ResearchSpecialFields::PrimaryAirOutletNodeName).get(), + }; +} + +TEST_F(EnergyPlusFixture, ForwardTranslator_ZoneHVACEvaporativeCoolerUnit_Nodes) { + for (std::string fanPlacement : {"BlowThrough", "DrawThrough"}) { + + // first evaporative cooler + { + Model m; + + ZoneHVACEvaporativeCoolerUnit zoneHVACEvaporativeCoolerUnit(m); + zoneHVACEvaporativeCoolerUnit.setFanPlacement(fanPlacement); + + ThermalZone z(m); + zoneHVACEvaporativeCoolerUnit.addToThermalZone(z); + Space s(m); + s.setThermalZone(z); + + ForwardTranslator ft; + Workspace workspace = ft.translateModel(m); + + std::vector evaporativeCoolerUnitNodes = getEvaporativeCoolerUnitNodes(workspace); + std::vector fanNodes = getSupplyAirFanNodes(workspace); + std::vector firstEvaporativeCoolerNodes = getFirstEvaporativeCoolerNodes(workspace); + + if (fanPlacement == "BlowThrough") { + EXPECT_EQ(evaporativeCoolerUnitNodes[0], fanNodes[0]); + EXPECT_EQ(fanNodes[1], firstEvaporativeCoolerNodes[0]); + EXPECT_EQ(firstEvaporativeCoolerNodes[1], evaporativeCoolerUnitNodes[1]); + } else if (fanPlacement == "DrawThrough") { + EXPECT_EQ(evaporativeCoolerUnitNodes[0], firstEvaporativeCoolerNodes[0]); + EXPECT_EQ(firstEvaporativeCoolerNodes[1], fanNodes[0]); + EXPECT_EQ(fanNodes[1], evaporativeCoolerUnitNodes[1]); + } + } + + // first evaporative cooler, second evaporative cooler + { + Model m; + + ZoneHVACEvaporativeCoolerUnit zoneHVACEvaporativeCoolerUnit(m); + zoneHVACEvaporativeCoolerUnit.setFanPlacement(fanPlacement); + EvaporativeCoolerIndirectResearchSpecial secondEvaporativeCooler(m); + EXPECT_TRUE(zoneHVACEvaporativeCoolerUnit.setSecondEvaporativeCooler(secondEvaporativeCooler)); + + ThermalZone z(m); + zoneHVACEvaporativeCoolerUnit.addToThermalZone(z); + Space s(m); + s.setThermalZone(z); + + ForwardTranslator ft; + Workspace workspace = ft.translateModel(m); + + std::vector evaporativeCoolerUnitNodes = getEvaporativeCoolerUnitNodes(workspace); + std::vector fanNodes = getSupplyAirFanNodes(workspace); + std::vector firstEvaporativeCoolerNodes = getFirstEvaporativeCoolerNodes(workspace); + std::vector secondEvaporativeCoolerNodes = getSecondEvaporativeCoolerNodes(workspace); + + if (fanPlacement == "BlowThrough") { + EXPECT_EQ(evaporativeCoolerUnitNodes[0], fanNodes[0]); + EXPECT_EQ(fanNodes[1], firstEvaporativeCoolerNodes[0]); + EXPECT_EQ(firstEvaporativeCoolerNodes[1], secondEvaporativeCoolerNodes[0]); + EXPECT_EQ(secondEvaporativeCoolerNodes[1], evaporativeCoolerUnitNodes[1]); + } else if (fanPlacement == "DrawThrough") { + EXPECT_EQ(evaporativeCoolerUnitNodes[0], firstEvaporativeCoolerNodes[0]); + EXPECT_EQ(firstEvaporativeCoolerNodes[1], secondEvaporativeCoolerNodes[0]); + EXPECT_EQ(secondEvaporativeCoolerNodes[1], fanNodes[0]); + EXPECT_EQ(fanNodes[1], evaporativeCoolerUnitNodes[1]); + } + } + } +} diff --git a/src/model/CMakeLists.txt b/src/model/CMakeLists.txt index 8477362fea..548f9e3816 100644 --- a/src/model/CMakeLists.txt +++ b/src/model/CMakeLists.txt @@ -1774,6 +1774,9 @@ set(${target_name}_src ZoneHVACEquipmentList.hpp ZoneHVACEquipmentList_Impl.hpp ZoneHVACEquipmentList.cpp + ZoneHVACEvaporativeCoolerUnit.hpp + ZoneHVACEvaporativeCoolerUnit_Impl.hpp + ZoneHVACEvaporativeCoolerUnit.cpp ZoneHVACFourPipeFanCoil.hpp ZoneHVACFourPipeFanCoil_Impl.hpp ZoneHVACFourPipeFanCoil.cpp @@ -2365,6 +2368,7 @@ set(${target_name}_test_src test/ZoneHVACEnergyRecoveryVentilator_GTest.cpp test/ZoneHVACEnergyRecoveryVentilatorController_GTest.cpp test/ZoneHVACEquipmentList_GTest.cpp + test/ZoneHVACEvaporativeCoolerUnit_GTest.cpp test/ZoneHVACFourPipeFanCoil_GTest.cpp test/ZoneHVACHighTemperatureRadiant_GTest.cpp test/ZoneHVACLowTemperatureRadiantElectric_GTest.cpp diff --git a/src/model/ConcreteModelObjects.hpp b/src/model/ConcreteModelObjects.hpp index 9d36231578..3ea8d92cf8 100644 --- a/src/model/ConcreteModelObjects.hpp +++ b/src/model/ConcreteModelObjects.hpp @@ -542,6 +542,7 @@ #include "ZoneHVACEnergyRecoveryVentilator.hpp" #include "ZoneHVACEnergyRecoveryVentilatorController.hpp" #include "ZoneHVACEquipmentList.hpp" +#include "ZoneHVACEvaporativeCoolerUnit.hpp" #include "ZoneHVACFourPipeFanCoil.hpp" #include "ZoneHVACHighTemperatureRadiant.hpp" #include "ZoneHVACIdealLoadsAirSystem.hpp" @@ -1096,6 +1097,7 @@ #include "ZoneHVACEnergyRecoveryVentilator_Impl.hpp" #include "ZoneHVACEnergyRecoveryVentilatorController_Impl.hpp" #include "ZoneHVACEquipmentList_Impl.hpp" +#include "ZoneHVACEvaporativeCoolerUnit_Impl.hpp" #include "ZoneHVACFourPipeFanCoil_Impl.hpp" #include "ZoneHVACHighTemperatureRadiant_Impl.hpp" #include "ZoneHVACIdealLoadsAirSystem_Impl.hpp" diff --git a/src/model/EvaporativeCoolerDirectResearchSpecial.cpp b/src/model/EvaporativeCoolerDirectResearchSpecial.cpp index a8c8fa14e0..bc52fbb885 100644 --- a/src/model/EvaporativeCoolerDirectResearchSpecial.cpp +++ b/src/model/EvaporativeCoolerDirectResearchSpecial.cpp @@ -15,6 +15,10 @@ #include "Curve_Impl.hpp" #include "Model.hpp" +// containing ZoneHVAC Component +#include "ZoneHVACEvaporativeCoolerUnit.hpp" +#include "ZoneHVACEvaporativeCoolerUnit_Impl.hpp" + #include "../utilities/core/Assert.hpp" #include "../utilities/core/Compare.hpp" #include "../utilities/data/DataEnums.hpp" @@ -126,6 +130,28 @@ namespace model { return value.get(); } + boost::optional EvaporativeCoolerDirectResearchSpecial_Impl::containingZoneHVACComponent() const { + + std::vector zoneHVACComponent = this->model().getModelObjects(); + for (const auto& elem : zoneHVACComponent) { + switch (elem.iddObject().type().value()) { + case openstudio::IddObjectType::OS_ZoneHVAC_EvaporativeCoolerUnit: { + auto component = elem.cast(); + if (component.firstEvaporativeCooler().handle() == this->handle()) { + return elem; + } + // I guess it's fine since this is optional anyways + // } else if (auto comp_ = component.secondEvaporativeCooler(); comp_ && comp_->handle() == this->handle()) { return elem; } + break; + } + default: { + break; + } + } + } + return boost::none; + } + bool EvaporativeCoolerDirectResearchSpecial_Impl::setAvailabilitySchedule(Schedule& schedule) { bool result = setSchedule(OS_EvaporativeCooler_Direct_ResearchSpecialFields::AvailabilityScheduleName, "EvaporativeCoolerDirectResearchSpecial", "Availability", schedule); diff --git a/src/model/EvaporativeCoolerDirectResearchSpecial_Impl.hpp b/src/model/EvaporativeCoolerDirectResearchSpecial_Impl.hpp index 922fc35265..3757eb5ce3 100644 --- a/src/model/EvaporativeCoolerDirectResearchSpecial_Impl.hpp +++ b/src/model/EvaporativeCoolerDirectResearchSpecial_Impl.hpp @@ -131,6 +131,8 @@ namespace model { boost::optional availabilityScheduleAsModelObject() const; bool setAvailabilityScheduleAsModelObject(const boost::optional& modelObject); + + virtual boost::optional containingZoneHVACComponent() const override; }; } // namespace detail diff --git a/src/model/EvaporativeCoolerIndirectResearchSpecial.cpp b/src/model/EvaporativeCoolerIndirectResearchSpecial.cpp index 0cfa6a6994..a135d61765 100644 --- a/src/model/EvaporativeCoolerIndirectResearchSpecial.cpp +++ b/src/model/EvaporativeCoolerIndirectResearchSpecial.cpp @@ -9,6 +9,7 @@ #include "EvaporativeCoolerIndirectResearchSpecial_Impl.hpp" #include "Schedule.hpp" #include "Schedule_Impl.hpp" +#include "Model.hpp" #include "Node.hpp" #include "Node_Impl.hpp" #include "Curve.hpp" @@ -16,6 +17,10 @@ #include "ScheduleTypeLimits.hpp" #include "ScheduleTypeRegistry.hpp" +// containing ZoneHVAC Component +#include "ZoneHVACEvaporativeCoolerUnit.hpp" +#include "ZoneHVACEvaporativeCoolerUnit_Impl.hpp" + #include "../utilities/core/Assert.hpp" #include "../utilities/data/DataEnums.hpp" @@ -261,6 +266,28 @@ namespace model { return false; } + boost::optional EvaporativeCoolerIndirectResearchSpecial_Impl::containingZoneHVACComponent() const { + + std::vector zoneHVACComponent = this->model().getModelObjects(); + for (const auto& elem : zoneHVACComponent) { + switch (elem.iddObject().type().value()) { + case openstudio::IddObjectType::OS_ZoneHVAC_EvaporativeCoolerUnit: { + auto component = elem.cast(); + if (component.firstEvaporativeCooler().handle() == this->handle()) { + return elem; + } + // I guess it's fine since this is optional anyways + // } else if (auto comp_ = component.secondEvaporativeCooler(); comp_ && comp_->handle() == this->handle()) { return elem; } + break; + } + default: { + break; + } + } + } + return boost::none; + } + bool EvaporativeCoolerIndirectResearchSpecial_Impl::setReliefAirInletNode(const Node& node) { return setPointer(OS_EvaporativeCooler_Indirect_ResearchSpecialFields::ReliefAirInletNode, node.handle()); } diff --git a/src/model/EvaporativeCoolerIndirectResearchSpecial_Impl.hpp b/src/model/EvaporativeCoolerIndirectResearchSpecial_Impl.hpp index 5b01c4acc3..6db2d3e268 100644 --- a/src/model/EvaporativeCoolerIndirectResearchSpecial_Impl.hpp +++ b/src/model/EvaporativeCoolerIndirectResearchSpecial_Impl.hpp @@ -195,6 +195,8 @@ namespace model { protected: private: REGISTER_LOGGER("openstudio.model.EvaporativeCoolerIndirectResearchSpecial"); + + virtual boost::optional containingZoneHVACComponent() const override; }; } // namespace detail diff --git a/src/model/FanComponentModel.cpp b/src/model/FanComponentModel.cpp index b949cc3863..c0129635a1 100644 --- a/src/model/FanComponentModel.cpp +++ b/src/model/FanComponentModel.cpp @@ -39,8 +39,8 @@ #include "AirTerminalSingleDuctSeriesPIUReheat_Impl.hpp" // containing ZoneHVAC Component -// #include "ZoneHVACEvaporativeCoolerUnit.hpp" -// #include "ZoneHVACEvaporativeCoolerUnit_Impl.hpp" +#include "ZoneHVACEvaporativeCoolerUnit.hpp" +#include "ZoneHVACEvaporativeCoolerUnit_Impl.hpp" #include #include @@ -386,23 +386,21 @@ namespace model { boost::optional FanComponentModel_Impl::containingZoneHVACComponent() const { - // Note JM 2021-01-26: Only ZoneHVAC:EvaporativeCoolerUnit apparently, which isn't wrapped in the OS SDK currently - - //std::vector zoneHVACComponent = this->model().getModelObjects(); - //for (const auto& elem : zoneHVACComponent) { - //switch (elem.iddObject().type().value()) { - - //// ZoneHVAC:EvaporativeCoolerUnit: not wrapped - //case openstudio::IddObjectType::OS_ZoneHVAC_EvaporativeCoolerUnit: { - //ZoneHVACEnergyRecoveryVentilator component = elem.cast(); - //if (component.supplyAirFan().handle() == this->handle()) return elem; - //break; - //} - //default: { - //break; - //} - //} - //} + std::vector zoneHVACComponent = this->model().getModelObjects(); + for (const auto& elem : zoneHVACComponent) { + switch (elem.iddObject().type().value()) { + case openstudio::IddObjectType::OS_ZoneHVAC_EvaporativeCoolerUnit: { + auto component = elem.cast(); + if (component.supplyAirFan().handle() == this->handle()) { + return elem; + } + break; + } + default: { + break; + } + } + } return boost::none; } diff --git a/src/model/FanConstantVolume.cpp b/src/model/FanConstantVolume.cpp index 9471406bea..045d7f4c3d 100644 --- a/src/model/FanConstantVolume.cpp +++ b/src/model/FanConstantVolume.cpp @@ -19,6 +19,8 @@ #include "ZoneHVACUnitHeater_Impl.hpp" #include "ZoneHVACUnitVentilator.hpp" #include "ZoneHVACUnitVentilator_Impl.hpp" +#include "ZoneHVACEvaporativeCoolerUnit.hpp" +#include "ZoneHVACEvaporativeCoolerUnit_Impl.hpp" #include "AirLoopHVACOutdoorAirSystem.hpp" #include "AirLoopHVACOutdoorAirSystem_Impl.hpp" #include "AirTerminalSingleDuctParallelPIUReheat.hpp" @@ -344,6 +346,19 @@ namespace model { } } + // ZoneHVACEvaporativeCoolerUnit + std::vector zoneHVACEvaporativeCoolerUnit; + + zoneHVACEvaporativeCoolerUnit = this->model().getConcreteModelObjects(); + + for (const auto& elem : zoneHVACEvaporativeCoolerUnit) { + if (boost::optional fan = elem.supplyAirFan()) { + if (fan->handle() == this->handle()) { + return elem; + } + } + } + return boost::none; } diff --git a/src/model/FanOnOff.cpp b/src/model/FanOnOff.cpp index 326bc8c5df..9c092fba30 100644 --- a/src/model/FanOnOff.cpp +++ b/src/model/FanOnOff.cpp @@ -37,6 +37,8 @@ #include "ZoneHVACUnitHeater_Impl.hpp" #include "ZoneHVACUnitVentilator.hpp" #include "ZoneHVACUnitVentilator_Impl.hpp" +#include "ZoneHVACEvaporativeCoolerUnit.hpp" +#include "ZoneHVACEvaporativeCoolerUnit_Impl.hpp" #include "AirLoopHVACUnitaryHeatPumpAirToAir.hpp" #include "AirLoopHVACUnitaryHeatPumpAirToAir_Impl.hpp" #include "AirLoopHVACUnitarySystem.hpp" @@ -424,6 +426,13 @@ namespace model { } break; } + case openstudio::IddObjectType::OS_ZoneHVAC_EvaporativeCoolerUnit: { + auto component = elem.cast(); + if (component.supplyAirFan().handle() == this->handle()) { + return elem; + } + break; + } default: { break; } diff --git a/src/model/FanSystemModel.cpp b/src/model/FanSystemModel.cpp index 6076457f83..0633fee2f2 100644 --- a/src/model/FanSystemModel.cpp +++ b/src/model/FanSystemModel.cpp @@ -47,6 +47,8 @@ #include "ZoneHVACUnitHeater_Impl.hpp" #include "ZoneHVACUnitVentilator.hpp" #include "ZoneHVACUnitVentilator_Impl.hpp" +#include "ZoneHVACEvaporativeCoolerUnit.hpp" +#include "ZoneHVACEvaporativeCoolerUnit_Impl.hpp" #include "ZoneHVACWaterToAirHeatPump.hpp" #include "ZoneHVACWaterToAirHeatPump_Impl.hpp" // These are supposed to be ZoneHVACComponents @@ -484,6 +486,13 @@ namespace model { } break; } + case openstudio::IddObjectType::OS_ZoneHVAC_EvaporativeCoolerUnit: { + auto component = elem.cast(); + if (component.supplyAirFan().handle() == this->handle()) { + return elem; + } + break; + } case openstudio::IddObjectType::OS_ZoneHVAC_WaterToAirHeatPump: { auto component = elem.cast(); if (component.supplyAirFan().handle() == this->handle()) { diff --git a/src/model/FanVariableVolume.cpp b/src/model/FanVariableVolume.cpp index 993893f753..185790c78c 100644 --- a/src/model/FanVariableVolume.cpp +++ b/src/model/FanVariableVolume.cpp @@ -20,6 +20,8 @@ #include "ZoneHVACUnitHeater_Impl.hpp" #include "ZoneHVACUnitVentilator.hpp" #include "ZoneHVACUnitVentilator_Impl.hpp" +#include "ZoneHVACEvaporativeCoolerUnit.hpp" +#include "ZoneHVACEvaporativeCoolerUnit_Impl.hpp" #include "AirLoopHVACUnitarySystem.hpp" #include "AirLoopHVACUnitarySystem_Impl.hpp" #include "SetpointManagerMixedAir.hpp" @@ -492,6 +494,19 @@ namespace model { } } + // ZoneHVACEvaporativeCoolerUnit + std::vector zoneHVACEvaporativeCoolerUnit; + + zoneHVACEvaporativeCoolerUnit = this->model().getConcreteModelObjects(); + + for (const auto& elem : zoneHVACEvaporativeCoolerUnit) { + if (boost::optional fan = elem.supplyAirFan()) { + if (fan->handle() == this->handle()) { + return elem; + } + } + } + return boost::none; } diff --git a/src/model/Model.cpp b/src/model/Model.cpp index 75d57bf3ea..38a56fb71d 100644 --- a/src/model/Model.cpp +++ b/src/model/Model.cpp @@ -2254,9 +2254,9 @@ namespace model { if (!openstudio::equal(inputResult, outputResult, tol)) { LOG_FREE(logLevel, "openstudio.model.Model", "The " << attributeName << " values determined for " << object.briefDescription() - << " using input and output data differ by a (relative) error " << "greater than " << tol - << ". The value calculated from input data was " << inputResult << ", whereas the value calculated from output data was " - << outputResult << "."); + << " using input and output data differ by a (relative) error " + << "greater than " << tol << ". The value calculated from input data was " << inputResult + << ", whereas the value calculated from output data was " << outputResult << "."); return false; } return true; @@ -4410,6 +4410,7 @@ namespace model { REGISTER_CONSTRUCTOR(ZoneControlHumidistat); REGISTER_CONSTRUCTOR(ZoneControlThermostatStagedDualSetpoint); REGISTER_CONSTRUCTOR(ZoneHVACEquipmentList); + REGISTER_CONSTRUCTOR(ZoneHVACEvaporativeCoolerUnit); REGISTER_CONSTRUCTOR(ZoneHVACBaseboardConvectiveElectric); REGISTER_CONSTRUCTOR(ZoneHVACBaseboardConvectiveWater); REGISTER_CONSTRUCTOR(ZoneHVACBaseboardRadiantConvectiveElectric); @@ -4984,6 +4985,7 @@ namespace model { REGISTER_COPYCONSTRUCTORS(ZoneControlHumidistat); REGISTER_COPYCONSTRUCTORS(ZoneControlThermostatStagedDualSetpoint); REGISTER_COPYCONSTRUCTORS(ZoneHVACEquipmentList); + REGISTER_COPYCONSTRUCTORS(ZoneHVACEvaporativeCoolerUnit); REGISTER_COPYCONSTRUCTORS(ZoneHVACBaseboardConvectiveElectric); REGISTER_COPYCONSTRUCTORS(ZoneHVACBaseboardConvectiveWater); REGISTER_COPYCONSTRUCTORS(ZoneHVACBaseboardRadiantConvectiveElectric); diff --git a/src/model/ModelZoneHVAC.i b/src/model/ModelZoneHVAC.i index aa727fd6b0..2c9d85ae1f 100644 --- a/src/model/ModelZoneHVAC.i +++ b/src/model/ModelZoneHVAC.i @@ -54,6 +54,7 @@ MODELOBJECT_TEMPLATES(ZoneHVACCoolingPanelRadiantConvectiveWater); MODELOBJECT_TEMPLATES(ZoneHVACDehumidifierDX); MODELOBJECT_TEMPLATES(ZoneHVACEnergyRecoveryVentilator); MODELOBJECT_TEMPLATES(ZoneHVACEnergyRecoveryVentilatorController); +MODELOBJECT_TEMPLATES(ZoneHVACEvaporativeCoolerUnit); MODELOBJECT_TEMPLATES(ZoneHVACFourPipeFanCoil); MODELOBJECT_TEMPLATES(ZoneHVACHighTemperatureRadiant); MODELOBJECT_TEMPLATES(ZoneHVACIdealLoadsAirSystem); @@ -79,6 +80,7 @@ SWIG_MODELOBJECT(ZoneHVACCoolingPanelRadiantConvectiveWater,1); SWIG_MODELOBJECT(ZoneHVACDehumidifierDX,1); SWIG_MODELOBJECT(ZoneHVACEnergyRecoveryVentilator,1); SWIG_MODELOBJECT(ZoneHVACEnergyRecoveryVentilatorController,1); +SWIG_MODELOBJECT(ZoneHVACEvaporativeCoolerUnit,1); SWIG_MODELOBJECT(ZoneHVACFourPipeFanCoil,1); SWIG_MODELOBJECT(ZoneHVACHighTemperatureRadiant,1); SWIG_MODELOBJECT(ZoneHVACIdealLoadsAirSystem,1); diff --git a/src/model/ScheduleTypeRegistry.cpp b/src/model/ScheduleTypeRegistry.cpp index a24cbe7b57..e63e6ddf9f 100644 --- a/src/model/ScheduleTypeRegistry.cpp +++ b/src/model/ScheduleTypeRegistry.cpp @@ -558,6 +558,7 @@ namespace model { {"ZoneHVACUnitVentilator", "Maximum Outdoor Air Fraction or Temperature", "maximumOutdoorAirFractionorTemperatureSchedule", true, "", OptionalDouble(), OptionalDouble()}, {"ZoneHVACUnitVentilator", "Supply Air Fan Operating Mode", "supplyAirFanOperatingModeSchedule", false, "ControlMode", 0.0, 1.0}, + {"ZoneHVACEvaporativeCoolerUnit", "Availability", "availabilitySchedule", false, "Availability", 0.0, 1.0}, {"ZoneMixing", "Zone Mixing", "schedule", true, "Dimensionless", 0.0, 1.0}, {"ZoneMixing", "Delta Temperature", "deltaTemperatureSchedule", true, "DeltaTemperature", OptionalDouble(), OptionalDouble()}, {"ZoneMixing", "Minimum Receiving Temperature", "minimumReceivingTemperatureSchedule", true, "Temperature", OptionalDouble(), OptionalDouble()}, diff --git a/src/model/ZoneHVACEvaporativeCoolerUnit.cpp b/src/model/ZoneHVACEvaporativeCoolerUnit.cpp new file mode 100644 index 0000000000..37b35e6506 --- /dev/null +++ b/src/model/ZoneHVACEvaporativeCoolerUnit.cpp @@ -0,0 +1,518 @@ +/*********************************************************************************************************************** +* OpenStudio(R), Copyright (c) 2008-2023, Alliance for Sustainable Energy, LLC, and other contributors. All rights reserved. +* +* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +* following conditions are met: +* +* (1) Redistributions of source code must retain the above copyright notice, this list of conditions and the following +* disclaimer. +* +* (2) Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following +* disclaimer in the documentation and/or other materials provided with the distribution. +* +* (3) Neither the name of the copyright holder nor the names of any contributors may be used to endorse or promote products +* derived from this software without specific prior written permission from the respective party. +* +* (4) Other than as required in clauses (1) and (2), distributions in any form of modifications or other derivative works +* may not use the "OpenStudio" trademark, "OS", "os", or any other confusingly similar designation without specific prior +* written permission from Alliance for Sustainable Energy, LLC. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND ANY CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S), ANY CONTRIBUTORS, THE UNITED STATES GOVERNMENT, OR THE UNITED +* STATES DEPARTMENT OF ENERGY, NOR ANY OF THEIR EMPLOYEES, BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF +* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***********************************************************************************************************************/ + +#include "ZoneHVACEvaporativeCoolerUnit.hpp" +#include "ZoneHVACEvaporativeCoolerUnit_Impl.hpp" + +#include "Model.hpp" +#include "Model_Impl.hpp" +#include "Schedule.hpp" +#include "Schedule_Impl.hpp" +#include "HVACComponent.hpp" +#include "HVACComponent_Impl.hpp" +#include "ScheduleTypeLimits.hpp" +#include "ScheduleTypeRegistry.hpp" +#include "FanSystemModel.hpp" +#include "FanSystemModel_Impl.hpp" +#include "EvaporativeCoolerDirectResearchSpecial.hpp" +#include "EvaporativeCoolerDirectResearchSpecial_Impl.hpp" + +#include "../utilities/core/Assert.hpp" +#include "../utilities/data/DataEnums.hpp" + +#include +#include +#include + +namespace openstudio { +namespace model { + + namespace detail { + + ZoneHVACEvaporativeCoolerUnit_Impl::ZoneHVACEvaporativeCoolerUnit_Impl(const IdfObject& idfObject, Model_Impl* model, bool keepHandle) + : ZoneHVACComponent_Impl(idfObject, model, keepHandle) { + OS_ASSERT(idfObject.iddObject().type() == ZoneHVACEvaporativeCoolerUnit::iddObjectType()); + } + + ZoneHVACEvaporativeCoolerUnit_Impl::ZoneHVACEvaporativeCoolerUnit_Impl(const openstudio::detail::WorkspaceObject_Impl& other, Model_Impl* model, + bool keepHandle) + : ZoneHVACComponent_Impl(other, model, keepHandle) { + OS_ASSERT(other.iddObject().type() == ZoneHVACEvaporativeCoolerUnit::iddObjectType()); + } + + ZoneHVACEvaporativeCoolerUnit_Impl::ZoneHVACEvaporativeCoolerUnit_Impl(const ZoneHVACEvaporativeCoolerUnit_Impl& other, Model_Impl* model, + bool keepHandle) + : ZoneHVACComponent_Impl(other, model, keepHandle) {} + + ModelObject ZoneHVACEvaporativeCoolerUnit_Impl::clone(Model model) const { + auto evaporativeCoolUnitClone = ZoneHVACComponent_Impl::clone(model).cast(); + + if (OptionalHVACComponent intermediate = optionalSupplyAirFan()) { + evaporativeCoolUnitClone.setSupplyAirFan(intermediate->clone(model).cast()); + } + if (OptionalHVACComponent intermediate = optionalFirstEvaporativeCooler()) { + evaporativeCoolUnitClone.setFirstEvaporativeCooler(intermediate->clone(model).cast()); + } + if (OptionalHVACComponent intermediate = secondEvaporativeCooler()) { + evaporativeCoolUnitClone.setSecondEvaporativeCooler(intermediate->clone(model).cast()); + } + + return std::move(evaporativeCoolUnitClone); + } + + const std::vector& ZoneHVACEvaporativeCoolerUnit_Impl::outputVariableNames() const { + static std::vector result; + if (result.empty()) { + } + return result; + } + + IddObjectType ZoneHVACEvaporativeCoolerUnit_Impl::iddObjectType() const { + return ZoneHVACEvaporativeCoolerUnit::iddObjectType(); + } + + std::vector ZoneHVACEvaporativeCoolerUnit_Impl::getScheduleTypeKeys(const Schedule& schedule) const { + std::vector result; + const UnsignedVector fieldIndices = getSourceIndices(schedule.handle()); + if (std::find(fieldIndices.cbegin(), fieldIndices.cend(), OS_ZoneHVAC_EvaporativeCoolerUnitFields::AvailabilityScheduleName) + != fieldIndices.cend()) { + result.emplace_back("ZoneHVACEvaporativeCoolerUnit", "Availability"); + } + return result; + } + + std::vector ZoneHVACEvaporativeCoolerUnit_Impl::children() const { + std::vector result; + if (OptionalHVACComponent intermediate = optionalSupplyAirFan()) { + result.push_back(*intermediate); + } + if (OptionalHVACComponent intermediate = optionalFirstEvaporativeCooler()) { + result.push_back(*intermediate); + } + if (OptionalHVACComponent intermediate = secondEvaporativeCooler()) { + result.push_back(*intermediate); + } + return result; + } + + unsigned ZoneHVACEvaporativeCoolerUnit_Impl::inletPort() const { + return OS_ZoneHVAC_EvaporativeCoolerUnitFields::ZoneReliefAirNodeName; + } + + unsigned ZoneHVACEvaporativeCoolerUnit_Impl::outletPort() const { + return OS_ZoneHVAC_EvaporativeCoolerUnitFields::CoolerOutletNodeName; + } + + ComponentType ZoneHVACEvaporativeCoolerUnit_Impl::componentType() const { + return ComponentType::Cooling; + } + + std::vector ZoneHVACEvaporativeCoolerUnit_Impl::coolingFuelTypes() const { + std::set result; + for (auto ft : firstEvaporativeCooler().coolingFuelTypes()) { + result.insert(ft); + } + if (auto secondEvaporativeCooler_ = secondEvaporativeCooler()) { + for (auto ft : secondEvaporativeCooler_->coolingFuelTypes()) { + result.insert(ft); + } + } + return {result.begin(), result.end()}; + } + + std::vector ZoneHVACEvaporativeCoolerUnit_Impl::heatingFuelTypes() const { + std::set result; + for (auto ft : firstEvaporativeCooler().heatingFuelTypes()) { + result.insert(ft); + } + if (auto secondEvaporativeCooler_ = secondEvaporativeCooler()) { + for (auto ft : secondEvaporativeCooler_->heatingFuelTypes()) { + result.insert(ft); + } + } + return {result.begin(), result.end()}; + } + + std::vector ZoneHVACEvaporativeCoolerUnit_Impl::appGHeatingFuelTypes() const { + std::set result; + for (auto ft : firstEvaporativeCooler().appGHeatingFuelTypes()) { + result.insert(ft); + } + if (auto secondEvaporativeCooler_ = secondEvaporativeCooler()) { + for (auto ft : secondEvaporativeCooler_->appGHeatingFuelTypes()) { + result.insert(ft); + } + } + return {result.begin(), result.end()}; + } + + Schedule ZoneHVACEvaporativeCoolerUnit_Impl::availabilitySchedule() const { + boost::optional value = optionalAvailabilitySchedule(); + if (!value) { + LOG_AND_THROW(briefDescription() << " does not have an Availability Schedule attached."); + } + return value.get(); + } + + HVACComponent ZoneHVACEvaporativeCoolerUnit_Impl::supplyAirFan() const { + boost::optional value = optionalSupplyAirFan(); + if (!value) { + LOG_AND_THROW(briefDescription() << " does not have an Supply Air Fan attached."); + } + return value.get(); + } + + boost::optional ZoneHVACEvaporativeCoolerUnit_Impl::designSupplyAirFlowRate() const { + return getDouble(OS_ZoneHVAC_EvaporativeCoolerUnitFields::DesignSupplyAirFlowRate, true); + } + + bool ZoneHVACEvaporativeCoolerUnit_Impl::isDesignSupplyAirFlowRateAutosized() const { + bool result = false; + boost::optional value = getString(OS_ZoneHVAC_EvaporativeCoolerUnitFields::DesignSupplyAirFlowRate, true); + if (value) { + result = openstudio::istringEqual(value.get(), "autosize"); + } + return result; + } + + boost::optional ZoneHVACEvaporativeCoolerUnit_Impl::autosizedDesignSupplyAirFlowRate() { + return getAutosizedValue("Design Size Design Supply Air Flow Rate", "m3/s"); + } + + std::string ZoneHVACEvaporativeCoolerUnit_Impl::fanPlacement() const { + boost::optional value = getString(OS_ZoneHVAC_EvaporativeCoolerUnitFields::FanPlacement, true); + OS_ASSERT(value); + return value.get(); + } + + std::string ZoneHVACEvaporativeCoolerUnit_Impl::coolerUnitControlMethod() const { + boost::optional value = getString(OS_ZoneHVAC_EvaporativeCoolerUnitFields::CoolerUnitControlMethod, true); + OS_ASSERT(value); + return value.get(); + } + + double ZoneHVACEvaporativeCoolerUnit_Impl::throttlingRangeTemperatureDifference() const { + boost::optional value = getDouble(OS_ZoneHVAC_EvaporativeCoolerUnitFields::ThrottlingRangeTemperatureDifference, true); + OS_ASSERT(value); + return value.get(); + } + + double ZoneHVACEvaporativeCoolerUnit_Impl::coolingLoadControlThresholdHeatTransferRate() const { + boost::optional value = getDouble(OS_ZoneHVAC_EvaporativeCoolerUnitFields::CoolingLoadControlThresholdHeatTransferRate, true); + OS_ASSERT(value); + return value.get(); + } + + HVACComponent ZoneHVACEvaporativeCoolerUnit_Impl::firstEvaporativeCooler() const { + boost::optional value = optionalFirstEvaporativeCooler(); + if (!value) { + LOG_AND_THROW(briefDescription() << " does not have an First Evaporative Cooler attached."); + } + return value.get(); + } + + boost::optional ZoneHVACEvaporativeCoolerUnit_Impl::secondEvaporativeCooler() const { + return getObject().getModelObjectTarget(OS_ZoneHVAC_EvaporativeCoolerUnitFields::SecondEvaporativeCooler); + } + + double ZoneHVACEvaporativeCoolerUnit_Impl::shutOffRelativeHumidity() const { + boost::optional value = getDouble(OS_ZoneHVAC_EvaporativeCoolerUnitFields::ShutOffRelativeHumidity, true); + OS_ASSERT(value); + return value.get(); + } + + bool ZoneHVACEvaporativeCoolerUnit_Impl::setAvailabilitySchedule(Schedule& schedule) { + const bool result = + setSchedule(OS_ZoneHVAC_EvaporativeCoolerUnitFields::AvailabilityScheduleName, "ZoneHVACEvaporativeCoolerUnit", "Availability", schedule); + return result; + } + + bool ZoneHVACEvaporativeCoolerUnit_Impl::setSupplyAirFan(const HVACComponent& hvacComponent) { + const bool result = setPointer(OS_ZoneHVAC_EvaporativeCoolerUnitFields::SupplyAirFanName, hvacComponent.handle()); + return result; + } + + bool ZoneHVACEvaporativeCoolerUnit_Impl::setDesignSupplyAirFlowRate(double designSupplyAirFlowRate) { + const bool result = setDouble(OS_ZoneHVAC_EvaporativeCoolerUnitFields::DesignSupplyAirFlowRate, designSupplyAirFlowRate); + return result; + } + + void ZoneHVACEvaporativeCoolerUnit_Impl::autosizeDesignSupplyAirFlowRate() { + const bool result = setString(OS_ZoneHVAC_EvaporativeCoolerUnitFields::DesignSupplyAirFlowRate, "autosize"); + OS_ASSERT(result); + } + + bool ZoneHVACEvaporativeCoolerUnit_Impl::setFanPlacement(const std::string& fanPlacement) { + const bool result = setString(OS_ZoneHVAC_EvaporativeCoolerUnitFields::FanPlacement, fanPlacement); + return result; + } + + bool ZoneHVACEvaporativeCoolerUnit_Impl::setCoolerUnitControlMethod(const std::string& coolerUnitControlMethod) { + const bool result = setString(OS_ZoneHVAC_EvaporativeCoolerUnitFields::CoolerUnitControlMethod, coolerUnitControlMethod); + return result; + } + + bool ZoneHVACEvaporativeCoolerUnit_Impl::setThrottlingRangeTemperatureDifference(double throttlingRangeTemperatureDifference) { + const bool result = + setDouble(OS_ZoneHVAC_EvaporativeCoolerUnitFields::ThrottlingRangeTemperatureDifference, throttlingRangeTemperatureDifference); + return result; + } + + bool ZoneHVACEvaporativeCoolerUnit_Impl::setCoolingLoadControlThresholdHeatTransferRate(double coolingLoadControlThresholdHeatTransferRate) { + const bool result = + setDouble(OS_ZoneHVAC_EvaporativeCoolerUnitFields::CoolingLoadControlThresholdHeatTransferRate, coolingLoadControlThresholdHeatTransferRate); + return result; + } + + bool ZoneHVACEvaporativeCoolerUnit_Impl::setFirstEvaporativeCooler(const HVACComponent& hvacComponent) { + const bool result = setPointer(OS_ZoneHVAC_EvaporativeCoolerUnitFields::FirstEvaporativeCooler, hvacComponent.handle()); + return result; + } + + bool ZoneHVACEvaporativeCoolerUnit_Impl::setSecondEvaporativeCooler(const HVACComponent& hvacComponent) { + const bool result = setPointer(OS_ZoneHVAC_EvaporativeCoolerUnitFields::SecondEvaporativeCooler, hvacComponent.handle()); + return result; + } + + void ZoneHVACEvaporativeCoolerUnit_Impl::resetSecondEvaporativeCooler() { + const bool result = setString(OS_ZoneHVAC_EvaporativeCoolerUnitFields::SecondEvaporativeCooler, ""); + OS_ASSERT(result); + } + + bool ZoneHVACEvaporativeCoolerUnit_Impl::setShutOffRelativeHumidity(double shutOffRelativeHumidity) { + const bool result = setDouble(OS_ZoneHVAC_EvaporativeCoolerUnitFields::ShutOffRelativeHumidity, shutOffRelativeHumidity); + return result; + } + + void ZoneHVACEvaporativeCoolerUnit_Impl::autosize() { + autosizeDesignSupplyAirFlowRate(); + } + + void ZoneHVACEvaporativeCoolerUnit_Impl::applySizingValues() { + if (boost::optional val_ = autosizedDesignSupplyAirFlowRate()) { + setDesignSupplyAirFlowRate(*val_); + } + } + + boost::optional ZoneHVACEvaporativeCoolerUnit_Impl::optionalAvailabilitySchedule() const { + return getObject().getModelObjectTarget(OS_ZoneHVAC_EvaporativeCoolerUnitFields::AvailabilityScheduleName); + } + + boost::optional ZoneHVACEvaporativeCoolerUnit_Impl::optionalSupplyAirFan() const { + return getObject().getModelObjectTarget(OS_ZoneHVAC_EvaporativeCoolerUnitFields::SupplyAirFanName); + } + + boost::optional ZoneHVACEvaporativeCoolerUnit_Impl::optionalFirstEvaporativeCooler() const { + return getObject().getModelObjectTarget(OS_ZoneHVAC_EvaporativeCoolerUnitFields::FirstEvaporativeCooler); + } + + } // namespace detail + + ZoneHVACEvaporativeCoolerUnit::ZoneHVACEvaporativeCoolerUnit(const Model& model) + : ZoneHVACComponent(ZoneHVACEvaporativeCoolerUnit::iddObjectType(), model) { + OS_ASSERT(getImpl()); + + bool ok = true; + auto alwaysOn = model.alwaysOnDiscreteSchedule(); + ok = setAvailabilitySchedule(alwaysOn); + OS_ASSERT(ok); + + FanSystemModel supplyAirFan(model); + ok = setSupplyAirFan(supplyAirFan); + OS_ASSERT(ok); + + autosizeDesignSupplyAirFlowRate(); + ok = setFanPlacement("BlowThrough"); + OS_ASSERT(ok); + ok = setCoolerUnitControlMethod("ZoneTemperatureDeadbandOnOffCycling"); + OS_ASSERT(ok); + ok = setThrottlingRangeTemperatureDifference(1.0); + OS_ASSERT(ok); + ok = setCoolingLoadControlThresholdHeatTransferRate(100.0); + OS_ASSERT(ok); + + EvaporativeCoolerDirectResearchSpecial firstEvaporativeCooler(model, alwaysOn); + ok = setFirstEvaporativeCooler(firstEvaporativeCooler); + OS_ASSERT(ok); + + ok = setShutOffRelativeHumidity(100.0); + OS_ASSERT(ok); + } + + ZoneHVACEvaporativeCoolerUnit::ZoneHVACEvaporativeCoolerUnit(const Model& model, Schedule& availabilitySchedule, HVACComponent& supplyAirFan, + HVACComponent& firstEvaporativeCooler) + : ZoneHVACComponent(ZoneHVACEvaporativeCoolerUnit::iddObjectType(), model) { + OS_ASSERT(getImpl()); + + bool ok = true; + ok = setAvailabilitySchedule(availabilitySchedule); + OS_ASSERT(ok); + + ok = setSupplyAirFan(supplyAirFan); + if (!ok) { + remove(); + LOG_AND_THROW("Unable to set " << briefDescription() << "'s supply air fan to " << supplyAirFan.briefDescription() << "."); + } + + autosizeDesignSupplyAirFlowRate(); + ok = setFanPlacement("BlowThrough"); + OS_ASSERT(ok); + ok = setCoolerUnitControlMethod("ZoneTemperatureDeadbandOnOffCycling"); + OS_ASSERT(ok); + ok = setThrottlingRangeTemperatureDifference(1.0); + OS_ASSERT(ok); + ok = setCoolingLoadControlThresholdHeatTransferRate(100.0); + OS_ASSERT(ok); + + ok = setFirstEvaporativeCooler(firstEvaporativeCooler); + if (!ok) { + remove(); + LOG_AND_THROW("Unable to set " << briefDescription() << "'s first evaporative cooler to " << firstEvaporativeCooler.briefDescription() << "."); + } + + ok = setShutOffRelativeHumidity(100.0); + OS_ASSERT(ok); + } + + IddObjectType ZoneHVACEvaporativeCoolerUnit::iddObjectType() { + return {IddObjectType::OS_ZoneHVAC_EvaporativeCoolerUnit}; + } + + std::vector ZoneHVACEvaporativeCoolerUnit::fanPlacementValues() { + return getIddKeyNames(IddFactory::instance().getObject(iddObjectType()).get(), OS_ZoneHVAC_EvaporativeCoolerUnitFields::FanPlacement); + } + + std::vector ZoneHVACEvaporativeCoolerUnit::coolerUnitControlMethodValues() { + return getIddKeyNames(IddFactory::instance().getObject(iddObjectType()).get(), OS_ZoneHVAC_EvaporativeCoolerUnitFields::CoolerUnitControlMethod); + } + + Schedule ZoneHVACEvaporativeCoolerUnit::availabilitySchedule() const { + return getImpl()->availabilitySchedule(); + } + + HVACComponent ZoneHVACEvaporativeCoolerUnit::supplyAirFan() const { + return getImpl()->supplyAirFan(); + } + + boost::optional ZoneHVACEvaporativeCoolerUnit::designSupplyAirFlowRate() const { + return getImpl()->designSupplyAirFlowRate(); + } + + bool ZoneHVACEvaporativeCoolerUnit::isDesignSupplyAirFlowRateAutosized() const { + return getImpl()->isDesignSupplyAirFlowRateAutosized(); + } + + boost::optional ZoneHVACEvaporativeCoolerUnit::autosizedDesignSupplyAirFlowRate() { + return getImpl()->autosizedDesignSupplyAirFlowRate(); + } + + std::string ZoneHVACEvaporativeCoolerUnit::fanPlacement() const { + return getImpl()->fanPlacement(); + } + + std::string ZoneHVACEvaporativeCoolerUnit::coolerUnitControlMethod() const { + return getImpl()->coolerUnitControlMethod(); + } + + double ZoneHVACEvaporativeCoolerUnit::throttlingRangeTemperatureDifference() const { + return getImpl()->throttlingRangeTemperatureDifference(); + } + + double ZoneHVACEvaporativeCoolerUnit::coolingLoadControlThresholdHeatTransferRate() const { + return getImpl()->coolingLoadControlThresholdHeatTransferRate(); + } + + HVACComponent ZoneHVACEvaporativeCoolerUnit::firstEvaporativeCooler() const { + return getImpl()->firstEvaporativeCooler(); + } + + boost::optional ZoneHVACEvaporativeCoolerUnit::secondEvaporativeCooler() const { + return getImpl()->secondEvaporativeCooler(); + } + + double ZoneHVACEvaporativeCoolerUnit::shutOffRelativeHumidity() const { + return getImpl()->shutOffRelativeHumidity(); + } + + bool ZoneHVACEvaporativeCoolerUnit::setAvailabilitySchedule(Schedule& schedule) { + return getImpl()->setAvailabilitySchedule(schedule); + } + + bool ZoneHVACEvaporativeCoolerUnit::setSupplyAirFan(const HVACComponent& hvacComponent) { + return getImpl()->setSupplyAirFan(hvacComponent); + } + + bool ZoneHVACEvaporativeCoolerUnit::setDesignSupplyAirFlowRate(double designSupplyAirFlowRate) { + return getImpl()->setDesignSupplyAirFlowRate(designSupplyAirFlowRate); + } + + void ZoneHVACEvaporativeCoolerUnit::autosizeDesignSupplyAirFlowRate() { + getImpl()->autosizeDesignSupplyAirFlowRate(); + } + + bool ZoneHVACEvaporativeCoolerUnit::setFanPlacement(const std::string& fanPlacement) { + return getImpl()->setFanPlacement(fanPlacement); + } + + bool ZoneHVACEvaporativeCoolerUnit::setCoolerUnitControlMethod(const std::string& coolerUnitControlMethod) { + return getImpl()->setCoolerUnitControlMethod(coolerUnitControlMethod); + } + + bool ZoneHVACEvaporativeCoolerUnit::setThrottlingRangeTemperatureDifference(double throttlingRangeTemperatureDifference) { + return getImpl()->setThrottlingRangeTemperatureDifference(throttlingRangeTemperatureDifference); + } + + bool ZoneHVACEvaporativeCoolerUnit::setCoolingLoadControlThresholdHeatTransferRate(double coolingLoadControlThresholdHeatTransferRate) { + return getImpl()->setCoolingLoadControlThresholdHeatTransferRate( + coolingLoadControlThresholdHeatTransferRate); + } + + bool ZoneHVACEvaporativeCoolerUnit::setFirstEvaporativeCooler(const HVACComponent& hvacComponent) { + return getImpl()->setFirstEvaporativeCooler(hvacComponent); + } + + bool ZoneHVACEvaporativeCoolerUnit::setSecondEvaporativeCooler(const HVACComponent& hvacComponent) { + return getImpl()->setSecondEvaporativeCooler(hvacComponent); + } + + void ZoneHVACEvaporativeCoolerUnit::resetSecondEvaporativeCooler() { + getImpl()->resetSecondEvaporativeCooler(); + } + + bool ZoneHVACEvaporativeCoolerUnit::setShutOffRelativeHumidity(double shutOffRelativeHumidity) { + return getImpl()->setShutOffRelativeHumidity(shutOffRelativeHumidity); + } + + /// @cond + ZoneHVACEvaporativeCoolerUnit::ZoneHVACEvaporativeCoolerUnit(std::shared_ptr impl) + : ZoneHVACComponent(std::move(impl)) {} + /// @endcond + +} // namespace model +} // namespace openstudio diff --git a/src/model/ZoneHVACEvaporativeCoolerUnit.hpp b/src/model/ZoneHVACEvaporativeCoolerUnit.hpp new file mode 100644 index 0000000000..b33c610ac9 --- /dev/null +++ b/src/model/ZoneHVACEvaporativeCoolerUnit.hpp @@ -0,0 +1,160 @@ +/*********************************************************************************************************************** +* OpenStudio(R), Copyright (c) 2008-2023, Alliance for Sustainable Energy, LLC, and other contributors. All rights reserved. +* +* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +* following conditions are met: +* +* (1) Redistributions of source code must retain the above copyright notice, this list of conditions and the following +* disclaimer. +* +* (2) Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following +* disclaimer in the documentation and/or other materials provided with the distribution. +* +* (3) Neither the name of the copyright holder nor the names of any contributors may be used to endorse or promote products +* derived from this software without specific prior written permission from the respective party. +* +* (4) Other than as required in clauses (1) and (2), distributions in any form of modifications or other derivative works +* may not use the "OpenStudio" trademark, "OS", "os", or any other confusingly similar designation without specific prior +* written permission from Alliance for Sustainable Energy, LLC. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND ANY CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S), ANY CONTRIBUTORS, THE UNITED STATES GOVERNMENT, OR THE UNITED +* STATES DEPARTMENT OF ENERGY, NOR ANY OF THEIR EMPLOYEES, BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF +* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***********************************************************************************************************************/ + +#ifndef MODEL_ZONEHVACEVAPORATIVECOOLERUNIT_HPP +#define MODEL_ZONEHVACEVAPORATIVECOOLERUNIT_HPP + +#include +#include "ZoneHVACComponent.hpp" + +namespace openstudio { + +namespace model { + + class Schedule; + class HVACComponent; + + namespace detail { + + class ZoneHVACEvaporativeCoolerUnit_Impl; + + } // namespace detail + + /** ZoneHVACEvaporativeCoolerUnit is a ZoneHVACComponent that wraps the OpenStudio IDD object 'OS:ZoneHVAC:EvaporativeCoolerUnit'. */ + class MODEL_API ZoneHVACEvaporativeCoolerUnit : public ZoneHVACComponent + { + public: + /** @name Constructors and Destructors */ + //@{ + + explicit ZoneHVACEvaporativeCoolerUnit(const Model& model); + + explicit ZoneHVACEvaporativeCoolerUnit(const Model& model, Schedule& availabilitySchedule, HVACComponent& supplyAirFan, + HVACComponent& firstEvaporativeCooler); + + virtual ~ZoneHVACEvaporativeCoolerUnit() = default; + // Default the copy and move operators because the virtual dtor is explicit + ZoneHVACEvaporativeCoolerUnit(const ZoneHVACEvaporativeCoolerUnit& other) = default; + ZoneHVACEvaporativeCoolerUnit(ZoneHVACEvaporativeCoolerUnit&& other) = default; + ZoneHVACEvaporativeCoolerUnit& operator=(const ZoneHVACEvaporativeCoolerUnit&) = default; + ZoneHVACEvaporativeCoolerUnit& operator=(ZoneHVACEvaporativeCoolerUnit&&) = default; + + //@} + + static IddObjectType iddObjectType(); + + static std::vector fanPlacementValues(); + + static std::vector coolerUnitControlMethodValues(); + + /** @name Getters */ + //@{ + + Schedule availabilitySchedule() const; + + HVACComponent supplyAirFan() const; + + boost::optional designSupplyAirFlowRate() const; + + bool isDesignSupplyAirFlowRateAutosized() const; + + std::string fanPlacement() const; + + std::string coolerUnitControlMethod() const; + + double throttlingRangeTemperatureDifference() const; + + double coolingLoadControlThresholdHeatTransferRate() const; + + HVACComponent firstEvaporativeCooler() const; + + boost::optional secondEvaporativeCooler() const; + + double shutOffRelativeHumidity() const; + + //@} + /** @name Setters */ + //@{ + + bool setAvailabilitySchedule(Schedule& schedule); + + bool setSupplyAirFan(const HVACComponent& hvacComponent); + + bool setDesignSupplyAirFlowRate(double designSupplyAirFlowRate); + + void autosizeDesignSupplyAirFlowRate(); + + bool setFanPlacement(const std::string& fanPlacement); + + bool setCoolerUnitControlMethod(const std::string& coolerUnitControlMethod); + + bool setThrottlingRangeTemperatureDifference(double throttlingRangeTemperatureDifference); + + bool setCoolingLoadControlThresholdHeatTransferRate(double coolingLoadControlThresholdHeatTransferRate); + + bool setFirstEvaporativeCooler(const HVACComponent& hvacComponent); + + bool setSecondEvaporativeCooler(const HVACComponent& hvacComponent); + + void resetSecondEvaporativeCooler(); + + bool setShutOffRelativeHumidity(double shutOffRelativeHumidity); + + //@} + /** @name Other */ + //@{ + + boost::optional autosizedDesignSupplyAirFlowRate(); + + //@} + protected: + /// @cond + using ImplType = detail::ZoneHVACEvaporativeCoolerUnit_Impl; + + explicit ZoneHVACEvaporativeCoolerUnit(std::shared_ptr impl); + + friend class detail::ZoneHVACEvaporativeCoolerUnit_Impl; + friend class Model; + friend class IdfObject; + friend class openstudio::detail::IdfObject_Impl; + /// @endcond + private: + REGISTER_LOGGER("openstudio.model.ZoneHVACEvaporativeCoolerUnit"); + }; + + /** \relates ZoneHVACEvaporativeCoolerUnit*/ + using OptionalZoneHVACEvaporativeCoolerUnit = boost::optional; + + /** \relates ZoneHVACEvaporativeCoolerUnit*/ + using ZoneHVACEvaporativeCoolerUnitVector = std::vector; + +} // namespace model +} // namespace openstudio + +#endif // MODEL_ZONEHVACEVAPORATIVECOOLERUNIT_HPP diff --git a/src/model/ZoneHVACEvaporativeCoolerUnit_Impl.hpp b/src/model/ZoneHVACEvaporativeCoolerUnit_Impl.hpp new file mode 100644 index 0000000000..369ebff196 --- /dev/null +++ b/src/model/ZoneHVACEvaporativeCoolerUnit_Impl.hpp @@ -0,0 +1,161 @@ +/*********************************************************************************************************************** +* OpenStudio(R), Copyright (c) 2008-2023, Alliance for Sustainable Energy, LLC, and other contributors. All rights reserved. +* +* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +* following conditions are met: +* +* (1) Redistributions of source code must retain the above copyright notice, this list of conditions and the following +* disclaimer. +* +* (2) Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following +* disclaimer in the documentation and/or other materials provided with the distribution. +* +* (3) Neither the name of the copyright holder nor the names of any contributors may be used to endorse or promote products +* derived from this software without specific prior written permission from the respective party. +* +* (4) Other than as required in clauses (1) and (2), distributions in any form of modifications or other derivative works +* may not use the "OpenStudio" trademark, "OS", "os", or any other confusingly similar designation without specific prior +* written permission from Alliance for Sustainable Energy, LLC. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND ANY CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S), ANY CONTRIBUTORS, THE UNITED STATES GOVERNMENT, OR THE UNITED +* STATES DEPARTMENT OF ENERGY, NOR ANY OF THEIR EMPLOYEES, BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF +* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***********************************************************************************************************************/ + +#ifndef MODEL_ZONEHVACEVAPORATIVECOOLERUNIT_IMPL_HPP +#define MODEL_ZONEHVACEVAPORATIVECOOLERUNIT_IMPL_HPP + +#include +#include "ZoneHVACComponent_Impl.hpp" + +namespace openstudio { +namespace model { + + class Schedule; + class HVACComponent; + + namespace detail { + + /** ZoneHVACEvaporativeCoolerUnit_Impl is a ZoneHVACComponent_Impl that is the implementation class for ZoneHVACEvaporativeCoolerUnit.*/ + class MODEL_API ZoneHVACEvaporativeCoolerUnit_Impl : public ZoneHVACComponent_Impl + { + public: + /** @name Constructors and Destructors */ + //@{ + + ZoneHVACEvaporativeCoolerUnit_Impl(const IdfObject& idfObject, Model_Impl* model, bool keepHandle); + + ZoneHVACEvaporativeCoolerUnit_Impl(const openstudio::detail::WorkspaceObject_Impl& other, Model_Impl* model, bool keepHandle); + + ZoneHVACEvaporativeCoolerUnit_Impl(const ZoneHVACEvaporativeCoolerUnit_Impl& other, Model_Impl* model, bool keepHandle); + + virtual ~ZoneHVACEvaporativeCoolerUnit_Impl() = default; + + //@} + /** @name Virtual Methods */ + //@{ + + virtual ModelObject clone(Model model) const override; + + virtual const std::vector& outputVariableNames() const override; + + virtual IddObjectType iddObjectType() const override; + + virtual std::vector getScheduleTypeKeys(const Schedule& schedule) const override; + + virtual std::vector children() const override; + + virtual unsigned inletPort() const override; + + virtual unsigned outletPort() const override; + + virtual void autosize() override; + + virtual void applySizingValues() override; + + virtual ComponentType componentType() const override; + virtual std::vector coolingFuelTypes() const override; + virtual std::vector heatingFuelTypes() const override; + virtual std::vector appGHeatingFuelTypes() const override; + + //@} + /** @name Getters */ + //@{ + + Schedule availabilitySchedule() const; + + HVACComponent supplyAirFan() const; + + boost::optional designSupplyAirFlowRate() const; + + bool isDesignSupplyAirFlowRateAutosized() const; + + std::string fanPlacement() const; + + std::string coolerUnitControlMethod() const; + + double throttlingRangeTemperatureDifference() const; + + double coolingLoadControlThresholdHeatTransferRate() const; + + HVACComponent firstEvaporativeCooler() const; + + boost::optional secondEvaporativeCooler() const; + + double shutOffRelativeHumidity() const; + + //@} + /** @name Setters */ + //@{ + + bool setAvailabilitySchedule(Schedule& schedule); + + bool setSupplyAirFan(const HVACComponent& hvacComponent); + + bool setDesignSupplyAirFlowRate(double designSupplyAirFlowRate); + + void autosizeDesignSupplyAirFlowRate(); + + bool setFanPlacement(const std::string& fanPlacement); + + bool setCoolerUnitControlMethod(const std::string& coolerUnitControlMethod); + + bool setThrottlingRangeTemperatureDifference(double throttlingRangeTemperatureDifference); + + bool setCoolingLoadControlThresholdHeatTransferRate(double coolingLoadControlThresholdHeatTransferRate); + + bool setFirstEvaporativeCooler(const HVACComponent& hvacComponent); + + bool setSecondEvaporativeCooler(const HVACComponent& hvacComponent); + + void resetSecondEvaporativeCooler(); + + bool setShutOffRelativeHumidity(double shutOffRelativeHumidity); + + //@} + /** @name Other */ + //@{ + + boost::optional autosizedDesignSupplyAirFlowRate(); + + //@} + protected: + private: + REGISTER_LOGGER("openstudio.model.ZoneHVACEvaporativeCoolerUnit"); + + boost::optional optionalAvailabilitySchedule() const; + boost::optional optionalSupplyAirFan() const; + boost::optional optionalFirstEvaporativeCooler() const; + }; + + } // namespace detail + +} // namespace model +} // namespace openstudio + +#endif // MODEL_ZONEHVACEVAPORATIVECOOLERUNIT_IMPL_HPP diff --git a/src/model/test/ZoneHVACEvaporativeCoolerUnit_GTest.cpp b/src/model/test/ZoneHVACEvaporativeCoolerUnit_GTest.cpp new file mode 100644 index 0000000000..f9c0a7fdb2 --- /dev/null +++ b/src/model/test/ZoneHVACEvaporativeCoolerUnit_GTest.cpp @@ -0,0 +1,277 @@ +/*********************************************************************************************************************** +* OpenStudio(R), Copyright (c) 2008-2023, Alliance for Sustainable Energy, LLC, and other contributors. All rights reserved. +* +* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +* following conditions are met: +* +* (1) Redistributions of source code must retain the above copyright notice, this list of conditions and the following +* disclaimer. +* +* (2) Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following +* disclaimer in the documentation and/or other materials provided with the distribution. +* +* (3) Neither the name of the copyright holder nor the names of any contributors may be used to endorse or promote products +* derived from this software without specific prior written permission from the respective party. +* +* (4) Other than as required in clauses (1) and (2), distributions in any form of modifications or other derivative works +* may not use the "OpenStudio" trademark, "OS", "os", or any other confusingly similar designation without specific prior +* written permission from Alliance for Sustainable Energy, LLC. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND ANY CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S), ANY CONTRIBUTORS, THE UNITED STATES GOVERNMENT, OR THE UNITED +* STATES DEPARTMENT OF ENERGY, NOR ANY OF THEIR EMPLOYEES, BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF +* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***********************************************************************************************************************/ + +#include "ModelFixture.hpp" + +#include "../ZoneHVACEvaporativeCoolerUnit.hpp" +#include "../ZoneHVACEvaporativeCoolerUnit_Impl.hpp" +#include "../Schedule.hpp" +#include "../Schedule_Impl.hpp" +#include "../ScheduleConstant.hpp" +#include "../ScheduleConstant_Impl.hpp" +#include "../FanSystemModel.hpp" +#include "../FanSystemModel_Impl.hpp" +#include "../EvaporativeCoolerDirectResearchSpecial.hpp" +#include "../EvaporativeCoolerDirectResearchSpecial_Impl.hpp" +#include "../EvaporativeCoolerIndirectResearchSpecial.hpp" +#include "../EvaporativeCoolerIndirectResearchSpecial_Impl.hpp" +#include "../Node.hpp" +#include "../Node_Impl.hpp" +#include "../AirLoopHVAC.hpp" +#include "../AirLoopHVAC_Impl.hpp" +#include "../AirLoopHVACZoneSplitter.hpp" +#include "../AirLoopHVACZoneSplitter_Impl.hpp" +#include "../PlantLoop.hpp" +#include "../PlantLoop_Impl.hpp" +#include "../ThermalZone.hpp" +#include "../ThermalZone_Impl.hpp" + +using namespace openstudio; +using namespace openstudio::model; + +TEST_F(ModelFixture, ZoneHVACEvaporativeCoolerUnit_GettersSetters) { + Model m; + + ZoneHVACEvaporativeCoolerUnit zoneHVACEvaporativeCoolerUnit(m); + + zoneHVACEvaporativeCoolerUnit.setName("My ZoneHVACEvaporativeCoolerUnit"); + + // Availability Schedule Name: Required Object + Schedule availabilitySchedule = m.alwaysOnDiscreteSchedule(); + EXPECT_TRUE(zoneHVACEvaporativeCoolerUnit.setAvailabilitySchedule(availabilitySchedule)); + EXPECT_EQ(availabilitySchedule, zoneHVACEvaporativeCoolerUnit.availabilitySchedule()); + + // Supply Air Fan Name: Required Object + FanSystemModel supplyAirFan(m); + EXPECT_TRUE(zoneHVACEvaporativeCoolerUnit.setSupplyAirFan(supplyAirFan)); + EXPECT_EQ(supplyAirFan, zoneHVACEvaporativeCoolerUnit.supplyAirFan()); + + // Design Supply Air Flow Rate: Required Double + // Autosize + zoneHVACEvaporativeCoolerUnit.autosizeDesignSupplyAirFlowRate(); + EXPECT_TRUE(zoneHVACEvaporativeCoolerUnit.isDesignSupplyAirFlowRateAutosized()); + // Set + EXPECT_TRUE(zoneHVACEvaporativeCoolerUnit.setDesignSupplyAirFlowRate(0.9)); + ASSERT_TRUE(zoneHVACEvaporativeCoolerUnit.designSupplyAirFlowRate()); + EXPECT_EQ(0.9, zoneHVACEvaporativeCoolerUnit.designSupplyAirFlowRate().get()); + // Bad Value + EXPECT_FALSE(zoneHVACEvaporativeCoolerUnit.setDesignSupplyAirFlowRate(-10.0)); + ASSERT_TRUE(zoneHVACEvaporativeCoolerUnit.designSupplyAirFlowRate()); + EXPECT_EQ(0.9, zoneHVACEvaporativeCoolerUnit.designSupplyAirFlowRate().get()); + EXPECT_FALSE(zoneHVACEvaporativeCoolerUnit.isDesignSupplyAirFlowRateAutosized()); + + // Fan Placement: Required String + EXPECT_TRUE(zoneHVACEvaporativeCoolerUnit.setFanPlacement("BlowThrough")); + EXPECT_EQ("BlowThrough", zoneHVACEvaporativeCoolerUnit.fanPlacement()); + // Bad Value + EXPECT_FALSE(zoneHVACEvaporativeCoolerUnit.setFanPlacement("BADENUM")); + EXPECT_EQ("BlowThrough", zoneHVACEvaporativeCoolerUnit.fanPlacement()); + + // Cooler Unit Control Method: Required String + EXPECT_TRUE(zoneHVACEvaporativeCoolerUnit.setCoolerUnitControlMethod("ZoneTemperatureDeadbandOnOffCycling")); + EXPECT_EQ("ZoneTemperatureDeadbandOnOffCycling", zoneHVACEvaporativeCoolerUnit.coolerUnitControlMethod()); + // Bad Value + EXPECT_FALSE(zoneHVACEvaporativeCoolerUnit.setCoolerUnitControlMethod("BADENUM")); + EXPECT_EQ("ZoneTemperatureDeadbandOnOffCycling", zoneHVACEvaporativeCoolerUnit.coolerUnitControlMethod()); + + // Throttling Range Temperature Difference: Required Double + EXPECT_TRUE(zoneHVACEvaporativeCoolerUnit.setThrottlingRangeTemperatureDifference(1.2)); + EXPECT_EQ(1.2, zoneHVACEvaporativeCoolerUnit.throttlingRangeTemperatureDifference()); + // Bad Value + EXPECT_FALSE(zoneHVACEvaporativeCoolerUnit.setThrottlingRangeTemperatureDifference(-10.0)); + EXPECT_EQ(1.2, zoneHVACEvaporativeCoolerUnit.throttlingRangeTemperatureDifference()); + + // Cooling Load Control Threshold Heat Transfer Rate: Required Double + EXPECT_TRUE(zoneHVACEvaporativeCoolerUnit.setCoolingLoadControlThresholdHeatTransferRate(1.3)); + EXPECT_EQ(1.3, zoneHVACEvaporativeCoolerUnit.coolingLoadControlThresholdHeatTransferRate()); + // Bad Value + EXPECT_FALSE(zoneHVACEvaporativeCoolerUnit.setCoolingLoadControlThresholdHeatTransferRate(-10.0)); + EXPECT_EQ(1.3, zoneHVACEvaporativeCoolerUnit.coolingLoadControlThresholdHeatTransferRate()); + + // First Evaporative Cooler: Required Object + EvaporativeCoolerDirectResearchSpecial firstEvaporativeCooler(m, availabilitySchedule); + EXPECT_TRUE(zoneHVACEvaporativeCoolerUnit.setFirstEvaporativeCooler(firstEvaporativeCooler)); + EXPECT_EQ(firstEvaporativeCooler, zoneHVACEvaporativeCoolerUnit.firstEvaporativeCooler()); + + // Second Evaporative Cooler Name: Optional Object + EXPECT_FALSE(zoneHVACEvaporativeCoolerUnit.secondEvaporativeCooler()); + EvaporativeCoolerIndirectResearchSpecial secondEvaporativeCooler(m); + EXPECT_TRUE(zoneHVACEvaporativeCoolerUnit.setSecondEvaporativeCooler(secondEvaporativeCooler)); + ASSERT_TRUE(zoneHVACEvaporativeCoolerUnit.secondEvaporativeCooler()); + EXPECT_EQ(secondEvaporativeCooler, zoneHVACEvaporativeCoolerUnit.secondEvaporativeCooler().get()); + + // Shut Off Relative Humidity: Required Double + EXPECT_TRUE(zoneHVACEvaporativeCoolerUnit.setShutOffRelativeHumidity(94.444)); + EXPECT_EQ(94.444, zoneHVACEvaporativeCoolerUnit.shutOffRelativeHumidity()); + // Bad Value + EXPECT_FALSE(zoneHVACEvaporativeCoolerUnit.setShutOffRelativeHumidity(-10.0)); + EXPECT_EQ(94.444, zoneHVACEvaporativeCoolerUnit.shutOffRelativeHumidity()); +} + +TEST_F(ModelFixture, ZoneHVACEvaporativeCoolerUnit_HeatCoolFuelTypes) { + Model m; + + ZoneHVACEvaporativeCoolerUnit zoneHVACEvaporativeCoolerUnit(m); + + EXPECT_EQ(ComponentType(ComponentType::Cooling), zoneHVACEvaporativeCoolerUnit.componentType()); + testFuelTypeEquality({FuelType::Electricity}, zoneHVACEvaporativeCoolerUnit.coolingFuelTypes()); + testFuelTypeEquality({}, zoneHVACEvaporativeCoolerUnit.heatingFuelTypes()); + testAppGFuelTypeEquality({}, zoneHVACEvaporativeCoolerUnit.appGHeatingFuelTypes()); +} + +TEST_F(ModelFixture, ZoneHVACEvaporativeCoolerUnit_addToThermalZone) { + Model m; + ZoneHVACEvaporativeCoolerUnit zonehvac(m); + + ThermalZone tz(m); + ASSERT_TRUE(zonehvac.addToThermalZone(tz)); + EXPECT_TRUE(zonehvac.inletNode()); + EXPECT_TRUE(zonehvac.outletNode()); + ASSERT_TRUE(zonehvac.thermalZone()); + ASSERT_EQ(tz, zonehvac.thermalZone().get()); + ASSERT_EQ(1u, tz.equipment().size()); + zonehvac.removeFromThermalZone(); + ASSERT_EQ(0u, tz.equipment().size()); + + ZoneHVACEvaporativeCoolerUnit zonehvac2(m); + zonehvac2.addToThermalZone(tz); + zonehvac2.remove(); + ASSERT_EQ(0u, tz.equipment().size()); +} + +TEST_F(ModelFixture, ZoneHVACEvaporativeCoolerUnit_clone) { + Model m; + FanSystemModel fan(m); + Schedule availabilitySchedule = m.alwaysOnDiscreteSchedule(); + EvaporativeCoolerDirectResearchSpecial firstEvaporativeCooler(m, availabilitySchedule); + ZoneHVACEvaporativeCoolerUnit zonehvac(m, availabilitySchedule, fan, firstEvaporativeCooler); + EvaporativeCoolerIndirectResearchSpecial secondEvaporativeCooler(m); + EXPECT_TRUE(zonehvac.setSecondEvaporativeCooler(secondEvaporativeCooler)); + EXPECT_EQ(1u, m.getConcreteModelObjects().size()); + EXPECT_EQ(1u, m.getConcreteModelObjects().size()); + EXPECT_EQ(1u, m.getConcreteModelObjects().size()); + + ThermalZone tz(m); + EXPECT_TRUE(zonehvac.addToThermalZone(tz)); + EXPECT_TRUE(zonehvac.inletNode()); + EXPECT_TRUE(zonehvac.outletNode()); + EXPECT_TRUE(zonehvac.thermalZone()); + { + Model m2; + auto zonehvacClone = zonehvac.clone(m2).cast(); + EXPECT_FALSE(zonehvacClone.inletNode()); + EXPECT_FALSE(zonehvacClone.outletNode()); + EXPECT_FALSE(zonehvacClone.thermalZone()); + EXPECT_NE(fan.handle(), zonehvacClone.supplyAirFan().handle()); + EXPECT_NE(firstEvaporativeCooler.handle(), zonehvacClone.firstEvaporativeCooler().handle()); + ASSERT_TRUE(zonehvacClone.secondEvaporativeCooler()); + EXPECT_NE(secondEvaporativeCooler.handle(), zonehvacClone.secondEvaporativeCooler().get().handle()); + EXPECT_EQ(1u, m2.getConcreteModelObjects().size()); + EXPECT_EQ(1u, m2.getConcreteModelObjects().size()); + EXPECT_EQ(1u, m2.getConcreteModelObjects().size()); + } + + { + auto zonehvacClone = zonehvac.clone(m).cast(); + EXPECT_FALSE(zonehvacClone.inletNode()); + EXPECT_FALSE(zonehvacClone.outletNode()); + EXPECT_FALSE(zonehvacClone.thermalZone()); + EXPECT_NE(fan.handle(), zonehvacClone.supplyAirFan().handle()); + EXPECT_NE(firstEvaporativeCooler.handle(), zonehvacClone.firstEvaporativeCooler().handle()); + ASSERT_TRUE(zonehvacClone.secondEvaporativeCooler()); + EXPECT_NE(secondEvaporativeCooler.handle(), zonehvacClone.secondEvaporativeCooler().get().handle()); + EXPECT_EQ(2u, m.getConcreteModelObjects().size()); + EXPECT_EQ(2u, m.getConcreteModelObjects().size()); + EXPECT_EQ(2u, m.getConcreteModelObjects().size()); + } +} + +TEST_F(ModelFixture, ZoneHVACEvaporativeCoolerUnit_remove) { + Model m; + ZoneHVACEvaporativeCoolerUnit zonehvac(m); + + auto fanSystem = zonehvac.supplyAirFan(); + ASSERT_TRUE(fanSystem.optionalCast()); + EXPECT_TRUE(fanSystem.containingZoneHVACComponent()); + EXPECT_EQ(zonehvac.handle(), fanSystem.containingZoneHVACComponent().get().handle()); + EXPECT_FALSE(fanSystem.isRemovable()); + + auto evapCoolerComponent = zonehvac.firstEvaporativeCooler(); + ASSERT_TRUE(evapCoolerComponent.optionalCast()); + EXPECT_TRUE(evapCoolerComponent.containingZoneHVACComponent()); + EXPECT_EQ(zonehvac.handle(), evapCoolerComponent.containingZoneHVACComponent().get().handle()); + EXPECT_FALSE(evapCoolerComponent.isRemovable()); + + auto size = m.modelObjects().size(); + EXPECT_EQ(1, m.getConcreteModelObjects().size()); + EXPECT_EQ(1, m.getConcreteModelObjects().size()); + EXPECT_EQ(1, m.getConcreteModelObjects().size()); + EXPECT_EQ(0, m.getConcreteModelObjects().size()); + EXPECT_EQ(1, m.getConcreteModelObjects().size()); + // remove returns: ["Zone HVAC Evaporative Cooler Unit 1", "Fan Component Model 1", "Evaporative Cooler Direct Research Special 1"] + EXPECT_FALSE(zonehvac.remove().empty()); + EXPECT_EQ(size - 3, m.modelObjects().size()); + 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(1, m.getConcreteModelObjects().size()); +} + +TEST_F(ModelFixture, ZoneHVACEvaporativeCoolerUnit_addToNode) { + Model m; + ZoneHVACEvaporativeCoolerUnit zonehvac(m); + + AirLoopHVAC airLoop(m); + + Node supplyOutletNode = airLoop.supplyOutletNode(); + + EXPECT_FALSE(zonehvac.addToNode(supplyOutletNode)); + EXPECT_EQ(2, airLoop.supplyComponents().size()); + + Node inletNode = airLoop.zoneSplitter().lastOutletModelObject()->cast(); + + EXPECT_FALSE(zonehvac.addToNode(inletNode)); + EXPECT_EQ(5, airLoop.demandComponents().size()); + + PlantLoop plantLoop(m); + supplyOutletNode = plantLoop.supplyOutletNode(); + EXPECT_FALSE(zonehvac.addToNode(supplyOutletNode)); + EXPECT_EQ(5, plantLoop.supplyComponents().size()); + + Node demandOutletNode = plantLoop.demandOutletNode(); + EXPECT_FALSE(zonehvac.addToNode(demandOutletNode)); + EXPECT_EQ(5, plantLoop.demandComponents().size()); + + auto zonehvacClone = zonehvac.clone(m).cast(); + supplyOutletNode = plantLoop.supplyOutletNode(); + + EXPECT_FALSE(zonehvacClone.addToNode(supplyOutletNode)); + EXPECT_EQ(5, plantLoop.supplyComponents().size()); +}