From dd7dbee58fffde568ed4c29369d41ec611cb51aa Mon Sep 17 00:00:00 2001 From: "Lerond, Jeremy" Date: Sun, 21 Jul 2024 23:19:33 -0700 Subject: [PATCH 1/2] Allow ZoneHVAC:WindowAirConditioner along AFN simulations. --- src/EnergyPlus/AirflowNetwork/src/Solver.cpp | 14 ++ src/EnergyPlus/WindowAC.cc | 30 ++++ src/EnergyPlus/WindowAC.hh | 2 + .../unit/AirflowNetworkHVAC.unit.cc | 137 ++++++++++++++++++ 4 files changed, 183 insertions(+) diff --git a/src/EnergyPlus/AirflowNetwork/src/Solver.cpp b/src/EnergyPlus/AirflowNetwork/src/Solver.cpp index 8d583fbb388..058ea051962 100644 --- a/src/EnergyPlus/AirflowNetwork/src/Solver.cpp +++ b/src/EnergyPlus/AirflowNetwork/src/Solver.cpp @@ -102,6 +102,7 @@ #include #include #include +#include #include #include @@ -10143,6 +10144,7 @@ namespace AirflowNetwork { using SplitterComponent::GetSplitterNodeNumbers; using SplitterComponent::GetSplitterOutletNumber; using WaterThermalTanks::GetHeatPumpWaterHeaterNodeNumber; + using WindowAC::GetWindowACNodeNumber; using ZoneDehumidifier::GetZoneDehumidifierNodeNumber; // SUBROUTINE PARAMETER DEFINITIONS: @@ -10161,6 +10163,7 @@ namespace AirflowNetwork { bool HPWHFound(false); // Flag for HPWH identification bool StandaloneERVFound(false); // Flag for Standalone ERV (ZoneHVAC:EnergyRecoveryVentilator) identification + bool WindowACFound(false); // Flag for Window AC (ZoneHVAC:WindowAirConditioner) identification // Validate supply and return connections NodeFound.dimension(m_state.dataLoopNodes->NumOfNodes, false); @@ -10273,6 +10276,12 @@ namespace AirflowNetwork { NodeFound(i) = true; StandaloneERVFound = true; } + + // Skip Window AC with no OA + if (GetWindowACNodeNumber(m_state, i)) { + NodeFound(i) = true; + WindowACFound = true; + } } for (int zoneNum = 1; zoneNum <= m_state.dataGlobal->NumOfZones; ++zoneNum) { @@ -10413,6 +10422,11 @@ namespace AirflowNetwork { format(RoutineName) + "A ZoneHVAC:EnergyRecoveryVentilator is simulated along with an AirflowNetwork but is not " "included in the AirflowNetwork."); } + if (WindowACFound) { + ShowWarningError(m_state, + format(RoutineName) + "A ZoneHVAC:WindowAirConditioner is simulated along with an AirflowNetwork but is not " + "included in the AirflowNetwork."); + } NodeFound.deallocate(); // Assign AirLoop Number to every node and linkage diff --git a/src/EnergyPlus/WindowAC.cc b/src/EnergyPlus/WindowAC.cc index 6dad593cbcc..740b9eabd99 100644 --- a/src/EnergyPlus/WindowAC.cc +++ b/src/EnergyPlus/WindowAC.cc @@ -1499,6 +1499,36 @@ namespace WindowAC { } // WindAC(WindACNum)%DXCoilType_Num == CoilDX_CoolingHXAssisted && * } + bool GetWindowACNodeNumber(EnergyPlusData &state, int const NodeNumber) + { + if (state.dataWindowAC->GetWindowACInputFlag) { + GetWindowAC(state); + state.dataWindowAC->GetWindowACInputFlag = false; + } + + bool windowACOutdoorAir = false; + + for (int windowACIndex = 1; windowACIndex <= state.dataWindowAC->NumWindAC; ++windowACIndex) { + auto &windowAC = state.dataWindowAC->WindAC(windowACIndex); + if (windowAC.OutAirVolFlow == 0) { + windowACOutdoorAir = true; + } else { + windowACOutdoorAir = false; + } + int FanInletNodeIndex = 0; + int FanOutletNodeIndex = 0; + FanInletNodeIndex = state.dataFans->fans(windowAC.FanIndex)->inletNodeNum; + FanOutletNodeIndex = state.dataFans->fans(windowAC.FanIndex)->outletNodeNum; + + if (windowACOutdoorAir && + (NodeNumber == windowAC.OutsideAirNode || NodeNumber == windowAC.MixedAirNode || NodeNumber == windowAC.AirReliefNode || + NodeNumber == FanInletNodeIndex || NodeNumber == FanOutletNodeIndex || NodeNumber == windowAC.AirInNode)) { + return true; + } + } + return false; + } + int GetWindowACZoneInletAirNode(EnergyPlusData &state, int const WindACNum) { diff --git a/src/EnergyPlus/WindowAC.hh b/src/EnergyPlus/WindowAC.hh index 1520ddcf6a4..953c6eeee5a 100644 --- a/src/EnergyPlus/WindowAC.hh +++ b/src/EnergyPlus/WindowAC.hh @@ -197,6 +197,8 @@ namespace WindowAC { bool &HXUnitOn // Used to control HX heat recovery as needed ); + bool GetWindowACNodeNumber(EnergyPlusData &state, int const WindACNum); + int GetWindowACZoneInletAirNode(EnergyPlusData &state, int const WindACNum); int GetWindowACOutAirNode(EnergyPlusData &state, int const WindACNum); diff --git a/tst/EnergyPlus/unit/AirflowNetworkHVAC.unit.cc b/tst/EnergyPlus/unit/AirflowNetworkHVAC.unit.cc index 712f22d1fba..e94bc58351d 100644 --- a/tst/EnergyPlus/unit/AirflowNetworkHVAC.unit.cc +++ b/tst/EnergyPlus/unit/AirflowNetworkHVAC.unit.cc @@ -83,6 +83,7 @@ #include #include #include +#include #include #include #include @@ -19767,4 +19768,140 @@ TEST_F(EnergyPlusFixture, AirflowNetwork_ZoneOrderTest) state->afn->AirflowNetworkNodeData(3).EPlusNodeNum = 0; } +TEST_F(EnergyPlusFixture, AirflowNetwork_TestZoneEqpSupportZoneWindowAC) +{ + // Create zone + state->dataGlobal->NumOfZones = 1; + state->dataHeatBal->Zone.allocate(1); + state->dataHeatBal->Zone(1).Name = "ZONE 1"; + + // Create surfaces + state->dataSurface->Surface.allocate(1); + state->dataSurface->Surface(1).Name = "ZN004:ROOF001"; + state->dataSurface->Surface(1).Zone = 1; + state->dataSurface->Surface(1).ZoneName = "ZONE 1"; + state->dataSurface->Surface(1).Azimuth = 0.0; + state->dataSurface->Surface(1).ExtBoundCond = 0; + state->dataSurface->Surface(1).HeatTransSurf = true; + state->dataSurface->Surface(1).Tilt = 180.0; + state->dataSurface->Surface(1).Sides = 4; + state->dataSurface->Surface(1).Name = "ZN004:ROOF002"; + state->dataSurface->Surface(1).Zone = 1; + state->dataSurface->Surface(1).ZoneName = "ZONE 1"; + state->dataSurface->Surface(1).Azimuth = 0.0; + state->dataSurface->Surface(1).ExtBoundCond = 0; + state->dataSurface->Surface(1).HeatTransSurf = true; + state->dataSurface->Surface(1).Tilt = 180.0; + state->dataSurface->Surface(1).Sides = 4; + + state->dataSurface->Surface(1).OriginalClass = DataSurfaces::SurfaceClass::Window; + + // Create air system + state->dataAirSystemsData->PrimaryAirSystems.allocate(1); + state->dataAirSystemsData->PrimaryAirSystems(1).NumBranches = 1; + state->dataAirSystemsData->PrimaryAirSystems(1).Branch.allocate(1); + state->dataAirSystemsData->PrimaryAirSystems(1).Branch(1).TotalComponents = 1; + state->dataAirSystemsData->PrimaryAirSystems(1).Branch(1).Comp.allocate(1); + state->dataAirSystemsData->PrimaryAirSystems(1).Branch(1).Comp(1).TypeOf = "Fan:ConstantVolume"; + + // Create air nodes + state->dataLoopNodes->NumOfNodes = 3; + state->dataLoopNodes->Node.allocate(3); + state->dataLoopNodes->Node(1).FluidType = DataLoopNode::NodeFluidType::Air; + state->dataLoopNodes->Node(2).FluidType = DataLoopNode::NodeFluidType::Air; + state->dataLoopNodes->Node(3).FluidType = DataLoopNode::NodeFluidType::Air; + state->dataLoopNodes->NodeID.allocate(3); + state->dataLoopNodes->NodeID(1) = "ZONE 1 AIR NODE"; + bool errFlag{false}; + BranchNodeConnections::RegisterNodeConnection(*state, + 1, + "ZONE 1 AIR NODE", + DataLoopNode::ConnectionObjectType::FanOnOff, + "Object1", + DataLoopNode::ConnectionType::ZoneNode, + NodeInputManager::CompFluidStream::Primary, + false, + errFlag); + EXPECT_FALSE(errFlag); + + // Connect zone to air node + state->dataZoneEquip->ZoneEquipConfig.allocate(1); + state->dataZoneEquip->ZoneEquipConfig(1).IsControlled = true; + state->dataZoneEquip->ZoneEquipConfig(1).ZoneName = "ZONE 1"; + state->dataZoneEquip->ZoneEquipConfig(1).ZoneNode = 1; + state->dataZoneEquip->ZoneEquipConfig(1).NumInletNodes = 0; + state->dataZoneEquip->ZoneEquipConfig(1).NumReturnNodes = 0; + state->dataZoneEquip->ZoneEquipConfig(1).IsControlled = true; + + // One AirflowNetwork:MultiZone:Zone object + state->afn->AirflowNetworkNumOfZones = 1; + state->afn->MultizoneZoneData.allocate(1); + state->afn->MultizoneZoneData(1).ZoneNum = 1; + state->afn->MultizoneZoneData(1).ZoneName = "ZONE 1"; + + // Assume only one AirflowNetwork:Distribution:Node object is set for the Zone Air Node + state->afn->AirflowNetworkNumOfNodes = 1; + state->afn->AirflowNetworkNodeData.allocate(1); + state->afn->AirflowNetworkNodeData(1).Name = "ZONE 1"; + state->afn->AirflowNetworkNodeData(1).EPlusZoneNum = 1; + + state->afn->SplitterNodeNumbers.allocate(2); + state->afn->SplitterNodeNumbers(1) = 0; + state->afn->SplitterNodeNumbers(2) = 0; + + // Set flag to support zone equipment + state->afn->simulation_control.allow_unsupported_zone_equipment = true; + + // Create Fans + Real64 supplyFlowRate = 0.005; + Real64 exhaustFlowRate = 0.005; + + auto *fan1 = new Fans::FanComponent; + fan1->Name = "SupplyFan"; + + fan1->inletNodeNum = 2; + fan1->outletNodeNum = 3; + fan1->type = HVAC::FanType::OnOff; + fan1->maxAirFlowRate = supplyFlowRate; + + state->dataFans->fans.push_back(fan1); + state->dataFans->fanMap.insert_or_assign(fan1->Name, state->dataFans->fans.size()); + + state->dataLoopNodes->NodeID(2) = "SupplyFanInletNode"; + BranchNodeConnections::RegisterNodeConnection(*state, + 2, + state->dataLoopNodes->NodeID(2), + DataLoopNode::ConnectionObjectType::FanOnOff, + state->dataFans->fans(1)->Name, + DataLoopNode::ConnectionType::Inlet, + NodeInputManager::CompFluidStream::Primary, + false, + errFlag); + state->dataLoopNodes->NodeID(3) = "SupplyFanOutletNode"; + BranchNodeConnections::RegisterNodeConnection(*state, + 3, + state->dataLoopNodes->NodeID(3), + DataLoopNode::ConnectionObjectType::FanOnOff, + state->dataFans->fans(1)->Name, + DataLoopNode::ConnectionType::Outlet, + NodeInputManager::CompFluidStream::Primary, + false, + errFlag); + + // Create Window AC + state->dataWindowAC->WindAC.allocate(1); + state->dataWindowAC->GetWindowACInputFlag = false; + state->dataWindowAC->NumWindAC = 1; + state->dataWindowAC->WindAC(1).OutAirVolFlow = 0.0; + state->dataWindowAC->WindAC(1).FanName = state->dataFans->fans(1)->Name; + state->dataWindowAC->WindAC(1).FanIndex = 1; + + // Check validation and expected warning + state->afn->validate_distribution(); + + EXPECT_TRUE(compare_err_stream(" ** Warning ** AirflowNetwork::Solver::validate_distribution: A ZoneHVAC:WindowAirConditioner is simulated " + "along with an AirflowNetwork but is not included in the AirflowNetwork.\n", + true)); +} + } // namespace EnergyPlus From 096c7e6dddf42e708bcd295fa3439deb17ec81eb Mon Sep 17 00:00:00 2001 From: "Lerond, Jeremy" Date: Mon, 22 Jul 2024 09:06:06 -0700 Subject: [PATCH 2/2] Edit docs. --- .../src/overview/group-airflow-network.tex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/input-output-reference/src/overview/group-airflow-network.tex b/doc/input-output-reference/src/overview/group-airflow-network.tex index 488041495a6..0445fd9c008 100644 --- a/doc/input-output-reference/src/overview/group-airflow-network.tex +++ b/doc/input-output-reference/src/overview/group-airflow-network.tex @@ -331,7 +331,7 @@ \subsubsection{Inputs}\label{inputs-004} \paragraph{Field: Allow Unsupported Zone Equipment}\label{allow-unsupported-zone-equipment} -This is an optional field. Input is Yes or No. The default is No. Set this input to Yes to have zone equipment that are currently unsupported in the AirflowNetwork model allowed in the simulation. Setting this field to Yes, allows the following equipment to be modeled along an AirflowNetwork model: ZoneHVAC:Dehumidifier, ZoneHVAC:EnergyRecoveryVentilator, WaterHeater:HeatPump:*. The AirflowNetwork model will exclude mass balance in these equipment objects and assume the mass flows are self-balanced in the equipment objects. +This is an optional field. Input is Yes or No. The default is No. Set this input to Yes to have zone equipment that are currently unsupported in the AirflowNetwork model allowed in the simulation. Setting this field to Yes, allows the following equipment to be modeled along an AirflowNetwork model: ZoneHVAC:Dehumidifier, ZoneHVAC:EnergyRecoveryVentilator, WaterHeater:HeatPump:*, and ZoneHVAC:WindowAirConditioner. The AirflowNetwork model will exclude mass balance in these equipment objects and assume the mass flows are self-balanced in the equipment objects. \paragraph{Field: Do Distribution Duct Sizing Calculation}\label{do-distribution-duct-sizing-calculation}