diff --git a/src/EnergyPlus/CondenserLoopTowers.cc b/src/EnergyPlus/CondenserLoopTowers.cc index dfd6f8f7b5b..a99dee14c4c 100644 --- a/src/EnergyPlus/CondenserLoopTowers.cc +++ b/src/EnergyPlus/CondenserLoopTowers.cc @@ -137,16 +137,16 @@ namespace CondenserLoopTowers { this->initialize(state); switch (this->TowerType) { case DataPlant::PlantEquipmentType::CoolingTower_SingleSpd: - this->calculateSingleSpeedTower(state); + this->calculateSingleSpeedTower(state, CurLoad, RunFlag); break; case DataPlant::PlantEquipmentType::CoolingTower_TwoSpd: - this->calculateTwoSpeedTower(state); + this->calculateTwoSpeedTower(state, CurLoad, RunFlag); break; case DataPlant::PlantEquipmentType::CoolingTower_VarSpd: - this->calculateVariableSpeedTower(state); + this->calculateVariableSpeedTower(state, CurLoad, RunFlag); break; case DataPlant::PlantEquipmentType::CoolingTower_VarSpdMerkel: - this->calculateMerkelVariableSpeedTower(state, CurLoad); + this->calculateMerkelVariableSpeedTower(state, CurLoad, RunFlag); break; default: ShowFatalError(state, format("Plant Equipment Type specified for {} is not a Cooling Tower.", this->Name)); @@ -4535,7 +4535,7 @@ namespace CondenserLoopTowers { } } // namespace CondenserLoopTowers - void CoolingTower::calculateSingleSpeedTower(EnergyPlusData &state) + void CoolingTower::calculateSingleSpeedTower(EnergyPlusData &state, Real64 &MyLoad, bool RunFlag) { // SUBROUTINE INFORMATION: @@ -4695,13 +4695,9 @@ namespace CondenserLoopTowers { // Do not RETURN here if flow rate is less than SmallMassFlow. Check basin heater and then RETURN. - // MassFlowTolerance is a parameter to indicate a no flow condition - if (this->WaterMassFlowRate <= DataBranchAirLoopPlant::MassFlowTolerance) { - // for multiple cells, we assume that it's a common basin - CalcBasinHeaterPower( - state, this->BasinHeaterPowerFTempDiff, this->BasinHeaterSchedulePtr, this->BasinHeaterSetPointTemp, this->BasinHeaterPower); - return; - } + bool returnFlagSet = false; + this->checkMassFlowAndLoad(state, MyLoad, RunFlag, returnFlagSet); + if (returnFlagSet) return; bool IncrNumCellFlag = true; // determine if yes or no we increase the number of cells // set value to true to enter in the loop @@ -4847,7 +4843,7 @@ namespace CondenserLoopTowers { this->airFlowRateRatio = (AirFlowRate * this->NumCell) / this->HighSpeedAirFlowRate; } - void CoolingTower::calculateTwoSpeedTower(EnergyPlusData &state) + void CoolingTower::calculateTwoSpeedTower(EnergyPlusData &state, Real64 &MyLoad, bool RunFlag) { // SUBROUTINE INFORMATION: @@ -4974,12 +4970,9 @@ namespace CondenserLoopTowers { // Do not RETURN here if flow rate is less than SmallMassFlow. Check basin heater and then RETURN. if (state.dataPlnt->PlantLoop(this->plantLoc.loopNum).LoopSide(this->plantLoc.loopSideNum).FlowLock == DataPlant::FlowLock::Unlocked) return; // TODO: WTF - // MassFlowTolerance is a parameter to indicate a no flow condition - if (this->WaterMassFlowRate <= DataBranchAirLoopPlant::MassFlowTolerance) { - CalcBasinHeaterPower( - state, this->BasinHeaterPowerFTempDiff, this->BasinHeaterSchedulePtr, this->BasinHeaterSetPointTemp, this->BasinHeaterPower); - return; - } + bool returnFlagSet = false; + this->checkMassFlowAndLoad(state, MyLoad, RunFlag, returnFlagSet); + if (returnFlagSet) return; // Added for multi-cell. Determine the number of cells operating Real64 WaterMassFlowRatePerCellMin = 0.0; @@ -5089,7 +5082,7 @@ namespace CondenserLoopTowers { this->airFlowRateRatio = (AirFlowRate * this->NumCell) / this->HighSpeedAirFlowRate; } - void CoolingTower::calculateVariableSpeedTower(EnergyPlusData &state) + void CoolingTower::calculateVariableSpeedTower(EnergyPlusData &state, Real64 &MyLoad, bool RunFlag) { // SUBROUTINE INFORMATION: @@ -5209,12 +5202,10 @@ namespace CondenserLoopTowers { // Do not RETURN here if flow rate is less than MassFlowTolerance. Check basin heater and then RETURN. if (state.dataPlnt->PlantLoop(this->plantLoc.loopNum).LoopSide(this->plantLoc.loopSideNum).FlowLock == DataPlant::FlowLock::Unlocked) return; // TODO: WTF - // MassFlowTolerance is a parameter to indicate a no flow condition - if (this->WaterMassFlowRate <= DataBranchAirLoopPlant::MassFlowTolerance) { - CalcBasinHeaterPower( - state, this->BasinHeaterPowerFTempDiff, this->BasinHeaterSchedulePtr, this->BasinHeaterSetPointTemp, this->BasinHeaterPower); - return; - } + + bool returnFlagSet = false; + this->checkMassFlowAndLoad(state, MyLoad, RunFlag, returnFlagSet); + if (returnFlagSet) return; // loop to increment NumCell if we cannot meet the setpoint with the actual number of cells calculated above bool IncrNumCellFlag = true; @@ -5427,7 +5418,7 @@ namespace CondenserLoopTowers { } } - void CoolingTower::calculateMerkelVariableSpeedTower(EnergyPlusData &state, Real64 &MyLoad) + void CoolingTower::calculateMerkelVariableSpeedTower(EnergyPlusData &state, Real64 &MyLoad, bool RunFlag) { // SUBROUTINE INFORMATION: @@ -5512,15 +5503,8 @@ namespace CondenserLoopTowers { } WaterMassFlowRatePerCell = this->WaterMassFlowRate / this->NumCellOn; - // MassFlowTolerance is a parameter to indicate a no flow condition - if (this->WaterMassFlowRate <= DataBranchAirLoopPlant::MassFlowTolerance || (MyLoad > HVAC::SmallLoad)) { - // for multiple cells, we assume that it's a common bassin - CalcBasinHeaterPower( - state, this->BasinHeaterPowerFTempDiff, this->BasinHeaterSchedulePtr, this->BasinHeaterSetPointTemp, this->BasinHeaterPower); - return; - } - if (std::abs(MyLoad) <= HVAC::SmallLoad) { + if ((std::abs(MyLoad) <= HVAC::SmallLoad) || !RunFlag) { // tower doesn't need to do anything this->OutletWaterTemp = state.dataLoopNodes->Node(this->WaterInletNodeNum).Temp; this->FanPower = 0.0; @@ -5529,6 +5513,11 @@ namespace CondenserLoopTowers { CalcBasinHeaterPower( state, this->BasinHeaterPowerFTempDiff, this->BasinHeaterSchedulePtr, this->BasinHeaterSetPointTemp, this->BasinHeaterPower); return; + } else if (this->WaterMassFlowRate <= DataBranchAirLoopPlant::MassFlowTolerance || (MyLoad > HVAC::SmallLoad)) { + // for multiple cells, we assume that it's a common basin + CalcBasinHeaterPower( + state, this->BasinHeaterPowerFTempDiff, this->BasinHeaterSchedulePtr, this->BasinHeaterSetPointTemp, this->BasinHeaterPower); + return; } // first find free convection cooling rate @@ -6355,6 +6344,7 @@ namespace CondenserLoopTowers { // This subroutine is for passing results to the outlet water node. // set node information + PlantUtilities::SafeCopyPlantNode(state, this->WaterInletNodeNum, this->WaterOutletNodeNum); state.dataLoopNodes->Node(this->WaterOutletNodeNum).Temp = this->OutletWaterTemp; if (state.dataPlnt->PlantLoop(this->plantLoc.loopNum).LoopSide(this->plantLoc.loopSideNum).FlowLock == DataPlant::FlowLock::Unlocked || @@ -6463,6 +6453,27 @@ namespace CondenserLoopTowers { } } + void CoolingTower::checkMassFlowAndLoad(EnergyPlusData &state, Real64 const MyLoad, bool RunFlag, bool &returnFlagSet) + { + if ((MyLoad > -HVAC::SmallLoad) || !RunFlag) { + // tower doesn't need to do anything + this->OutletWaterTemp = state.dataLoopNodes->Node(this->WaterInletNodeNum).Temp; + this->FanPower = 0.0; + this->airFlowRateRatio = 0.0; + this->Qactual = 0.0; + this->WaterMassFlowRate = 0.0; + PlantUtilities::SetComponentFlowRate(state, this->WaterMassFlowRate, this->WaterInletNodeNum, this->WaterOutletNodeNum, this->plantLoc); + CalcBasinHeaterPower( + state, this->BasinHeaterPowerFTempDiff, this->BasinHeaterSchedulePtr, this->BasinHeaterSetPointTemp, this->BasinHeaterPower); + returnFlagSet = true; + } else if (this->WaterMassFlowRate <= DataBranchAirLoopPlant::MassFlowTolerance) { + // for multiple cells, we assume that it's a common basin + CalcBasinHeaterPower( + state, this->BasinHeaterPowerFTempDiff, this->BasinHeaterSchedulePtr, this->BasinHeaterSetPointTemp, this->BasinHeaterPower); + returnFlagSet = true; + } + } + } // namespace CondenserLoopTowers } // namespace EnergyPlus diff --git a/src/EnergyPlus/CondenserLoopTowers.hh b/src/EnergyPlus/CondenserLoopTowers.hh index d1f128674ce..f49d8b2ad8e 100644 --- a/src/EnergyPlus/CondenserLoopTowers.hh +++ b/src/EnergyPlus/CondenserLoopTowers.hh @@ -387,13 +387,13 @@ namespace CondenserLoopTowers { void SizeVSMerkelTower(EnergyPlusData &state); - void calculateSingleSpeedTower(EnergyPlusData &state); + void calculateSingleSpeedTower(EnergyPlusData &state, Real64 &MyLoad, bool RunFlag); - void calculateTwoSpeedTower(EnergyPlusData &state); + void calculateTwoSpeedTower(EnergyPlusData &state, Real64 &MyLoad, bool RunFlag); - void calculateMerkelVariableSpeedTower(EnergyPlusData &state, Real64 &MyLoad); + void calculateMerkelVariableSpeedTower(EnergyPlusData &state, Real64 &MyLoad, bool RunFlag); - void calculateVariableSpeedTower(EnergyPlusData &state); + void calculateVariableSpeedTower(EnergyPlusData &state, Real64 &MyLoad, bool RunFlag); Real64 calculateSimpleTowerOutletTemp(EnergyPlusData &state, Real64 waterMassFlowRate, Real64 AirFlowRate, Real64 UAdesign); @@ -427,6 +427,8 @@ namespace CondenserLoopTowers { void report(EnergyPlusData &state, bool RunFlag); + void checkMassFlowAndLoad(EnergyPlusData &state, Real64 MyLoad, bool RunFlag, bool &returnFlagSet); + static CoolingTower *factory(EnergyPlusData &state, std::string_view objectName); }; diff --git a/tst/EnergyPlus/unit/CondenserLoopTowers.unit.cc b/tst/EnergyPlus/unit/CondenserLoopTowers.unit.cc index 99aac875025..41e89f21271 100644 --- a/tst/EnergyPlus/unit/CondenserLoopTowers.unit.cc +++ b/tst/EnergyPlus/unit/CondenserLoopTowers.unit.cc @@ -515,7 +515,8 @@ TEST_F(EnergyPlusFixture, CondenserLoopTowers_MerkelNoCooling) state->dataCondenserLoopTowers->towers(1).SizeVSMerkelTower(*state); state->dataCondenserLoopTowers->towers(1).initialize(*state); Real64 MyLoad = 0.0; - state->dataCondenserLoopTowers->towers(1).calculateMerkelVariableSpeedTower(*state, MyLoad); + bool RunFlag = false; + state->dataCondenserLoopTowers->towers(1).calculateMerkelVariableSpeedTower(*state, MyLoad, RunFlag); state->dataCondenserLoopTowers->towers(1).update(*state); state->dataCondenserLoopTowers->towers(1).report(*state, true); @@ -904,10 +905,12 @@ TEST_F(EnergyPlusFixture, CondenserLoopTowers_SingleSpeedSizing) SimulationManager::SetupSimulation(*state, ErrorsFound); CondenserLoopTowers::GetTowerInput(*state); + Real64 MyLoad = 0.0; + bool RunFlag = false; state->dataCondenserLoopTowers->towers(1).initialize(*state); state->dataCondenserLoopTowers->towers(1).SizeTower(*state); state->dataCondenserLoopTowers->towers(1).initialize(*state); - state->dataCondenserLoopTowers->towers(1).calculateSingleSpeedTower(*state); + state->dataCondenserLoopTowers->towers(1).calculateSingleSpeedTower(*state, MyLoad, RunFlag); state->dataCondenserLoopTowers->towers(1).update(*state); state->dataCondenserLoopTowers->towers(1).report(*state, true); @@ -926,9 +929,8 @@ TEST_F(EnergyPlusFixture, CondenserLoopTowers_SingleSpeedSizing) if (outletNode != state->dataLoopNodes->NodeID.end()) { outletNodeIndex = std::distance(state->dataLoopNodes->NodeID.begin(), outletNode); } - // TODO: FIXME: This is failing. Actual is -10.409381032746095, expected is 30. - EXPECT_GT(state->dataLoopNodes->Node(inletNodeIndex).Temp, 30.0); // inlet node temperature - EXPECT_DOUBLE_EQ(30.0, state->dataLoopNodes->Node(outletNodeIndex).Temp); // outlet node temperature + EXPECT_GT(state->dataLoopNodes->Node(inletNodeIndex).Temp, 30.0); // inlet node temperature + EXPECT_DOUBLE_EQ(state->dataLoopNodes->Node(inletNodeIndex).Temp, state->dataLoopNodes->Node(outletNodeIndex).Temp); // outlet node temperature // input not needed for sizing (WasAutoSized = false) using NominalCapacity method but this variable should still size EXPECT_FALSE(state->dataCondenserLoopTowers->towers(1).HighSpeedTowerUAWasAutoSized); @@ -3967,7 +3969,9 @@ TEST_F(EnergyPlusFixture, VSCoolingTowers_WaterOutletTempTest) state->dataPlnt->PlantLoop(VSTower.plantLoc.loopNum).LoopSide(VSTower.plantLoc.loopSideNum).TempSetPoint = 30.0; VSTower.WaterMassFlowRate = VSTower.DesWaterMassFlowRate * WaterFlowRateRatio; - VSTower.calculateVariableSpeedTower(*state); + Real64 myLoad = -1000.0; + bool RunFlag = true; + VSTower.calculateVariableSpeedTower(*state, myLoad, RunFlag); EXPECT_DOUBLE_EQ(30.0, VSTower.OutletWaterTemp); EXPECT_DOUBLE_EQ(1.0, VSTower.FanCyclingRatio); Real64 TowerOutletWaterTemp = VSTower.calculateVariableTowerOutletTemp(*state, WaterFlowRateRatio, VSTower.airFlowRateRatio, AirWetBulbTemp); @@ -3985,7 +3989,7 @@ TEST_F(EnergyPlusFixture, VSCoolingTowers_WaterOutletTempTest) VSTower.AirWetBulb = state->dataEnvrn->OutWetBulbTemp; VSTower.AirHumRat = state->dataEnvrn->OutHumRat; - VSTower.calculateVariableSpeedTower(*state); + VSTower.calculateVariableSpeedTower(*state, myLoad, RunFlag); EXPECT_DOUBLE_EQ(30.0, VSTower.OutletWaterTemp); EXPECT_NEAR(0.5424, VSTower.FanCyclingRatio, 0.0001); // outside air condition is favorable that fan only needs to cycle at min flow to meet the setpoint @@ -4305,4 +4309,81 @@ TEST_F(EnergyPlusFixture, CondenserLoopTowers_CalculateVariableTowerOutletTemp) EXPECT_NEAR(tower.WaterTemp - 22.2222, tOutlet, 0.01); } +TEST_F(EnergyPlusFixture, CondenserLoopTowers_checkMassFlowAndLoadTest) +{ + bool flagToReturn; + bool runFlag; + Real64 myLoad; + Real64 constexpr allowedTolerance = 0.0001; + Real64 expectedPower; + Real64 expectedTemp; + + state->dataCondenserLoopTowers->towers.allocate(1); + auto &tower = state->dataCondenserLoopTowers->towers(1); + state->dataPlnt->PlantLoop.allocate(1); + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch.allocate(1); + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp.allocate(1); + state->dataLoopNodes->Node.allocate(2); + tower.WaterInletNodeNum = 1; + tower.WaterOutletNodeNum = 2; + tower.plantLoc.loopNum = 1; + tower.plantLoc.loopSideNum = DataPlant::LoopSideLocation::Supply; + tower.plantLoc.branchNum = 1; + tower.plantLoc.compNum = 1; + + // Test 1: Mass flow rate is low but myLoad is ok--flag should be set to true + flagToReturn = false; + runFlag = true; + myLoad = -1000.0; + state->dataEnvrn->OutDryBulbTemp = 27.0; + tower.WaterMassFlowRate = 0.0; + tower.BasinHeaterPowerFTempDiff = 1.0; + tower.BasinHeaterSchedulePtr = 0; + tower.BasinHeaterSetPointTemp = 26.0; + tower.BasinHeaterPower = 1.0; + expectedPower = 0.0; + tower.checkMassFlowAndLoad(*state, myLoad, runFlag, flagToReturn); + EXPECT_TRUE(flagToReturn); + EXPECT_NEAR(expectedPower, tower.BasinHeaterPower, allowedTolerance); + + // Test 2: Mass flow rate is ok but myLoad is zero--flag should be set to true + flagToReturn = false; + runFlag = false; + myLoad = 0.0; + state->dataEnvrn->OutDryBulbTemp = 27.0; + tower.WaterMassFlowRate = 0.5; + tower.BasinHeaterPowerFTempDiff = 1.0; + tower.BasinHeaterSchedulePtr = 0; + tower.BasinHeaterSetPointTemp = 25.0; + tower.BasinHeaterPower = 2.0; + tower.FanPower = 1.0; + tower.airFlowRateRatio = 1.0; + tower.Qactual = 1.0; + expectedPower = 0.0; + expectedTemp = 23.0; + state->dataLoopNodes->Node(tower.WaterInletNodeNum).Temp = 23.0; + tower.checkMassFlowAndLoad(*state, myLoad, runFlag, flagToReturn); + EXPECT_TRUE(flagToReturn); + EXPECT_NEAR(expectedPower, tower.BasinHeaterPower, allowedTolerance); + EXPECT_NEAR(expectedTemp, tower.OutletWaterTemp, allowedTolerance); + EXPECT_NEAR(0.0, tower.FanPower, allowedTolerance); + EXPECT_NEAR(0.0, tower.airFlowRateRatio, allowedTolerance); + EXPECT_NEAR(0.0, tower.Qactual, allowedTolerance); + + // Test 3: Mass flow rate and myLoad are both ok--nothing changes, power does not get calculated here + flagToReturn = false; + runFlag = true; + myLoad = -1000.0; + state->dataEnvrn->OutDryBulbTemp = 27.0; + tower.WaterMassFlowRate = 0.5; + tower.BasinHeaterPowerFTempDiff = 1.0; + tower.BasinHeaterSchedulePtr = 0; + tower.BasinHeaterSetPointTemp = 25.0; + tower.BasinHeaterPower = 3.0; + expectedPower = 3.0; + tower.checkMassFlowAndLoad(*state, myLoad, runFlag, flagToReturn); + EXPECT_FALSE(flagToReturn); + EXPECT_NEAR(expectedPower, tower.BasinHeaterPower, allowedTolerance); +} + } // namespace EnergyPlus