From 8c89051ad88be060445015831426b7dffd5611c3 Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Wed, 21 Aug 2024 18:53:51 +0200 Subject: [PATCH 1/2] Add a (failing) unit test for #10387 ``` [ RUN ] EnergyPlusFixture.GAHP_AirSource_CurveEval /home/julien/Software/Others/EnergyPlus2/tst/EnergyPlus/unit/PlantLoopHeatPumpEIR.unit.cc:5650: Failure Expected equality of these values: oaTemp Which is: 3 oaTempforCurve Which is: 43.0859 /home/julien/Software/Others/EnergyPlus2/tst/EnergyPlus/unit/PlantLoopHeatPumpEIR.unit.cc:5658: Failure Expected equality of these values: oaTemp Which is: 3 oaTempforCurve Which is: 43.0859 /home/julien/Software/Others/EnergyPlus2/tst/EnergyPlus/unit/PlantLoopHeatPumpEIR.unit.cc:5664: Failure Expected equality of these values: oaTemp Which is: 3 oaTempforCurve Which is: 3.33333 /home/julien/Software/Others/EnergyPlus2/tst/EnergyPlus/unit/PlantLoopHeatPumpEIR.unit.cc:5693: Failure Expected equality of these values: oaTemp Which is: 3 oaTempforCurve Which is: 43.0859 /home/julien/Software/Others/EnergyPlus2/tst/EnergyPlus/unit/PlantLoopHeatPumpEIR.unit.cc:5701: Failure Expected equality of these values: oaTemp Which is: 3 oaTempforCurve Which is: 43.0859 /home/julien/Software/Others/EnergyPlus2/tst/EnergyPlus/unit/PlantLoopHeatPumpEIR.unit.cc:5736: Failure Expected equality of these values: oaWetbulb Which is: -4.36026 oaTempforCurve Which is: 15.7288 /home/julien/Software/Others/EnergyPlus2/tst/EnergyPlus/unit/PlantLoopHeatPumpEIR.unit.cc:5744: Failure Expected equality of these values: oaWetbulb Which is: -4.36026 oaTempforCurve Which is: 15.7288 /home/julien/Software/Others/EnergyPlus2/tst/EnergyPlus/unit/PlantLoopHeatPumpEIR.unit.cc:5750: Failure Expected equality of these values: oaWetbulb Which is: -4.36026 oaTempforCurve Which is: 3.33333 /home/julien/Software/Others/EnergyPlus2/tst/EnergyPlus/unit/PlantLoopHeatPumpEIR.unit.cc:5778: Failure Expected equality of these values: oaWetbulb Which is: -4.36026 oaTempforCurve Which is: 17.0828 /home/julien/Software/Others/EnergyPlus2/tst/EnergyPlus/unit/PlantLoopHeatPumpEIR.unit.cc:5786: Failure Expected equality of these values: oaWetbulb Which is: -4.36026 oaTempforCurve Which is: 17.0828 [ FAILED ] EnergyPlusFixture.GAHP_AirSource_CurveEval (900 ms) ``` --- .../unit/PlantLoopHeatPumpEIR.unit.cc | 395 ++++++++++++++++++ 1 file changed, 395 insertions(+) diff --git a/tst/EnergyPlus/unit/PlantLoopHeatPumpEIR.unit.cc b/tst/EnergyPlus/unit/PlantLoopHeatPumpEIR.unit.cc index c09d3242191..28fdfd84721 100644 --- a/tst/EnergyPlus/unit/PlantLoopHeatPumpEIR.unit.cc +++ b/tst/EnergyPlus/unit/PlantLoopHeatPumpEIR.unit.cc @@ -58,6 +58,7 @@ // EnergyPlus Headers #include "Fixtures/EnergyPlusFixture.hh" #include +#include #include #include #include @@ -5393,5 +5394,399 @@ TEST_F(EnergyPlusFixture, CoolingSimulate_WSHP_SourceSideOutletTemp) EXPECT_NEAR(50.0, thisCoolingPLHP->sourceSideOutletTemp, 0.01); } } + +TEST_F(EnergyPlusFixture, GAHP_AirSource_CurveEval) +{ + // Test for #10665 + std::string const idf_objects = delimited_string({ + + "HeatPump:AirToWater:FuelFired:Heating,", + " FuelFired GAHP Heating, !- Name", + " Node 3, !- Water Inlet Node Name", + " Node 7, !- Water Outlet Node Name", + " FuelFired GAHP Heating OA Node, !- Air Source Node Name", + " FuelFired GAHP Cooling, !- Companion Cooling Heat Pump Name", + " NaturalGas, !- Fuel Type", + " GAHP, !- End-Use Subcategory", + " 3000, !- Nominal Heating Capacity {W}", + " 1.5, !- Nominal COP {W/W}", + " 0.005, !- Design Flow Rate {m3/s}", + " 60, !- Design Supply Temperature {C}", + " 11.1, !- Design Temperature Lift {deltaC}", + " 1, !- Sizing Factor", + " NotModulated, !- Flow Mode", + " DryBulb, !- Outdoor Air Temperature Curve Input Variable", + " EnteringCondenser, !- Water Temperature Curve Input Variable", + " CapCurveFuncTemp, !- Normalized Capacity Function of Temperature Curve Name", + " EIRCurveFuncTemp, !- Fuel Energy Input Ratio Function of Temperature Curve Name", + " EIRCurveFuncPLR, !- Fuel Energy Input Ratio Function of PLR Curve Name", + " 0.1, !- Minimum Part Load Ratio", + " 1, !- Maximum Part Load Ratio", + " OnDemand, !- Defrost Control Type", + " 0, !- Defrost Operation Time Fraction", + " EIRDefrostFoTCurve, !- Fuel Energy Input Ratio Defrost Adjustment Curve Name", + " 0, !- Resistive Defrost Heater Capacity {W}", + " 5, !- Maximum Outdoor Dry-bulb Temperature for Defrost Operation {C}", + " CRFCurve, !- Cycling Ratio Factor Curve Name", + " 500, !- Nominal Auxiliary Electric Power {W}", + " auxElecEIRCurveFuncTempCurve, !- Auxiliary Electric Energy Input Ratio Function of Temperature Curve Name", + " auxElecEIRFoPLRCurve, !- Auxiliary Electric Energy Input Ratio Function of PLR Curve Name", + " 20; !- Standby Electric Power {W}", + + "OutdoorAir:Node,", + " FuelFired GAHP Heating OA Node; !- Name", + + "HeatPump:AirToWater:FuelFired:Cooling,", + " FuelFired GAHP Cooling, !- Name", + " FuelFired GAHP Cooling Water Inlet Node, !- Water Inlet Node Name", + " FuelFired GAHP Cooling Water Outlet Node, !- Water Outlet Node Name", + " FuelFired GAHP Cooling OA Node, !- Air Source Node Name", + " FuelFired GAHP Heating, !- Companion Heating Heat Pump Name", + " NaturalGas, !- Fuel Type", + " GAHP, !- End-Use Subcategory", + " 4000, !- Nominal Cooling Capacity {W}", + " 2, !- Nominal COP {W/W}", + " 0.006, !- Design Flow Rate {m3/s}", + " 7, !- Design Supply Temperature {C}", + " 11.1, !- Design Temperature Lift {deltaC}", + " 1, !- Sizing Factor", + " NotModulated, !- Flow Mode", + " DryBulb, !- Outdoor Air Temperature Curve Input Variable", + " EnteringEvaporator, !- Water Temperature Curve Input Variable", + " CapCurveFuncTemp, !- Normalized Capacity Function of Temperature Curve Name", + " EIRCurveFuncTemp, !- Fuel Energy Input Ratio Function of Temperature Curve Name", + " EIRCurveFuncPLR, !- Fuel Energy Input Ratio Function of PLR Curve Name", + " 0.1, !- Minimum Part Load Ratio", + " 1, !- Maximum Part Load Ratio", + " CRFCurve, !- Cycling Ratio Factor Curve Name", + " 500, !- Nominal Auxiliary Electric Power {W}", + " auxElecEIRCurveFuncTempCurve, !- Auxiliary Electric Energy Input Ratio Function of Temperature Curve Name", + " auxElecEIRFoPLRCurve, !- Auxiliary Electric Energy Input Ratio Function of PLR Curve Name", + " 20; !- Standby Electric Power {W}", + + "OutdoorAir:Node,", + " FuelFired GAHP Cooling OA Node; !- Name", + + "Curve:Biquadratic,", + " CapCurveFuncTemp, !- Name", + " 1, !- Coefficient1 Constant", + " 0, !- Coefficient2 x", + " 0, !- Coefficient3 x**2", + " 0, !- Coefficient4 y", + " 0, !- Coefficient5 y**2", + " 0, !- Coefficient6 x*y", + " 5, !- Minimum Value of x {BasedOnField A2}", + " 10, !- Maximum Value of x {BasedOnField A2}", + " 24, !- Minimum Value of y {BasedOnField A3}", + " 35, !- Maximum Value of y {BasedOnField A3}", + " , !- Minimum Curve Output {BasedOnField A4}", + " , !- Maximum Curve Output {BasedOnField A4}", + " Temperature, !- Input Unit Type for X", + " Temperature; !- Input Unit Type for Y", + + "Curve:Biquadratic,", + " EIRCurveFuncTemp, !- Name", + " 1, !- Coefficient1 Constant", + " 0, !- Coefficient2 x", + " 0, !- Coefficient3 x**2", + " 0, !- Coefficient4 y", + " 0, !- Coefficient5 y**2", + " 0, !- Coefficient6 x*y", + " 5, !- Minimum Value of x {BasedOnField A2}", + " 10, !- Maximum Value of x {BasedOnField A2}", + " 24, !- Minimum Value of y {BasedOnField A3}", + " 35, !- Maximum Value of y {BasedOnField A3}", + " , !- Minimum Curve Output {BasedOnField A4}", + " , !- Maximum Curve Output {BasedOnField A4}", + " Temperature, !- Input Unit Type for X", + " Temperature; !- Input Unit Type for Y", + + "Curve:Quadratic,", + " EIRCurveFuncPLR, !- Name", + " 1, !- Coefficient1 Constant", + " 0, !- Coefficient2 x", + " 0, !- Coefficient3 x**2", + " 0, !- Minimum Value of x {BasedOnField A2}", + " 1; !- Maximum Value of x {BasedOnField A2}", + + "Curve:Quadratic,", + " CRFCurve, !- Name", + " 1, !- Coefficient1 Constant", + " 0, !- Coefficient2 x", + " 0, !- Coefficient3 x**2", + " 0, !- Minimum Value of x {BasedOnField A2}", + " 100, !- Maximum Value of x {BasedOnField A2}", + " 0, !- Minimum Curve Output {BasedOnField A3}", + " 10; !- Maximum Curve Output {BasedOnField A3}", + + "Curve:Biquadratic,", + " auxElecEIRCurveFuncTempCurve, !- Name", + " 1, !- Coefficient1 Constant", + " 0, !- Coefficient2 x", + " 0, !- Coefficient3 x**2", + " 0, !- Coefficient4 y", + " 0, !- Coefficient5 y**2", + " 0, !- Coefficient6 x*y", + " -100, !- Minimum Value of x {BasedOnField A2}", + " 100, !- Maximum Value of x {BasedOnField A2}", + " -100, !- Minimum Value of y {BasedOnField A3}", + " 100; !- Maximum Value of y {BasedOnField A3}", + + "Curve:Cubic,", + " auxElecEIRFoPLRCurve, !- Name", + " 1, !- Coefficient1 Constant", + " 0, !- Coefficient2 x", + " 0, !- Coefficient3 x**2", + " 0, !- Coefficient4 x**3", + " -100, !- Minimum Value of x {BasedOnField A2}", + " 100; !- Maximum Value of x {BasedOnField A2}", + + "Curve:Quadratic,", + " EIRDefrostFoTCurve, !- Name", + " 1.0317, !- Coefficient1 Constant", + " -0.006, !- Coefficient2 x", + " -0.0011, !- Coefficient3 x**2", + " -100, !- Minimum Value of x {BasedOnField A2}", + " 100, !- Maximum Value of x {BasedOnField A2}", + " 1, !- Minimum Curve Output {BasedOnField A3}", + " 10; !- Maximum Curve Output {BasedOnField A3}", + + }); + + ASSERT_TRUE(process_idf(idf_objects)); + + // set up the plant loops + // first the load side + state->dataPlnt->TotNumLoops = 1; + state->dataPlnt->PlantLoop.allocate(1); + + state->dataPlnt->PlantLoop(1).LoopDemandCalcScheme = DataPlant::LoopDemandCalcScheme::SingleSetPoint; + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).TotalBranches = 2; + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch.allocate(2); + + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).TotalComponents = 1; + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp.allocate(1); + auto &PLHPPlantLoadSideCompHeating = state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp(1); + PLHPPlantLoadSideCompHeating.Type = DataPlant::PlantEquipmentType::HeatPumpFuelFiredHeating; + PLHPPlantLoadSideCompHeating.CurOpSchemeType = DataPlant::OpScheme::CompSetPtBased; + + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(2).TotalComponents = 1; + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(2).Comp.allocate(1); + auto &PLHPPlantLoadSideCompCooling = state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(2).Comp(1); + PLHPPlantLoadSideCompCooling.Type = DataPlant::PlantEquipmentType::HeatPumpFuelFiredCooling; + PLHPPlantLoadSideCompCooling.CurOpSchemeType = DataPlant::OpScheme::CompSetPtBased; + + // the init call expects a "from" calling point + PlantLocation myHeatingLoadLocation = PlantLocation(1, DataPlant::LoopSideLocation::Supply, 1, 1); + PlantLocation myCoolingLoadLocation = PlantLocation(1, DataPlant::LoopSideLocation::Supply, 2, 1); + + // call the factory with a valid name to trigger reading inputs + EIRFuelFiredHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpFuelFiredHeating, "FUELFIRED GAHP HEATING"); + + EIRFuelFiredHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpFuelFiredCooling, "FUELFIRED GAHP COOLING"); + + // verify the size of the vector and the processed condition + EXPECT_EQ(2u, state->dataEIRFuelFiredHeatPump->heatPumps.size()); + + // for now we know the order is maintained, so get each heat pump object + const bool is_heating_first = state->dataEIRFuelFiredHeatPump->heatPumps[0].name == "FUELFIRED GAHP HEATING"; + EIRFuelFiredHeatPump *thisHeatingPLHP = &state->dataEIRFuelFiredHeatPump->heatPumps[is_heating_first ? 0 : 1]; + EIRFuelFiredHeatPump *thisCoolingPLHP = &state->dataEIRFuelFiredHeatPump->heatPumps[is_heating_first ? 1 : 0]; + EXPECT_EQ("FUELFIRED GAHP HEATING", thisHeatingPLHP->name); + EXPECT_EQ("FUELFIRED GAHP COOLING", thisCoolingPLHP->name); + + // do a bit of extra wiring up to the plant + PLHPPlantLoadSideCompHeating.Name = thisHeatingPLHP->name; + PLHPPlantLoadSideCompHeating.NodeNumIn = thisHeatingPLHP->loadSideNodes.inlet; + PLHPPlantLoadSideCompHeating.NodeNumOut = thisHeatingPLHP->loadSideNodes.outlet; + + PLHPPlantLoadSideCompCooling.Name = thisCoolingPLHP->name; + PLHPPlantLoadSideCompCooling.NodeNumIn = thisCoolingPLHP->loadSideNodes.inlet; + PLHPPlantLoadSideCompCooling.NodeNumOut = thisCoolingPLHP->loadSideNodes.outlet; + + // call for all initialization + state->dataGlobal->BeginEnvrnFlag = true; + state->dataPlnt->PlantFirstSizesOkayToFinalize = true; + + // I am picking a temperature that is: + // * Below the 'Maximum Outdoor Dry-bulb Temperature for Defrost Operation' I entered (5.0C) + // * Between the harcoded min/max defrost temperatures of 16F/-8.88C | 38F/3.33C + double constexpr oaTemp = 3.0; + state->dataEnvrn->OutDryBulbTemp = oaTemp; + + double const oaWetbulb = Psychrometrics::PsyTwbFnTdbWPb(*state, oaTemp, 0.0, 101325.0); + + Real64 constexpr expectedLoadMassFlowRate = 0.09999; + Real64 constexpr expectedCp = 4180; + Real64 constexpr specifiedLoadSetpoint = 45; + + // This is not the case, even though the E+ I/O Documentation says it should + constexpr bool isLoadSideHeatTransferNegativeForCooling = false; + + thisHeatingPLHP->onInitLoopEquip(*state, myHeatingLoadLocation); + { + EXPECT_ENUM_EQ(EIRFuelFiredHeatPump::OATempCurveVar::DryBulb, thisHeatingPLHP->oaTempCurveInputVar); + EXPECT_ENUM_EQ(EIRFuelFiredHeatPump::WaterTempCurveVar::EnteringCondenser, thisHeatingPLHP->waterTempCurveInputVar); + + bool firstHVAC = true; + Real64 curLoad = 800; + bool runFlag = true; + Real64 const calculatedLoadInletTemp = specifiedLoadSetpoint - curLoad / (expectedLoadMassFlowRate * expectedCp); + + state->dataLoopNodes->Node(thisHeatingPLHP->loadSideNodes.inlet).Temp = calculatedLoadInletTemp; + state->dataLoopNodes->Node(thisHeatingPLHP->loadSideNodes.outlet).TempSetPoint = specifiedLoadSetpoint; + state->dataLoopNodes->Node(thisHeatingPLHP->sourceSideNodes.inlet).Temp = oaTemp; + + thisHeatingPLHP->simulate(*state, myHeatingLoadLocation, firstHVAC, curLoad, runFlag); + // expect it to meet setpoint and have some pre-evaluated conditions + // EXPECT_NEAR(specifiedLoadSetpoint, thisHeatingPLHP->loadSideOutletTemp, 0.001); + EXPECT_NEAR(curLoad, thisHeatingPLHP->loadSideHeatTransfer, 0.001); + { + ASSERT_GT(thisHeatingPLHP->capFuncTempCurveIndex, 0); + auto const *thisCurve = state->dataCurveManager->PerfCurve(thisHeatingPLHP->capFuncTempCurveIndex); + Real64 const waterTempforCurve = thisCurve->inputs[0]; + Real64 const oaTempforCurve = thisCurve->inputs[1]; + EXPECT_EQ(calculatedLoadInletTemp, waterTempforCurve); + EXPECT_EQ(oaTemp, oaTempforCurve); + } + { + ASSERT_GT(thisHeatingPLHP->powerRatioFuncTempCurveIndex, 0); + auto const *thisCurve = state->dataCurveManager->PerfCurve(thisHeatingPLHP->powerRatioFuncTempCurveIndex); + Real64 const waterTempforCurve = thisCurve->inputs[0]; + Real64 const oaTempforCurve = thisCurve->inputs[1]; + EXPECT_EQ(calculatedLoadInletTemp, waterTempforCurve); + EXPECT_EQ(oaTemp, oaTempforCurve); + } + { + ASSERT_GT(thisHeatingPLHP->defrostEIRCurveIndex, 0); + auto const *thisCurve = state->dataCurveManager->PerfCurve(thisHeatingPLHP->defrostEIRCurveIndex); + Real64 const oaTempforCurve = thisCurve->inputs[0]; + EXPECT_EQ(oaTemp, oaTempforCurve); + } + } + + thisCoolingPLHP->onInitLoopEquip(*state, myCoolingLoadLocation); + + { + EXPECT_ENUM_EQ(EIRFuelFiredHeatPump::OATempCurveVar::DryBulb, thisCoolingPLHP->oaTempCurveInputVar); + EXPECT_ENUM_EQ(EIRFuelFiredHeatPump::WaterTempCurveVar::EnteringEvaporator, thisCoolingPLHP->waterTempCurveInputVar); + + bool firstHVAC = true; + Real64 curLoad = -800; + bool runFlag = true; + Real64 const calculatedLoadInletTemp = specifiedLoadSetpoint + curLoad / (expectedLoadMassFlowRate * expectedCp); + + state->dataLoopNodes->Node(thisCoolingPLHP->loadSideNodes.inlet).Temp = calculatedLoadInletTemp; + state->dataLoopNodes->Node(thisCoolingPLHP->loadSideNodes.outlet).TempSetPoint = specifiedLoadSetpoint; + state->dataLoopNodes->Node(thisCoolingPLHP->sourceSideNodes.inlet).Temp = oaTemp; + + thisCoolingPLHP->simulate(*state, myCoolingLoadLocation, firstHVAC, curLoad, runFlag); + // expect it to meet setpoint and have some pre-evaluated conditions + // EXPECT_NEAR(specifiedLoadSetpoint, thisCoolingPLHP->loadSideOutletTemp, 0.001); + EXPECT_NEAR(isLoadSideHeatTransferNegativeForCooling ? curLoad : -curLoad, thisCoolingPLHP->loadSideHeatTransfer, 0.001); + { + ASSERT_GT(thisCoolingPLHP->powerRatioFuncTempCurveIndex, 0); + auto const *thisCurve = state->dataCurveManager->PerfCurve(thisCoolingPLHP->capFuncTempCurveIndex); + Real64 const waterTempforCurve = thisCurve->inputs[0]; + Real64 const oaTempforCurve = thisCurve->inputs[1]; + EXPECT_EQ(calculatedLoadInletTemp, waterTempforCurve); + EXPECT_EQ(oaTemp, oaTempforCurve); + } + { + ASSERT_GT(thisCoolingPLHP->powerRatioFuncTempCurveIndex, 0); + auto const *thisCurve = state->dataCurveManager->PerfCurve(thisCoolingPLHP->powerRatioFuncTempCurveIndex); + Real64 const waterTempforCurve = thisCurve->inputs[0]; + Real64 const oaTempforCurve = thisCurve->inputs[1]; + EXPECT_EQ(calculatedLoadInletTemp, waterTempforCurve); + EXPECT_EQ(oaTemp, oaTempforCurve); + } + ASSERT_EQ(0, thisCoolingPLHP->defrostEIRCurveIndex); + } + + // Now we switch the evaluation variables to Wetbulb and Leaving + thisHeatingPLHP->oaTempCurveInputVar = EIRFuelFiredHeatPump::OATempCurveVar::WetBulb; + thisHeatingPLHP->waterTempCurveInputVar = EIRFuelFiredHeatPump::WaterTempCurveVar::LeavingCondenser; + thisCoolingPLHP->oaTempCurveInputVar = EIRFuelFiredHeatPump::OATempCurveVar::WetBulb; + thisCoolingPLHP->waterTempCurveInputVar = EIRFuelFiredHeatPump::WaterTempCurveVar::LeavingEvaporator; + + { + EXPECT_ENUM_EQ(EIRFuelFiredHeatPump::OATempCurveVar::WetBulb, thisHeatingPLHP->oaTempCurveInputVar); + EXPECT_ENUM_EQ(EIRFuelFiredHeatPump::WaterTempCurveVar::LeavingCondenser, thisHeatingPLHP->waterTempCurveInputVar); + + bool firstHVAC = true; + Real64 curLoad = 800; + bool runFlag = true; + Real64 const calculatedLoadInletTemp = specifiedLoadSetpoint - curLoad / (expectedLoadMassFlowRate * expectedCp); + + Real64 const ori_loadSideOutletTemp = thisHeatingPLHP->loadSideOutletTemp; + state->dataLoopNodes->Node(thisHeatingPLHP->loadSideNodes.inlet).Temp = calculatedLoadInletTemp; + state->dataLoopNodes->Node(thisHeatingPLHP->loadSideNodes.outlet).TempSetPoint = specifiedLoadSetpoint; + state->dataLoopNodes->Node(thisHeatingPLHP->sourceSideNodes.inlet).Temp = oaTemp; + + thisHeatingPLHP->simulate(*state, myHeatingLoadLocation, firstHVAC, curLoad, runFlag); + // expect it to meet setpoint and have some pre-evaluated conditions + // EXPECT_NEAR(specifiedLoadSetpoint, thisHeatingPLHP->loadSideOutletTemp, 0.001); + EXPECT_NEAR(curLoad, thisHeatingPLHP->loadSideHeatTransfer, 0.001); + { + ASSERT_GT(thisHeatingPLHP->capFuncTempCurveIndex, 0); + auto const *thisCurve = state->dataCurveManager->PerfCurve(thisHeatingPLHP->capFuncTempCurveIndex); + Real64 const waterTempforCurve = thisCurve->inputs[0]; + Real64 const oaTempforCurve = thisCurve->inputs[1]; + EXPECT_EQ(ori_loadSideOutletTemp, waterTempforCurve); + EXPECT_EQ(oaWetbulb, oaTempforCurve); + } + { + ASSERT_GT(thisHeatingPLHP->powerRatioFuncTempCurveIndex, 0); + auto const *thisCurve = state->dataCurveManager->PerfCurve(thisHeatingPLHP->powerRatioFuncTempCurveIndex); + Real64 const waterTempforCurve = thisCurve->inputs[0]; + Real64 const oaTempforCurve = thisCurve->inputs[1]; + EXPECT_EQ(ori_loadSideOutletTemp, waterTempforCurve); + EXPECT_EQ(oaWetbulb, oaTempforCurve); + } + { + ASSERT_GT(thisHeatingPLHP->defrostEIRCurveIndex, 0); + auto const *thisCurve = state->dataCurveManager->PerfCurve(thisHeatingPLHP->defrostEIRCurveIndex); + Real64 const oaTempforCurve = thisCurve->inputs[0]; + EXPECT_EQ(oaWetbulb, oaTempforCurve); + } + } + + { + EXPECT_ENUM_EQ(EIRFuelFiredHeatPump::OATempCurveVar::WetBulb, thisCoolingPLHP->oaTempCurveInputVar); + EXPECT_ENUM_EQ(EIRFuelFiredHeatPump::WaterTempCurveVar::LeavingEvaporator, thisCoolingPLHP->waterTempCurveInputVar); + + bool firstHVAC = true; + Real64 curLoad = -800; + bool runFlag = true; + Real64 const calculatedLoadInletTemp = specifiedLoadSetpoint - curLoad / (expectedLoadMassFlowRate * expectedCp); + + Real64 const ori_loadSideOutletTemp = thisCoolingPLHP->loadSideOutletTemp; + state->dataLoopNodes->Node(thisCoolingPLHP->loadSideNodes.inlet).Temp = calculatedLoadInletTemp; + state->dataLoopNodes->Node(thisCoolingPLHP->loadSideNodes.outlet).TempSetPoint = specifiedLoadSetpoint; + state->dataLoopNodes->Node(thisCoolingPLHP->sourceSideNodes.inlet).Temp = oaTemp; + + thisCoolingPLHP->simulate(*state, myCoolingLoadLocation, firstHVAC, curLoad, runFlag); + // expect it to meet setpoint and have some pre-evaluated conditions + // EXPECT_NEAR(specifiedLoadSetpoint, thisCoolingPLHP->loadSideOutletTemp, 0.001); + EXPECT_NEAR(isLoadSideHeatTransferNegativeForCooling ? curLoad : -curLoad, thisCoolingPLHP->loadSideHeatTransfer, 0.001); + { + ASSERT_GT(thisCoolingPLHP->powerRatioFuncTempCurveIndex, 0); + auto const *thisCurve = state->dataCurveManager->PerfCurve(thisCoolingPLHP->capFuncTempCurveIndex); + Real64 const waterTempforCurve = thisCurve->inputs[0]; + Real64 const oaTempforCurve = thisCurve->inputs[1]; + EXPECT_EQ(ori_loadSideOutletTemp, waterTempforCurve); + EXPECT_EQ(oaWetbulb, oaTempforCurve); + } + { + ASSERT_GT(thisCoolingPLHP->powerRatioFuncTempCurveIndex, 0); + auto const *thisCurve = state->dataCurveManager->PerfCurve(thisCoolingPLHP->powerRatioFuncTempCurveIndex); + Real64 const waterTempforCurve = thisCurve->inputs[0]; + Real64 const oaTempforCurve = thisCurve->inputs[1]; + EXPECT_EQ(ori_loadSideOutletTemp, waterTempforCurve); + EXPECT_EQ(oaWetbulb, oaTempforCurve); + } + ASSERT_EQ(0, thisCoolingPLHP->defrostEIRCurveIndex); + } +} #pragma clang diagnostic pop #pragma clang diagnostic pop From 3fe7397a39b3394a1acac0b8574a7dca02ccb1bb Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Wed, 21 Aug 2024 18:56:29 +0200 Subject: [PATCH 2/2] Fix #10387 - HeatPump:AirToWater:FuelFired:Heating was not using Outside air temperatures to evaluate curves --- src/EnergyPlus/PlantLoopHeatPumpEIR.cc | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/EnergyPlus/PlantLoopHeatPumpEIR.cc b/src/EnergyPlus/PlantLoopHeatPumpEIR.cc index b7bce4dd37f..0c9cb47fff3 100644 --- a/src/EnergyPlus/PlantLoopHeatPumpEIR.cc +++ b/src/EnergyPlus/PlantLoopHeatPumpEIR.cc @@ -2549,6 +2549,7 @@ void EIRFuelFiredHeatPump::doPhysics(EnergyPlusData &state, Real64 currentLoad) // will not shut down the branch auto &thisInletNode = state.dataLoopNodes->Node(this->loadSideNodes.inlet); auto &thisOutletNode = state.dataLoopNodes->Node(this->loadSideNodes.outlet); + auto &thisSourceSideInletNode = state.dataLoopNodes->Node(this->sourceSideNodes.inlet); // OA Intake node auto &sim_component = DataPlant::CompData::getPlantComponent(state, this->loadSidePlantLoc); if ((this->EIRHPType == DataPlant::PlantEquipmentType::HeatPumpFuelFiredHeating && currentLoad <= 0.0)) { if (sim_component.FlowCtrl == DataBranchAirLoopPlant::ControlType::SeriesActive) this->loadSideMassFlowRate = thisInletNode.MassFlowRate; @@ -2720,10 +2721,10 @@ void EIRFuelFiredHeatPump::doPhysics(EnergyPlusData &state, Real64 currentLoad) // Determine which air variable to use for GAHP: // Source (air) side variable to use // auto &thisloadsideinletnode = state.dataLoopNodes->Node(this->loadSideNodes.inlet); - Real64 oaTempforCurve = thisInletNode.Temp; // state.dataLoopNodes->Node(this->loadSideNodes.inlet).Temp; + Real64 oaTempforCurve = this->sourceSideInletTemp; // state.dataLoopNodes->Node(this->loadSideNodes.inlet).Temp; if (this->oaTempCurveInputVar == OATempCurveVar::WetBulb) { - oaTempforCurve = - Psychrometrics::PsyTwbFnTdbWPb(state, thisInletNode.Temp, thisInletNode.HumRat, thisInletNode.Press, "PLFFHPEIR::doPhysics()"); + oaTempforCurve = Psychrometrics::PsyTwbFnTdbWPb( + state, thisSourceSideInletNode.Temp, thisSourceSideInletNode.HumRat, thisSourceSideInletNode.Press, "PLFFHPEIR::doPhysics()"); } // Load (water) side temperature variable @@ -2733,10 +2734,7 @@ void EIRFuelFiredHeatPump::doPhysics(EnergyPlusData &state, Real64 currentLoad) } // evaluate capacity modifier curve and determine load side heat transfer - Real64 capacityModifierFuncTemp = - // CurveManager::CurveValue(state, this->capFuncTempCurveIndex, loadSideOutletSetpointTemp, this->sourceSideInletTemp); - // CurveManager::CurveValue(state, this->capFuncTempCurveIndex, loadSideOutletSetpointTemp, oaTempforCurve); - Curve::CurveValue(state, this->capFuncTempCurveIndex, waterTempforCurve, oaTempforCurve); + Real64 capacityModifierFuncTemp = Curve::CurveValue(state, this->capFuncTempCurveIndex, waterTempforCurve, oaTempforCurve); if (capacityModifierFuncTemp < 0.0) { if (this->capModFTErrorIndex == 0) {