From 3141020c8e5ebba3953fbb85218bf2bf302a3152 Mon Sep 17 00:00:00 2001 From: Rick Strand Date: Tue, 30 Jul 2024 15:11:38 -0500 Subject: [PATCH] 10065 Final Changes and Unit Tests This commit includes unit tests, a couple of fixes to the code that the unit tests uncovered, and some code simplification. This is PR candidate. --- design/FY2024/chiller_heater_part_load_fix.md | 10 +- src/EnergyPlus/PlantCentralGSHP.cc | 33 +- src/EnergyPlus/PlantCentralGSHP.hh | 2 +- tst/EnergyPlus/unit/PlantCentralGSHP.unit.cc | 407 ++++++++++++++++++ 4 files changed, 430 insertions(+), 22 deletions(-) diff --git a/design/FY2024/chiller_heater_part_load_fix.md b/design/FY2024/chiller_heater_part_load_fix.md index 649e86d0620..bb966f0f756 100644 --- a/design/FY2024/chiller_heater_part_load_fix.md +++ b/design/FY2024/chiller_heater_part_load_fix.md @@ -4,7 +4,7 @@ DEFECT: Fix for Chiller Heater Always Assuming Evaporator is at Full Load **Rick Strand, University of Illinois at Urbana-Champaign** - Original Date: July 22, 2024 - - Revision Date + - Revision Date: July 30, 2024 ## Justification for New Feature ## @@ -13,7 +13,7 @@ The current heater mode portion of the chiller heater model in PlantCentralGSHP. ## E-mail and Conference Call Conclusions ## -This is the first time this will be discussed via email or in a Technicalities Call. +July 24, 2024: Discussed this on the technicalities call. Decision was made to not implement an iteration strategy but to simply make an approximation of the PLR from the condenser load and then multiple full load evaporator load, compressor power, and false loading by that PLR. Not ideal, but given all of the suspected problems in this model, it was decided to not invest too heavily in this now and turn this into a potential development topic in the future. ## Overview ## @@ -23,7 +23,7 @@ The simlation flow in the heater portion of the model is summarized as follows. The problem here is that the evaporator load and the compressor power are still at full load and are never adjusted when the condenser load gets reduced because the heating load does not require full load. This is the source of the error--evaporator load and compressor power never change in heating mode regardless of the actual part load ratio based on the condenser load. PLR simply stays at near 100%. This is not correct and leads to over-estimation of both the evaporator load and the compressor power consumption. -## Approach ## +## Original Approach ## Note: before the actual fix takes place, it was decided to make a code improvement pass through the current chiller heater model. This has already taken place and has been merged into develop. The point was to make the code re-usable within the chiller heater model but it also realized some improvements in the cooling mode subroutine as well. The changes took several different code sections and turned them into smaller subroutines. The heating mode code is now much easier to follow, reducing the size of the routine by a factor of more than 3 (based on printouts of the routine before and after restructuring). The real benefit will be seen when the problem is fixed as the algorithm should stay fairly compact and easy to follow (hopefully). @@ -68,6 +68,10 @@ Step 8: Call adjustChillerHeaterFlowTemp to adjust flow rate and temperature if At this point, a new QCondenser has been calculated so the iteration cycle is done. No additional code is needed after the iteration cycle as it should just be able to pick up where it left off as it currently does. +## Modified Approach ## + +During the technicalities call, it was suggested that rather than iterating, we should just approximate the PLR from the condenser load and then multiply evaporator load and compressor power by this PLR. The false load was also factored in this way though in this case it was probably zero at full load anyway. Other problems in the algorithm were also fixed along the way. No guarantees that this model is now 100% bug free but it should be improved. + ## Testing/Validation/Data Sources ## Testing will be done using the existing user input file that shows the problem. Comparisons will be made between develop and the new version to establish that the results have changed after the fix has been implemented and that the new output makes sense. diff --git a/src/EnergyPlus/PlantCentralGSHP.cc b/src/EnergyPlus/PlantCentralGSHP.cc index 7e549916deb..27ed35a74b2 100644 --- a/src/EnergyPlus/PlantCentralGSHP.cc +++ b/src/EnergyPlus/PlantCentralGSHP.cc @@ -2692,7 +2692,7 @@ void WrapperSpecs::checkEvapOutletTemp(EnergyPlusData &state, Real64 const lowTempLimitEout, Real64 const evapInletTemp, Real64 &qEvaporator, - Real64 &evapMassFlowRate, + Real64 const evapMassFlowRate, Real64 const Cp) { // Check evaporator temperature low limit and adjust capacity if needed @@ -2709,10 +2709,9 @@ void WrapperSpecs::checkEvapOutletTemp(EnergyPlusData &state, // Check if the outlet temperature exceeds the node minimum temperature and adjust capacity if needed if (evapOutletTemp < this->ChillerHeater(numChillerHeater).EvapOutletNode.TempMin) { - if ((this->ChillerHeater(numChillerHeater).EvapInletNode.Temp - this->ChillerHeater(numChillerHeater).EvapOutletNode.TempMin) > - DataPlant::DeltaTempTol) { + if ((evapInletTemp - this->ChillerHeater(numChillerHeater).EvapOutletNode.TempMin) > DataPlant::DeltaTempTol) { evapOutletTemp = this->ChillerHeater(numChillerHeater).EvapOutletNode.TempMin; - Real64 evapDeltaTemp = this->ChillerHeater(numChillerHeater).EvapOutletNode.TempMin - evapOutletTemp; + Real64 evapDeltaTemp = evapInletTemp - evapOutletTemp; qEvaporator = evapMassFlowRate * Cp * evapDeltaTemp; } else { qEvaporator = 0.0; @@ -2730,29 +2729,27 @@ void WrapperSpecs::calcPLRAndCyclingRatio(EnergyPlusData &state, Real64 &frac) { // Calculate PLR (actualPartLoadRatio) based on evaporator load and available capacity, factoring in max PLR - if (availChillerCap > 0.0) { - actualPartLoadRatio = max(0.0, min((qEvaporator / availChillerCap), maxPartLoadRatio)); + if (availChillerCap <= 0.0) { + actualPartLoadRatio = 0; + frac = 1.0; } else { - actualPartLoadRatio = 0.0; + actualPartLoadRatio = max(0.0, min((qEvaporator / availChillerCap), maxPartLoadRatio)); + // If chiller cycles below minimum part load ratio, frac = amount of time chiller is ON during this time step + if (minPartLoadRatio > 0.0) { + frac = min(1.0, (actualPartLoadRatio / minPartLoadRatio)); + } else { + frac = 1.0; + } + actualPartLoadRatio = max(actualPartLoadRatio, minPartLoadRatio); } - // Chiller cycles below minimum part load ratio, frac = amount of time chiller is ON during this time step - if (actualPartLoadRatio < minPartLoadRatio) frac = min(1.0, (actualPartLoadRatio / minPartLoadRatio)); - if (frac <= 0.0) frac = 1.0; // CR 9303 COP reporting issue, it should be greater than zero in this routine state.dataPlantCentralGSHP->ChillerCyclingRatio = frac; - // Chiller is false loading below PLR = minimum unloading ratio, find PLR used for energy calculation - if (availChillerCap > 0.0) { - actualPartLoadRatio = max(actualPartLoadRatio, minPartLoadRatio); - } else { - actualPartLoadRatio = 0.0; - } - // Evaporator part load ratio state.dataPlantCentralGSHP->ChillerPartLoadRatio = actualPartLoadRatio; // Calculate the load due to false loading on chiller over and above water side load - state.dataPlantCentralGSHP->ChillerFalseLoadRate = (availChillerCap * actualPartLoadRatio * frac) - qEvaporator; + state.dataPlantCentralGSHP->ChillerFalseLoadRate = (availChillerCap * minPartLoadRatio) - qEvaporator; if (state.dataPlantCentralGSHP->ChillerFalseLoadRate < HVAC::SmallLoad) { state.dataPlantCentralGSHP->ChillerFalseLoadRate = 0.0; } diff --git a/src/EnergyPlus/PlantCentralGSHP.hh b/src/EnergyPlus/PlantCentralGSHP.hh index 30c937201e7..f359393f1ec 100644 --- a/src/EnergyPlus/PlantCentralGSHP.hh +++ b/src/EnergyPlus/PlantCentralGSHP.hh @@ -436,7 +436,7 @@ namespace PlantCentralGSHP { Real64 const lowTempLimitEout, Real64 evapInletTemp, Real64 &qEvaporator, - Real64 &evapMassFlowRate, + Real64 const evapMassFlowRate, Real64 const Cp); void calcPLRAndCyclingRatio(EnergyPlusData &state, diff --git a/tst/EnergyPlus/unit/PlantCentralGSHP.unit.cc b/tst/EnergyPlus/unit/PlantCentralGSHP.unit.cc index 4cdfdf1dea1..ca45c05d524 100644 --- a/tst/EnergyPlus/unit/PlantCentralGSHP.unit.cc +++ b/tst/EnergyPlus/unit/PlantCentralGSHP.unit.cc @@ -363,3 +363,410 @@ TEST_F(EnergyPlusFixture, Test_CentralHeatPumpSystem_Control_Schedule_fix) // verify that under this scenario of not finding a schedule match, ScheduleAlwaysOn is the treated default EXPECT_EQ(state->dataPlantCentralGSHP->Wrapper(1).WrapperComp(1).CHSchedPtr, ScheduleManager::ScheduleAlwaysOn); } + +TEST_F(EnergyPlusFixture, Test_CentralHeatPumpSystem_adjustChillerHeaterCondFlowTemp) +{ + state->dataPlantCentralGSHP->Wrapper.allocate(1); + state->dataPlantCentralGSHP->Wrapper(1).WrapperComp.allocate(1); + state->dataPlantCentralGSHP->Wrapper(1).ChillerHeater.allocate(1); + auto &thisWrap = state->dataPlantCentralGSHP->Wrapper(1); + auto &thisCH = thisWrap.ChillerHeater(1); + state->dataPlnt->PlantLoop.allocate(1); + state->dataPlnt->PlantLoop(1).FluidName = "WATER"; + state->dataPlnt->PlantLoop(1).FluidIndex = FluidProperties::GetGlycolNum(*state, state->dataPlnt->PlantLoop(1).FluidName); + thisWrap.HWPlantLoc.loopNum = 1; + FluidProperties::GetFluidPropertiesData(*state); + + Real64 qCondenser; + Real64 condMassFlowRate; + Real64 condOutletTemp; + Real64 condInletTemp; + Real64 condDeltaTemp; + Real64 expCondenser; + Real64 expMassFlowRate; + Real64 expOutletTemp; + Real64 constexpr allowedTolerance = 0.0001; + + // Test 1: Variable Flow--qCondenser is less than what the conditions say (mass flow reduced, nothing else changes) + qCondenser = 1000.0; + condMassFlowRate = 1.0; + condOutletTemp = 60.0; + condInletTemp = 59.0; + condDeltaTemp = 1.0; + thisWrap.VariableFlowCH = true; + expCondenser = 1000.0; + expMassFlowRate = 0.23897; + expOutletTemp = 60.0; + thisWrap.adjustChillerHeaterCondFlowTemp(*state, qCondenser, condMassFlowRate, condOutletTemp, condInletTemp, condDeltaTemp); + EXPECT_NEAR(qCondenser, expCondenser, allowedTolerance); + EXPECT_NEAR(condMassFlowRate, expMassFlowRate, allowedTolerance); + EXPECT_NEAR(condOutletTemp, expOutletTemp, allowedTolerance); + + // Test 2: Variable Flow--qCondenser is greater than what conditions say (load reduced, nothing else changes) + qCondenser = 5000.0; + condMassFlowRate = 1.0; + condOutletTemp = 60.0; + condInletTemp = 59.0; + condDeltaTemp = 1.0; + thisWrap.VariableFlowCH = true; + expCondenser = 4184.6; + expMassFlowRate = 1.0; + expOutletTemp = 60.0; + thisWrap.adjustChillerHeaterCondFlowTemp(*state, qCondenser, condMassFlowRate, condOutletTemp, condInletTemp, condDeltaTemp); + EXPECT_NEAR(qCondenser, expCondenser, allowedTolerance); + EXPECT_NEAR(condMassFlowRate, expMassFlowRate, allowedTolerance); + EXPECT_NEAR(condOutletTemp, expOutletTemp, allowedTolerance); + + // Test 3: Constant Flow--Outlet Temp greater than calculated outlet temp (outlet temp changes, nothing else changes) + qCondenser = 1000.0; + condMassFlowRate = 1.0; + condOutletTemp = 60.0; + condInletTemp = 59.0; + condDeltaTemp = 1.0; + thisWrap.VariableFlowCH = false; + expCondenser = 1000.0; + expMassFlowRate = 1.0; + expOutletTemp = 59.23897; + thisWrap.adjustChillerHeaterCondFlowTemp(*state, qCondenser, condMassFlowRate, condOutletTemp, condInletTemp, condDeltaTemp); + EXPECT_NEAR(qCondenser, expCondenser, allowedTolerance); + EXPECT_NEAR(condMassFlowRate, expMassFlowRate, allowedTolerance); + EXPECT_NEAR(condOutletTemp, expOutletTemp, allowedTolerance); + + // Test 4: Constant Flow--Outlet Temp less than calculated outlet temp (load changes, nothing else changes) + qCondenser = 8369.2; + condMassFlowRate = 1.0; + condOutletTemp = 60.0; + condInletTemp = 59.0; + condDeltaTemp = 1.0; + thisWrap.VariableFlowCH = false; + expCondenser = 4184.6; + expMassFlowRate = 1.0; + expOutletTemp = 60.0; + thisWrap.adjustChillerHeaterCondFlowTemp(*state, qCondenser, condMassFlowRate, condOutletTemp, condInletTemp, condDeltaTemp); + EXPECT_NEAR(qCondenser, expCondenser, allowedTolerance); + EXPECT_NEAR(condMassFlowRate, expMassFlowRate, allowedTolerance); + EXPECT_NEAR(condOutletTemp, expOutletTemp, allowedTolerance); +} + +TEST_F(EnergyPlusFixture, Test_CentralHeatPumpSystem_adjustChillerHeaterEvapFlowTemp) +{ + state->dataPlantCentralGSHP->Wrapper.allocate(1); + state->dataPlantCentralGSHP->Wrapper(1).WrapperComp.allocate(1); + state->dataPlantCentralGSHP->Wrapper(1).ChillerHeater.allocate(1); + auto &thisWrap = state->dataPlantCentralGSHP->Wrapper(1); + auto &thisCH = thisWrap.ChillerHeater(1); + state->dataPlnt->PlantLoop.allocate(1); + state->dataPlnt->PlantLoop(1).FluidName = "WATER"; + state->dataPlnt->PlantLoop(1).FluidIndex = FluidProperties::GetGlycolNum(*state, state->dataPlnt->PlantLoop(1).FluidName); + thisWrap.HWPlantLoc.loopNum = 1; + FluidProperties::GetFluidPropertiesData(*state); + + Real64 qEvaporator; + Real64 evapMassFlowRate; + Real64 evapOutletTemp; + Real64 evapInletTemp; + Real64 expMassFlowRate; + Real64 expOutletTemp; + Real64 constexpr allowedTolerance = 0.0001; + + // Test 1a: qEvaporator is too low, flow rate set to zero and outlet temp set to inlet temp + qEvaporator = 0.00001; + evapMassFlowRate = 1.0; + evapOutletTemp = 34.0; + evapInletTemp = 35.0; + thisWrap.VariableFlowCH = false; + expMassFlowRate = 0.0; + expOutletTemp = 35.0; + thisWrap.adjustChillerHeaterEvapFlowTemp(*state, qEvaporator, evapMassFlowRate, evapOutletTemp, evapInletTemp); + EXPECT_NEAR(evapMassFlowRate, expMassFlowRate, allowedTolerance); + EXPECT_NEAR(evapOutletTemp, expOutletTemp, allowedTolerance); + + // Test 1b: delta T is zero, load and flow rate set to zero and outlet temp set to inlet temp + qEvaporator = 1000.0; + evapMassFlowRate = 1.0; + evapOutletTemp = 35.0; + evapInletTemp = 35.0; + thisWrap.VariableFlowCH = false; + expMassFlowRate = 0.0; + expOutletTemp = 35.0; + thisWrap.adjustChillerHeaterEvapFlowTemp(*state, qEvaporator, evapMassFlowRate, evapOutletTemp, evapInletTemp); + EXPECT_NEAR(evapMassFlowRate, expMassFlowRate, allowedTolerance); + EXPECT_NEAR(evapOutletTemp, expOutletTemp, allowedTolerance); + + // Test 2a: Variable Flow, Load higher than max flow rate passed in, keep flow rate and adjust outlet temp + qEvaporator = 5000.0; + evapMassFlowRate = 1.0; + evapOutletTemp = 34.0; + evapInletTemp = 35.0; + thisWrap.VariableFlowCH = true; + expMassFlowRate = 1.0; + expOutletTemp = 33.80383; + thisWrap.adjustChillerHeaterEvapFlowTemp(*state, qEvaporator, evapMassFlowRate, evapOutletTemp, evapInletTemp); + EXPECT_NEAR(evapMassFlowRate, expMassFlowRate, allowedTolerance); + EXPECT_NEAR(evapOutletTemp, expOutletTemp, allowedTolerance); + + // Test 2b: Variable Flow, Load lower than max flow rate passed in, adjust flow rate and keep outlet temp + qEvaporator = 1045.0; + evapMassFlowRate = 1.0; + evapOutletTemp = 34.0; + evapInletTemp = 35.0; + thisWrap.VariableFlowCH = true; + expMassFlowRate = 0.25; + expOutletTemp = 34.0; + thisWrap.adjustChillerHeaterEvapFlowTemp(*state, qEvaporator, evapMassFlowRate, evapOutletTemp, evapInletTemp); + EXPECT_NEAR(evapMassFlowRate, expMassFlowRate, allowedTolerance); + EXPECT_NEAR(evapOutletTemp, expOutletTemp, allowedTolerance); + + // Test 3: Constant Flow--adjust outlet temperature + qEvaporator = 2090.0; + evapMassFlowRate = 1.0; + evapOutletTemp = 34.0; + evapInletTemp = 35.0; + thisWrap.VariableFlowCH = false; + expMassFlowRate = 1.0; + expOutletTemp = 34.5; + thisWrap.adjustChillerHeaterEvapFlowTemp(*state, qEvaporator, evapMassFlowRate, evapOutletTemp, evapInletTemp); + EXPECT_NEAR(evapMassFlowRate, expMassFlowRate, allowedTolerance); + EXPECT_NEAR(evapOutletTemp, expOutletTemp, allowedTolerance); +} + +TEST_F(EnergyPlusFixture, Test_CentralHeatPumpSystem_setChillerHeaterCondTemp) +{ + state->dataPlantCentralGSHP->Wrapper.allocate(1); + state->dataPlantCentralGSHP->Wrapper(1).WrapperComp.allocate(1); + state->dataPlantCentralGSHP->Wrapper(1).ChillerHeater.allocate(1); + auto &thisWrap = state->dataPlantCentralGSHP->Wrapper(1); + auto &thisCH = thisWrap.ChillerHeater(1); + + Real64 functionAnswer; + Real64 expectedAnswer; + Real64 constexpr allowedTolerance = 0.001; + Real64 condEnterTemp; + Real64 condLeaveTemp; + int chillNum = 1; + + // Test 1: get the condenser entering temperature + functionAnswer = 0.0; + thisCH.CondMode = EnergyPlus::PlantCentralGSHP::CondenserModeTemperature::EnteringCondenser; + condEnterTemp = 55.5; + condLeaveTemp = 44.4; + expectedAnswer = 55.5; + functionAnswer = thisWrap.setChillerHeaterCondTemp(*state, chillNum, condEnterTemp, condLeaveTemp); + EXPECT_NEAR(functionAnswer, expectedAnswer, allowedTolerance); + + // Test 2: get the condenser leaving temperature + functionAnswer = 0.0; + thisCH.CondMode = EnergyPlus::PlantCentralGSHP::CondenserModeTemperature::LeavingCondenser; + condEnterTemp = 55.5; + condLeaveTemp = 44.4; + expectedAnswer = 44.4; + functionAnswer = thisWrap.setChillerHeaterCondTemp(*state, chillNum, condEnterTemp, condLeaveTemp); + EXPECT_NEAR(functionAnswer, expectedAnswer, allowedTolerance); +} + +TEST_F(EnergyPlusFixture, Test_CentralHeatPumpSystem_checkEvapOutletTemp) +{ + state->dataPlantCentralGSHP->Wrapper.allocate(1); + state->dataPlantCentralGSHP->Wrapper(1).WrapperComp.allocate(1); + state->dataPlantCentralGSHP->Wrapper(1).ChillerHeater.allocate(1); + auto &thisWrap = state->dataPlantCentralGSHP->Wrapper(1); + auto &thisCH = thisWrap.ChillerHeater(1); + + int chNum = 1; + Real64 evapOutletTemp; + Real64 lowTempLimitEout; + Real64 evapInletTemp; + Real64 qEvaporator; + Real64 evapMassFlowRate; + Real64 Cp = 4000.0; + Real64 expQEvap; + Real64 expTout; + Real64 constexpr allowedTolerance = 0.0001; + + // Test 1a: Evaporator outlet temperature lower the evaporator outlet low temperature limit, adjust outlet and load + thisCH.EvapOutletNode.TempMin = 5.0; + evapInletTemp = 10.0; + evapOutletTemp = 8.0; + lowTempLimitEout = 9.0; + qEvaporator = 4000.0; + evapMassFlowRate = 0.5; + expQEvap = 2000.0; + expTout = 9.0; + thisWrap.checkEvapOutletTemp(*state, chNum, evapOutletTemp, lowTempLimitEout, evapInletTemp, qEvaporator, evapMassFlowRate, Cp); + EXPECT_NEAR(qEvaporator, expQEvap, allowedTolerance); + EXPECT_NEAR(evapOutletTemp, expTout, allowedTolerance); + + // Test 1b: Evaporator outlet temperature lower the evaporator outlet low temperature limit and inlet temp at or below lowTempLimitEout, + // zero flow and set outlet temperature to inlet temperature + thisCH.EvapOutletNode.TempMin = 5.0; + evapInletTemp = 8.0; + evapOutletTemp = 7.0; + lowTempLimitEout = 9.0; + qEvaporator = 2000.0; + evapMassFlowRate = 0.5; + expQEvap = 0.0; + expTout = 8.0; + thisWrap.checkEvapOutletTemp(*state, chNum, evapOutletTemp, lowTempLimitEout, evapInletTemp, qEvaporator, evapMassFlowRate, Cp); + EXPECT_NEAR(qEvaporator, expQEvap, allowedTolerance); + EXPECT_NEAR(evapOutletTemp, expTout, allowedTolerance); + + // Test 2a: Evaporator outlet temperature lower the node minimum temperature limit, adjust outlet and load + thisCH.EvapOutletNode.TempMin = 9.0; + evapInletTemp = 10.0; + evapOutletTemp = 8.0; + lowTempLimitEout = 5.0; + qEvaporator = 4000.0; + evapMassFlowRate = 0.5; + expQEvap = 2000.0; + expTout = 9.0; + thisWrap.checkEvapOutletTemp(*state, chNum, evapOutletTemp, lowTempLimitEout, evapInletTemp, qEvaporator, evapMassFlowRate, Cp); + EXPECT_NEAR(qEvaporator, expQEvap, allowedTolerance); + EXPECT_NEAR(evapOutletTemp, expTout, allowedTolerance); + + // Test 2b: Evaporator outlet temperature lower the node minimum temperature limit and inlet temp at or below node temperature limt, + // zero flow and set outlet temperature to inlet temperature + thisCH.EvapOutletNode.TempMin = 9.0; + evapInletTemp = 8.0; + evapOutletTemp = 7.0; + lowTempLimitEout = 5.0; + qEvaporator = 2000.0; + evapMassFlowRate = 0.5; + expQEvap = 0.0; + expTout = 8.0; + thisWrap.checkEvapOutletTemp(*state, chNum, evapOutletTemp, lowTempLimitEout, evapInletTemp, qEvaporator, evapMassFlowRate, Cp); + EXPECT_NEAR(qEvaporator, expQEvap, allowedTolerance); + EXPECT_NEAR(evapOutletTemp, expTout, allowedTolerance); + + // Test 3: Everything is fine, no changes to anything + thisCH.EvapOutletNode.TempMin = 5.0; + evapInletTemp = 8.0; + evapOutletTemp = 6.0; + lowTempLimitEout = 5.0; + qEvaporator = 4000.0; + evapMassFlowRate = 0.5; + expQEvap = 4000.0; + expTout = 6.0; + thisWrap.checkEvapOutletTemp(*state, chNum, evapOutletTemp, lowTempLimitEout, evapInletTemp, qEvaporator, evapMassFlowRate, Cp); + EXPECT_NEAR(qEvaporator, expQEvap, allowedTolerance); + EXPECT_NEAR(evapOutletTemp, expTout, allowedTolerance); +} + +TEST_F(EnergyPlusFixture, Test_CentralHeatPumpSystem_calcPLRAndCyclingRatio) +{ + state->dataPlantCentralGSHP->Wrapper.allocate(1); + state->dataPlantCentralGSHP->Wrapper(1).WrapperComp.allocate(1); + state->dataPlantCentralGSHP->Wrapper(1).ChillerHeater.allocate(1); + auto &thisWrap = state->dataPlantCentralGSHP->Wrapper(1); + auto &thisCH = thisWrap.ChillerHeater(1); + + Real64 availChillerCap; + Real64 actualPartLoadRatio; + Real64 minPartLoadRatio; + Real64 maxPartLoadRatio; + Real64 qEvaporator; + Real64 frac; + Real64 expPLR; + Real64 expFrac; + Real64 expFalseLoad; + Real64 constexpr allowedTolerance = 0.0001; + + // Test 1: available chiller capacity less than zero (PLR should be zero, frac should be 1.0) + availChillerCap = -10000.0; + actualPartLoadRatio = -1.0; + minPartLoadRatio = 0.1; + maxPartLoadRatio = 1.0; + qEvaporator = 50000.0; + frac = -1.0; + expPLR = 0.0; + expFrac = 1.0; + expFalseLoad = 0.0; + state->dataPlantCentralGSHP->ChillerCyclingRatio = -1.0; + state->dataPlantCentralGSHP->ChillerPartLoadRatio = -1.0; + state->dataPlantCentralGSHP->ChillerFalseLoadRate = -1.0; + thisWrap.calcPLRAndCyclingRatio(*state, availChillerCap, actualPartLoadRatio, minPartLoadRatio, maxPartLoadRatio, qEvaporator, frac); + EXPECT_NEAR(frac, expFrac, allowedTolerance); + EXPECT_NEAR(actualPartLoadRatio, expPLR, allowedTolerance); + EXPECT_NEAR(state->dataPlantCentralGSHP->ChillerCyclingRatio, expFrac, allowedTolerance); + EXPECT_NEAR(state->dataPlantCentralGSHP->ChillerPartLoadRatio, expPLR, allowedTolerance); + EXPECT_NEAR(state->dataPlantCentralGSHP->ChillerFalseLoadRate, expFalseLoad, allowedTolerance); + + // Test 2a: valid chiller capacity and evaporator load, negative minPLR + availChillerCap = 50000.0; + actualPartLoadRatio = -1.0; + minPartLoadRatio = -0.1; + maxPartLoadRatio = 1.0; + qEvaporator = 10000.0; + frac = -1.0; + expPLR = 0.2; + expFrac = 1.0; + expFalseLoad = 0.0; + state->dataPlantCentralGSHP->ChillerCyclingRatio = -1.0; + state->dataPlantCentralGSHP->ChillerPartLoadRatio = -1.0; + state->dataPlantCentralGSHP->ChillerFalseLoadRate = -1.0; + thisWrap.calcPLRAndCyclingRatio(*state, availChillerCap, actualPartLoadRatio, minPartLoadRatio, maxPartLoadRatio, qEvaporator, frac); + EXPECT_NEAR(frac, expFrac, allowedTolerance); + EXPECT_NEAR(actualPartLoadRatio, expPLR, allowedTolerance); + EXPECT_NEAR(state->dataPlantCentralGSHP->ChillerCyclingRatio, expFrac, allowedTolerance); + EXPECT_NEAR(state->dataPlantCentralGSHP->ChillerPartLoadRatio, expPLR, allowedTolerance); + EXPECT_NEAR(state->dataPlantCentralGSHP->ChillerFalseLoadRate, expFalseLoad, allowedTolerance); + + // Test 2b: valid chiller capacity and evaporator load, actualPLR lower then minPLR + availChillerCap = 50000.0; + actualPartLoadRatio = -1.0; + minPartLoadRatio = 0.4; + maxPartLoadRatio = 1.0; + qEvaporator = 10000.0; + frac = -1.0; + expPLR = 0.4; + expFrac = 0.5; + expFalseLoad = 10000.0; + state->dataPlantCentralGSHP->ChillerCyclingRatio = -1.0; + state->dataPlantCentralGSHP->ChillerPartLoadRatio = -1.0; + state->dataPlantCentralGSHP->ChillerFalseLoadRate = -1.0; + thisWrap.calcPLRAndCyclingRatio(*state, availChillerCap, actualPartLoadRatio, minPartLoadRatio, maxPartLoadRatio, qEvaporator, frac); + EXPECT_NEAR(frac, expFrac, allowedTolerance); + EXPECT_NEAR(actualPartLoadRatio, expPLR, allowedTolerance); + EXPECT_NEAR(state->dataPlantCentralGSHP->ChillerCyclingRatio, expFrac, allowedTolerance); + EXPECT_NEAR(state->dataPlantCentralGSHP->ChillerPartLoadRatio, expPLR, allowedTolerance); + EXPECT_NEAR(state->dataPlantCentralGSHP->ChillerFalseLoadRate, expFalseLoad, allowedTolerance); + + // Test 2c: valid chiller capacity and evaporator load, actualPLR higher then minPLR + availChillerCap = 50000.0; + actualPartLoadRatio = -1.0; + minPartLoadRatio = 0.4; + maxPartLoadRatio = 1.0; + qEvaporator = 30000.0; + frac = -1.0; + expPLR = 0.6; + expFrac = 1.0; + expFalseLoad = 0.0; + state->dataPlantCentralGSHP->ChillerCyclingRatio = -1.0; + state->dataPlantCentralGSHP->ChillerPartLoadRatio = -1.0; + state->dataPlantCentralGSHP->ChillerFalseLoadRate = -1.0; + thisWrap.calcPLRAndCyclingRatio(*state, availChillerCap, actualPartLoadRatio, minPartLoadRatio, maxPartLoadRatio, qEvaporator, frac); + EXPECT_NEAR(frac, expFrac, allowedTolerance); + EXPECT_NEAR(actualPartLoadRatio, expPLR, allowedTolerance); + EXPECT_NEAR(state->dataPlantCentralGSHP->ChillerCyclingRatio, expFrac, allowedTolerance); + EXPECT_NEAR(state->dataPlantCentralGSHP->ChillerPartLoadRatio, expPLR, allowedTolerance); + EXPECT_NEAR(state->dataPlantCentralGSHP->ChillerFalseLoadRate, expFalseLoad, allowedTolerance); + + // Test 2d: valid chiller capacity and evaporator load, actualPLR higher then maxPLR + availChillerCap = 50000.0; + actualPartLoadRatio = -1.0; + minPartLoadRatio = 0.4; + maxPartLoadRatio = 1.0; + qEvaporator = 60000.0; + frac = -1.0; + expPLR = 1.0; + expFrac = 1.0; + expFalseLoad = 0.0; + state->dataPlantCentralGSHP->ChillerCyclingRatio = -1.0; + state->dataPlantCentralGSHP->ChillerPartLoadRatio = -1.0; + state->dataPlantCentralGSHP->ChillerFalseLoadRate = -1.0; + thisWrap.calcPLRAndCyclingRatio(*state, availChillerCap, actualPartLoadRatio, minPartLoadRatio, maxPartLoadRatio, qEvaporator, frac); + EXPECT_NEAR(frac, expFrac, allowedTolerance); + EXPECT_NEAR(actualPartLoadRatio, expPLR, allowedTolerance); + EXPECT_NEAR(state->dataPlantCentralGSHP->ChillerCyclingRatio, expFrac, allowedTolerance); + EXPECT_NEAR(state->dataPlantCentralGSHP->ChillerPartLoadRatio, expPLR, allowedTolerance); + EXPECT_NEAR(state->dataPlantCentralGSHP->ChillerFalseLoadRate, expFalseLoad, allowedTolerance); +}