Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Correction of Condensers Not Operating Based on Operation Scheme #10653

Merged
merged 7 commits into from
Aug 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 46 additions & 35 deletions src/EnergyPlus/CondenserLoopTowers.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand Down Expand Up @@ -4535,7 +4535,7 @@ namespace CondenserLoopTowers {
}
} // namespace CondenserLoopTowers

void CoolingTower::calculateSingleSpeedTower(EnergyPlusData &state)
void CoolingTower::calculateSingleSpeedTower(EnergyPlusData &state, Real64 &MyLoad, bool RunFlag)
{

// SUBROUTINE INFORMATION:
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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;
Copy link
Contributor

@rraustad rraustad Aug 14, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you made this function a bool it would just be if (this->checkMassFlowAndLoad(state, MyLoad, RunFlag) return;

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I was concerned that a function would then be setting other parameters and calling other routines, not just getting a bool result. I felt like that was a violation of some standard, but I'll admit that I don't know whether that is an E+ standard or something that I simply picked up over the years that maybe isn't completely true. @Myoldmopar, any thoughts on this?


// loop to increment NumCell if we cannot meet the setpoint with the actual number of cells calculated above
bool IncrNumCellFlag = true;
Expand Down Expand Up @@ -5427,7 +5418,7 @@ namespace CondenserLoopTowers {
}
}

void CoolingTower::calculateMerkelVariableSpeedTower(EnergyPlusData &state, Real64 &MyLoad)
void CoolingTower::calculateMerkelVariableSpeedTower(EnergyPlusData &state, Real64 &MyLoad, bool RunFlag)
{

// SUBROUTINE INFORMATION:
Expand Down Expand Up @@ -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) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am wondering if just !RunFlag would meet the intent. I would think RunFlag would be false if (std::abs(MyLoad) <= HVAC::SmallLoad) but then I am not sure.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wasn't sure either and given that the Merkel model already used MyLoad, I felt like having both would be "safer". My guess is that both of these flags will probably trip this IF at the same time, but I didn't see anything wrong with doing it this way which seemed like it would catch all conditions.

// tower doesn't need to do anything
this->OutletWaterTemp = state.dataLoopNodes->Node(this->WaterInletNodeNum).Temp;
this->FanPower = 0.0;
Expand All @@ -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)) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is interesting. If this conditional is checked then the tower is running. I can't be sure if the water mass flow rate could really be this small if the tower is running but then you have the plant resolver making that determination. Also not sure of the load check.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I noticed that there was a basin heater check in checkMassFlowAndLoad similar to this. Does that make this new code unnecessary?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I left this alone because it was the one that was identified as working properly before the defect was logged. I'll admit, I did not see why it did this check (seemed confusing). However, I left it alone in the Merkel model because I didn't see a compelling reason to fix something that wasn't broken. You'll notice that the code here is slightly different than the code in checkMassFlowAndLoad--intentionally so because I trust the logic in checkMassFlowAndLoad and it makes more logical sense. So, I don't think the new code is unnecessary. It was somewhat based on what was done here in the Merkel model, with changes to the logic (by me and then improved with your suggestions).

// 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
Expand Down Expand Up @@ -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);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@RKStrand hope you don't mind me jumping in here. When components are in series, this is needed to pass data down the branch to the next component.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not at all, @EnergyArchmage! Thanks for making that change.

state.dataLoopNodes->Node(this->WaterOutletNodeNum).Temp = this->OutletWaterTemp;

if (state.dataPlnt->PlantLoop(this->plantLoc.loopNum).LoopSide(this->plantLoc.loopSideNum).FlowLock == DataPlant::FlowLock::Unlocked ||
Expand Down Expand Up @@ -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
10 changes: 6 additions & 4 deletions src/EnergyPlus/CondenserLoopTowers.hh
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down Expand Up @@ -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);
};

Expand Down
95 changes: 88 additions & 7 deletions tst/EnergyPlus/unit/CondenserLoopTowers.unit.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down Expand Up @@ -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);

Expand All @@ -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);
Expand Down Expand Up @@ -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);
Expand All @@ -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
Expand Down Expand Up @@ -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
Loading