From c94f9dd9c93a44569015a54ece14326fe8b7b347 Mon Sep 17 00:00:00 2001 From: lymereJ Date: Fri, 22 May 2026 14:40:32 -0700 Subject: [PATCH 1/7] For AWHP, if no load then no flow. --- src/EnergyPlus/PlantLoopHeatPumpEIR.cc | 2 +- tst/EnergyPlus/unit/PlantLoopHeatPumpEIR.unit.cc | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/EnergyPlus/PlantLoopHeatPumpEIR.cc b/src/EnergyPlus/PlantLoopHeatPumpEIR.cc index 1bd1ef2617a..34d256871ca 100644 --- a/src/EnergyPlus/PlantLoopHeatPumpEIR.cc +++ b/src/EnergyPlus/PlantLoopHeatPumpEIR.cc @@ -284,7 +284,7 @@ void EIRPlantLoopHeatPump::setOperatingFlowRatesWSHP(EnergyPlusData &state, bool void EIRPlantLoopHeatPump::setOperatingFlowRatesASHP(EnergyPlusData &state, bool FirstHVACIteration, [[maybe_unused]] Real64 const currentLoad) { - if (!this->running) { + if (std::abs(currentLoad) < HVAC::SmallLoad || !this->running) { this->loadSideMassFlowRate = 0.0; this->sourceSideMassFlowRate = 0.0; PlantUtilities::SetComponentFlowRate( diff --git a/tst/EnergyPlus/unit/PlantLoopHeatPumpEIR.unit.cc b/tst/EnergyPlus/unit/PlantLoopHeatPumpEIR.unit.cc index 7b5a812d93f..fa23cb8c6f3 100644 --- a/tst/EnergyPlus/unit/PlantLoopHeatPumpEIR.unit.cc +++ b/tst/EnergyPlus/unit/PlantLoopHeatPumpEIR.unit.cc @@ -4030,13 +4030,13 @@ TEST_F(EnergyPlusFixture, Initialization2_AirSource) EXPECT_NEAR(0.0, thisCoolingPLHP->loadSideMassFlowRate, 0.001); EXPECT_NEAR(0, thisCoolingPLHP->sourceSideMassFlowRate, 0.001); - // call with run flag ON, flow locked at zero on source side + // call with run flag ON, flow locked at zero on source side since there's no load state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).FlowLock = DataPlant::FlowLock::Locked; state->dataLoopNodes->Node(thisCoolingPLHP->loadSideNodes.inlet).MassFlowRate = 0.2; thisCoolingPLHP->running = true; thisCoolingPLHP->setOperatingFlowRatesASHP(*state, firstHVACIteration, currentLoad); EXPECT_NEAR(0.2, thisCoolingPLHP->loadSideMassFlowRate, 0.001); - EXPECT_NEAR(1.29, thisCoolingPLHP->sourceSideMassFlowRate, 0.1); + EXPECT_NEAR(0.0, thisCoolingPLHP->sourceSideMassFlowRate, 0.001); // call with run flag ON, flow locked at zero on both sides state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).FlowLock = DataPlant::FlowLock::Locked; @@ -4046,13 +4046,13 @@ TEST_F(EnergyPlusFixture, Initialization2_AirSource) EXPECT_NEAR(0.0, thisCoolingPLHP->loadSideMassFlowRate, 0.001); EXPECT_NEAR(0.0, thisCoolingPLHP->sourceSideMassFlowRate, 0.001); - // call with run flag ON, flow locked at nonzero both + // call with run flag ON, flow locked at zero on source side since there's no load state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).FlowLock = DataPlant::FlowLock::Locked; state->dataLoopNodes->Node(thisCoolingPLHP->loadSideNodes.inlet).MassFlowRate = 0.14; thisCoolingPLHP->running = true; thisCoolingPLHP->setOperatingFlowRatesASHP(*state, firstHVACIteration, currentLoad); EXPECT_NEAR(0.14, thisCoolingPLHP->loadSideMassFlowRate, 0.001); - EXPECT_NEAR(1.29, thisCoolingPLHP->sourceSideMassFlowRate, 0.1); + EXPECT_NEAR(0.0, thisCoolingPLHP->sourceSideMassFlowRate, 0.001); } TEST_F(EnergyPlusFixture, TestSizing_FullyAutosizedCoolingWithCompanion_AirSource) From 9d7a17db498fab1a7e668666362f5a2f4b56ed01 Mon Sep 17 00:00:00 2001 From: lymereJ Date: Fri, 22 May 2026 16:20:58 -0700 Subject: [PATCH 2/7] Flow is expected when companion HP is used for HR. --- src/EnergyPlus/PlantLoopHeatPumpEIR.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/EnergyPlus/PlantLoopHeatPumpEIR.cc b/src/EnergyPlus/PlantLoopHeatPumpEIR.cc index 34d256871ca..ca164bf780a 100644 --- a/src/EnergyPlus/PlantLoopHeatPumpEIR.cc +++ b/src/EnergyPlus/PlantLoopHeatPumpEIR.cc @@ -284,7 +284,7 @@ void EIRPlantLoopHeatPump::setOperatingFlowRatesWSHP(EnergyPlusData &state, bool void EIRPlantLoopHeatPump::setOperatingFlowRatesASHP(EnergyPlusData &state, bool FirstHVACIteration, [[maybe_unused]] Real64 const currentLoad) { - if (std::abs(currentLoad) < HVAC::SmallLoad || !this->running) { + if ((std::abs(currentLoad) < HVAC::SmallLoad && this->companionHeatPumpCoil == nullptr) || !this->running) { this->loadSideMassFlowRate = 0.0; this->sourceSideMassFlowRate = 0.0; PlantUtilities::SetComponentFlowRate( From dc6abf3f512f01ceac9f05eab7378f5a35af4b66 Mon Sep 17 00:00:00 2001 From: lymereJ Date: Fri, 22 May 2026 22:06:56 -0700 Subject: [PATCH 3/7] Account for series config. --- src/EnergyPlus/PlantLoopHeatPumpEIR.cc | 5 +- .../unit/PlantLoopHeatPumpEIR.unit.cc | 7711 ++++++++++++++++- 2 files changed, 7712 insertions(+), 4 deletions(-) diff --git a/src/EnergyPlus/PlantLoopHeatPumpEIR.cc b/src/EnergyPlus/PlantLoopHeatPumpEIR.cc index ca164bf780a..e5befbd1fbb 100644 --- a/src/EnergyPlus/PlantLoopHeatPumpEIR.cc +++ b/src/EnergyPlus/PlantLoopHeatPumpEIR.cc @@ -284,7 +284,8 @@ void EIRPlantLoopHeatPump::setOperatingFlowRatesWSHP(EnergyPlusData &state, bool void EIRPlantLoopHeatPump::setOperatingFlowRatesASHP(EnergyPlusData &state, bool FirstHVACIteration, [[maybe_unused]] Real64 const currentLoad) { - if ((std::abs(currentLoad) < HVAC::SmallLoad && this->companionHeatPumpCoil == nullptr) || !this->running) { + auto &comp = DataPlant::CompData::getPlantComponent(state, this->loadSidePlantLoc); + if ((std::abs(currentLoad) < HVAC::SmallLoad || !this->running) && comp.FlowCtrl != DataBranchAirLoopPlant::ControlType::SeriesActive) { this->loadSideMassFlowRate = 0.0; this->sourceSideMassFlowRate = 0.0; PlantUtilities::SetComponentFlowRate( @@ -2926,7 +2927,7 @@ void EIRFuelFiredHeatPump::doPhysics(EnergyPlusData &state, Real64 currentLoad) // get setpoint on the load side outlet // Real64 loadSideOutletSetpointTemp = this->getLoadSideOutletSetPointTemp(state); - // Use a logic similar to that for a boilder: If the specified load is 0.0 or the boiler should not run + // Use a logic similar to that for a boiler: If the specified load is 0.0 or the boiler should not run // then we leave this subroutine. Before leaving // if the component control is SERIESACTIVE we set the component flow to inlet flow so that flow resolver // will not shut down the branch diff --git a/tst/EnergyPlus/unit/PlantLoopHeatPumpEIR.unit.cc b/tst/EnergyPlus/unit/PlantLoopHeatPumpEIR.unit.cc index fa23cb8c6f3..807dc9094f9 100644 --- a/tst/EnergyPlus/unit/PlantLoopHeatPumpEIR.unit.cc +++ b/tst/EnergyPlus/unit/PlantLoopHeatPumpEIR.unit.cc @@ -4000,6 +4000,7709 @@ TEST_F(EnergyPlusFixture, Initialization2_AirSource) state->dataPlnt->PlantFirstSizesOkayToFinalize = true; thisCoolingPLHP->onInitLoopEquip(*state, myLocation); + // Componnent flow control type + auto &comp = DataPlant::CompData::getPlantComponent(*state, thisCoolingPLHP->loadSidePlantLoc); + comp.FlowCtrl = DataBranchAirLoopPlant::ControlType::Active; + + // call with run flag off, loose limits on node min/max + thisCoolingPLHP->running = false; + Real64 constexpr currentLoad = 0.0; + thisCoolingPLHP->setOperatingFlowRatesASHP(*state, firstHVACIteration, currentLoad); + EXPECT_NEAR(0.0, thisCoolingPLHP->loadSideMassFlowRate, 0.001); + EXPECT_NEAR(0.0, thisCoolingPLHP->sourceSideMassFlowRate, 0.001); + + // call with run flag off, nonzero minimums + state->dataLoopNodes->Node(thisCoolingPLHP->loadSideNodes.inlet).MassFlowRateMinAvail = 0.1; + thisCoolingPLHP->running = false; + thisCoolingPLHP->setOperatingFlowRatesASHP(*state, firstHVACIteration, currentLoad); + EXPECT_NEAR(0.1, thisCoolingPLHP->loadSideMassFlowRate, 0.001); + EXPECT_NEAR(0, thisCoolingPLHP->sourceSideMassFlowRate, 0.001); + + // call with run flag off, load side flow locked + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).FlowLock = DataPlant::FlowLock::Locked; + state->dataLoopNodes->Node(thisCoolingPLHP->loadSideNodes.inlet).MassFlowRate = 0.24; + thisCoolingPLHP->running = false; + thisCoolingPLHP->setOperatingFlowRatesASHP(*state, firstHVACIteration, currentLoad); + EXPECT_NEAR(0.24, thisCoolingPLHP->loadSideMassFlowRate, 0.001); + EXPECT_NEAR(0.0, thisCoolingPLHP->sourceSideMassFlowRate, 0.001); + + // call with run flag ON, flow locked at zero on load side + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).FlowLock = DataPlant::FlowLock::Locked; + state->dataLoopNodes->Node(thisCoolingPLHP->loadSideNodes.inlet).MassFlowRate = 0.0; + thisCoolingPLHP->running = true; + thisCoolingPLHP->setOperatingFlowRatesASHP(*state, firstHVACIteration, currentLoad); + EXPECT_NEAR(0.0, thisCoolingPLHP->loadSideMassFlowRate, 0.001); + EXPECT_NEAR(0, thisCoolingPLHP->sourceSideMassFlowRate, 0.001); + + // call with run flag ON, flow locked at zero on source side since there's no load + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).FlowLock = DataPlant::FlowLock::Locked; + state->dataLoopNodes->Node(thisCoolingPLHP->loadSideNodes.inlet).MassFlowRate = 0.2; + thisCoolingPLHP->running = true; + thisCoolingPLHP->setOperatingFlowRatesASHP(*state, firstHVACIteration, currentLoad); + EXPECT_NEAR(0.2, thisCoolingPLHP->loadSideMassFlowRate, 0.001); + EXPECT_NEAR(0.0, thisCoolingPLHP->sourceSideMassFlowRate, 0.001); + + comp.FlowCtrl = DataBranchAirLoopPlant::ControlType::SeriesActive; + thisCoolingPLHP->setOperatingFlowRatesASHP(*state, firstHVACIteration, currentLoad); + EXPECT_NEAR(1.29, thisCoolingPLHP->sourceSideMassFlowRate, 0.01); + + // call with run flag ON, flow locked at zero on both sides + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).FlowLock = DataPlant::FlowLock::Locked; + state->dataLoopNodes->Node(thisCoolingPLHP->loadSideNodes.inlet).MassFlowRate = 0.0; + thisCoolingPLHP->running = true; + thisCoolingPLHP->setOperatingFlowRatesASHP(*state, firstHVACIteration, currentLoad); + EXPECT_NEAR(0.0, thisCoolingPLHP->loadSideMassFlowRate, 0.001); + EXPECT_NEAR(0.0, thisCoolingPLHP->sourceSideMassFlowRate, 0.001); + + // call with run flag ON, flow locked at nonzero both + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).FlowLock = DataPlant::FlowLock::Locked; + state->dataLoopNodes->Node(thisCoolingPLHP->loadSideNodes.inlet).MassFlowRate = 0.14; + thisCoolingPLHP->running = true; + thisCoolingPLHP->setOperatingFlowRatesASHP(*state, firstHVACIteration, currentLoad); + EXPECT_NEAR(0.14, thisCoolingPLHP->loadSideMassFlowRate, 0.001); + EXPECT_NEAR(1.29, thisCoolingPLHP->sourceSideMassFlowRate, 0.01); +} + +TEST_F(EnergyPlusFixture, TestSizing_FullyAutosizedCoolingWithCompanion_AirSource) +{ + std::string const idf_objects = delimited_string({"HeatPump:PlantLoop:EIR:Cooling,", + " hp cooling side,", + " node 1,", + " node 2,", + " AirSource,", + " node 3,", + " node 4,", + " ,", + " ,", + " hp heating side,", + " Autosize,", + " Autosize,", + " ,", + " Autosize,", + " 1.0,", + " 1,", + " dummyCurve,", + " dummyCurve,", + " dummyCurve;", + "HeatPump:PlantLoop:EIR:Heating,", + " hp heating side,", + " node 5,", + " node 6,", + " AirSource,", + " node 7,", + " node 8,", + " ,", + " ,", + " hp cooling side,", + " Autosize,", + " Autosize,", + " ,", + " Autosize,", + " 2.0,", + " 1,", + " dummyCurve,", + " dummyCurve,", + " dummyCurve;", + "Curve:Linear,", + " dummyCurve,", + " 1,", + " 0,", + " 1,", + " 1;"}); + ASSERT_TRUE(process_idf(idf_objects)); + state->init_state(*state); + + // call the factory with a valid name to trigger reading inputs + EIRPlantLoopHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpEIRCooling, "HP COOLING SIDE"); + + // verify the size of the vector and the processed condition + EXPECT_EQ(2u, state->dataEIRPlantLoopHeatPump->heatPumps.size()); + + // for now we know the order is maintained, so get each heat pump object + EIRPlantLoopHeatPump *thisCoolingPLHP = &state->dataEIRPlantLoopHeatPump->heatPumps[0]; + EIRPlantLoopHeatPump *thisHeatingPLHP = &state->dataEIRPlantLoopHeatPump->heatPumps[1]; + + // validate that we have the right ones + EXPECT_EQ("HP COOLING SIDE", thisCoolingPLHP->name); + EXPECT_EQ("HP HEATING SIDE", thisHeatingPLHP->name); + + // We'll set up two plant loops: a load and a source loop + state->dataPlnt->TotNumLoops = 1; + state->dataPlnt->PlantLoop.allocate(state->dataPlnt->TotNumLoops); + + state->dataPlnt->PlantLoop(1).FluidName = "WATER"; + state->dataPlnt->PlantLoop(1).glycol = Fluid::GetWater(*state); + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Demand).TotalBranches = 1; + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Demand).Branch.allocate(1); + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).TotalComponents = 1; + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp.allocate(1); + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).TotalBranches = 1; + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch.allocate(1); + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).TotalComponents = 2; + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp.allocate(2); + + Real64 constexpr plantSizingLoadVolFlow = 0.01; + Real64 constexpr plantSizingLoadDeltaT = 1.0; + + Real64 constexpr plantSizingSrcDeltaT = 10.0; + + state->dataSize->PlantSizData.allocate(2); + state->dataSize->PlantSizData(1).DesVolFlowRate = 0.01; + state->dataSize->PlantSizData(1).DeltaT = 1.0; + state->dataSize->PlantSizData(2).DesVolFlowRate = 0.03; + state->dataSize->PlantSizData(2).DeltaT = 1.0; + + auto &loop1supplyComponent1 = state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp(1); + auto &loop1supplyComponent2 = state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp(2); + + loop1supplyComponent1.Type = DataPlant::PlantEquipmentType::HeatPumpEIRHeating; + loop1supplyComponent2.Type = DataPlant::PlantEquipmentType::HeatPumpEIRCooling; + + loop1supplyComponent1.Name = thisHeatingPLHP->name; + loop1supplyComponent2.Name = thisCoolingPLHP->name; + + loop1supplyComponent1.NodeNumIn = thisHeatingPLHP->loadSideNodes.inlet; + loop1supplyComponent2.NodeNumIn = thisCoolingPLHP->loadSideNodes.inlet; + + // the init call expects a "from" calling point + PlantLocation myCoolingLoadLocation = PlantLocation(1, DataPlant::LoopSideLocation::Supply, 1, 1); + PlantLocation myHeatingLoadLocation = PlantLocation(1, DataPlant::LoopSideLocation::Supply, 1, 2); + + // set a couple global flags + state->dataGlobal->BeginEnvrnFlag = true; + + // initialize so the components can find themselves on the plant + thisCoolingPLHP->onInitLoopEquip(*state, myCoolingLoadLocation); + thisHeatingPLHP->onInitLoopEquip(*state, myHeatingLoadLocation); + + state->dataPlnt->PlantFinalSizesOkayToReport = true; + state->dataPlnt->PlantFirstSizesOkayToReport = true; + state->dataPlnt->PlantFirstSizesOkayToFinalize = true; + + // assign the plant sizing data + state->dataPlnt->PlantLoop(1).PlantSizNum = 1; + + Real64 constexpr sourceSideInitTemp = 20.0; + Real64 constexpr sourceSideHumRat = 0.0; + Real64 expectedLoadCp = 4197.93; + Real64 expectedLoadRho = 999.898; + Real64 expectedSourceCp = Psychrometrics::PsyCpAirFnW(sourceSideHumRat); + Real64 expectedSourceRho = Psychrometrics::PsyRhoAirFnPbTdbW(*state, state->dataEnvrn->StdBaroPress, sourceSideInitTemp, sourceSideHumRat); + Real64 expectedLoadFlow = plantSizingLoadVolFlow; + Real64 expectedCapacity = expectedLoadRho * expectedLoadFlow * expectedLoadCp * plantSizingLoadDeltaT; + Real64 expectedSourceLoad = 0.0; + expectedSourceLoad = expectedCapacity * (1 + 1 / thisCoolingPLHP->referenceCOP); + Real64 expectedSourceFlow = expectedSourceLoad / (expectedSourceCp * expectedSourceRho * plantSizingSrcDeltaT); + thisCoolingPLHP->sizeLoadSide(*state); + thisCoolingPLHP->sizeSrcSideASHP(*state); + EXPECT_NEAR(expectedLoadFlow, thisCoolingPLHP->loadSideDesignVolFlowRate, 0.0001); + EXPECT_NEAR(expectedSourceFlow, thisCoolingPLHP->sourceSideDesignVolFlowRate, 0.1); + EXPECT_NEAR(expectedCapacity, thisCoolingPLHP->referenceCapacity, 0.0001); + + // with a sizing run complete, we can also go ahead and get the design capacities... + // they should be nonzero for the load side of things + Real64 tmpMin = -1.0, tmpMax = -1.0, tmpOpt = -1.0; + thisCoolingPLHP->getDesignCapacities(*state, myCoolingLoadLocation, tmpMax, tmpMin, tmpOpt); + EXPECT_NEAR(0.0, tmpMin, 0.001); + EXPECT_NEAR(expectedCapacity, tmpMax, 0.001); + EXPECT_NEAR(expectedCapacity, tmpOpt, 0.001); + + // we can reset things and do a few more corner cases here + + // lets just try it again but with the plant sizing data set to zero flow, it should try to use the companion + // but the companion isn't sized yet, so it should get zero conditions + thisCoolingPLHP->loadSideDesignVolFlowRate = DataSizing::AutoSize; + thisCoolingPLHP->sourceSideDesignVolFlowRate = DataSizing::AutoSize; + thisCoolingPLHP->referenceCapacity = DataSizing::AutoSize; + state->dataSize->PlantSizData(1).DesVolFlowRate = 0.0; + thisCoolingPLHP->sizeLoadSide(*state); + thisCoolingPLHP->sizeSrcSideASHP(*state); + EXPECT_NEAR(0.0, thisCoolingPLHP->loadSideDesignVolFlowRate, 0.0001); + EXPECT_NEAR(0.0, thisCoolingPLHP->sourceSideDesignVolFlowRate, 0.0001); + EXPECT_NEAR(0.0, thisCoolingPLHP->referenceCapacity, 0.0001); + + // but now let's try to size the heating coil, which will try to use the cooling coil's sized data + thisCoolingPLHP->loadSideDesignVolFlowRate = expectedLoadFlow; + thisCoolingPLHP->sourceSideDesignVolFlowRate = expectedSourceFlow; + thisCoolingPLHP->referenceCapacity = expectedCapacity; + expectedCapacity = expectedLoadRho * expectedLoadFlow * expectedLoadCp * plantSizingLoadDeltaT; + expectedSourceLoad = expectedCapacity * (1 - 1 / thisHeatingPLHP->referenceCOP); + expectedSourceFlow = expectedSourceLoad / (expectedSourceCp * expectedSourceRho * plantSizingSrcDeltaT); + thisHeatingPLHP->sizeLoadSide(*state); + thisHeatingPLHP->sizeSrcSideASHP(*state); + EXPECT_NEAR(expectedLoadFlow, thisHeatingPLHP->loadSideDesignVolFlowRate, 0.0001); + EXPECT_NEAR(expectedSourceFlow, thisHeatingPLHP->sourceSideDesignVolFlowRate, 0.1); + EXPECT_NEAR(expectedCapacity, thisHeatingPLHP->referenceCapacity, 0.0001); +} + +TEST_F(EnergyPlusFixture, TestSizing_HardsizedFlowAutosizedCoolingWithCompanion_AirSource) +{ + std::string const idf_objects = delimited_string({"HeatPump:PlantLoop:EIR:Cooling,", + " hp cooling side,", + " node 1,", + " node 2,", + " AirSource,", + " node 3,", + " node 4,", + " ,", + " ,", + " hp heating side,", + " Autosize,", + " 2.0,", + " ,", + " Autosize,", + " 1.0,", + " 1,", + " dummyCurve,", + " dummyCurve,", + " dummyCurve;", + "HeatPump:PlantLoop:EIR:Heating,", + " hp heating side,", + " node 5,", + " node 6,", + " AirSource,", + " node 7,", + " node 8,", + " ,", + " ,", + " hp cooling side,", + " Autosize,", + " 2.0,", + " ,", + " Autosize,", + " 1.0,", + " 1,", + " dummyCurve,", + " dummyCurve,", + " dummyCurve;", + "Curve:Linear,", + " dummyCurve,", + " 1,", + " 0,", + " 1,", + " 1;"}); + ASSERT_TRUE(process_idf(idf_objects)); + state->init_state(*state); + + // call the factory with a valid name to trigger reading inputs + EIRPlantLoopHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpEIRCooling, "HP COOLING SIDE"); + + // verify the size of the vector and the processed condition + EXPECT_EQ(2u, state->dataEIRPlantLoopHeatPump->heatPumps.size()); + + // for now we know the order is maintained, so get each heat pump object + EIRPlantLoopHeatPump *thisCoolingPLHP = &state->dataEIRPlantLoopHeatPump->heatPumps[0]; + EIRPlantLoopHeatPump *thisHeatingPLHP = &state->dataEIRPlantLoopHeatPump->heatPumps[1]; + + // validate that we have the right ones + EXPECT_EQ("HP COOLING SIDE", thisCoolingPLHP->name); + EXPECT_EQ("HP HEATING SIDE", thisHeatingPLHP->name); + + // We'll set up two plant loops: a load and a source loop + state->dataPlnt->TotNumLoops = 1; + state->dataPlnt->PlantLoop.allocate(state->dataPlnt->TotNumLoops); + + state->dataPlnt->PlantLoop(1).FluidName = "WATER"; + state->dataPlnt->PlantLoop(1).glycol = Fluid::GetWater(*state); + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Demand).TotalBranches = 1; + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Demand).Branch.allocate(1); + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).TotalComponents = 1; + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp.allocate(1); + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).TotalBranches = 1; + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch.allocate(1); + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).TotalComponents = 2; + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp.allocate(2); + + Real64 constexpr plantSizingLoadVolFlow = 0.01; + Real64 constexpr plantSizingLoadDeltaT = 1.0; + + state->dataSize->PlantSizData.allocate(2); + state->dataSize->PlantSizData(1).DesVolFlowRate = 0.01; + state->dataSize->PlantSizData(1).DeltaT = 1.0; + state->dataSize->PlantSizData(2).DesVolFlowRate = 0.03; + state->dataSize->PlantSizData(2).DeltaT = 1.0; + + auto &loop1supplyComponent1 = state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp(1); + auto &loop1supplyComponent2 = state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp(2); + + loop1supplyComponent1.Type = DataPlant::PlantEquipmentType::HeatPumpEIRHeating; + loop1supplyComponent2.Type = DataPlant::PlantEquipmentType::HeatPumpEIRCooling; + + loop1supplyComponent1.Name = thisHeatingPLHP->name; + loop1supplyComponent2.Name = thisCoolingPLHP->name; + + loop1supplyComponent1.NodeNumIn = thisHeatingPLHP->loadSideNodes.inlet; + loop1supplyComponent2.NodeNumIn = thisCoolingPLHP->loadSideNodes.inlet; + + // the init call expects a "from" calling point + PlantLocation myCoolingLoadLocation = PlantLocation(1, DataPlant::LoopSideLocation::Supply, 1, 1); + PlantLocation myHeatingLoadLocation = PlantLocation(1, DataPlant::LoopSideLocation::Supply, 1, 2); + + // set a couple global flags + state->dataGlobal->BeginEnvrnFlag = true; + + // initialize so the components can find themselves on the plant + thisCoolingPLHP->onInitLoopEquip(*state, myCoolingLoadLocation); + thisHeatingPLHP->onInitLoopEquip(*state, myHeatingLoadLocation); + + state->dataPlnt->PlantFinalSizesOkayToReport = true; + state->dataPlnt->PlantFirstSizesOkayToReport = true; + state->dataPlnt->PlantFirstSizesOkayToFinalize = true; + + // assign the plant sizing data + state->dataPlnt->PlantLoop(1).PlantSizNum = 1; + + Real64 expectedLoadCp = 4197.93; + Real64 expectedLoadRho = 999.898; + Real64 expectedLoadFlow = plantSizingLoadVolFlow; + Real64 expectedCapacity = expectedLoadRho * expectedLoadFlow * expectedLoadCp * plantSizingLoadDeltaT; + Real64 expectedSourceFlow = 2.0; + thisCoolingPLHP->sizeLoadSide(*state); + thisCoolingPLHP->sizeSrcSideASHP(*state); + EXPECT_NEAR(expectedLoadFlow, thisCoolingPLHP->loadSideDesignVolFlowRate, 0.0001); + EXPECT_NEAR(expectedSourceFlow, thisCoolingPLHP->sourceSideDesignVolFlowRate, 0.1); + EXPECT_NEAR(expectedCapacity, thisCoolingPLHP->referenceCapacity, 0.0001); + + // with a sizing run complete, we can also go ahead and get the design capacities... + // they should be nonzero for the load side of things + Real64 tmpMin = -1.0, tmpMax = -1.0, tmpOpt = -1.0; + thisCoolingPLHP->getDesignCapacities(*state, myCoolingLoadLocation, tmpMax, tmpMin, tmpOpt); + EXPECT_NEAR(0.0, tmpMin, 0.001); + EXPECT_NEAR(expectedCapacity, tmpMax, 0.001); + EXPECT_NEAR(expectedCapacity, tmpOpt, 0.001); +} + +TEST_F(EnergyPlusFixture, TestSizing_AutosizedFlowWithCompanion_AirSource) +{ + std::string const idf_objects = delimited_string({"HeatPump:PlantLoop:EIR:Cooling,", + " hp cooling side,", + " node 1,", + " node 2,", + " AirSource,", + " node 3,", + " node 4,", + " ,", + " ,", + " hp heating side,", + " 0.005,", + " Autosize,", + " ,", + " Autosize,", + " 1.0,", + " 1,", + " dummyCurve,", + " dummyCurve,", + " dummyCurve;", + "HeatPump:PlantLoop:EIR:Heating,", + " hp heating side,", + " node 5,", + " node 6,", + " AirSource,", + " node 7,", + " node 8,", + " ,", + " ,", + " hp cooling side,", + " 0.005,", + " Autosize,", + " ,", + " Autosize,", + " 2.0,", + " 1,", + " dummyCurve,", + " dummyCurve,", + " dummyCurve;", + "Curve:Linear,", + " dummyCurve,", + " 1,", + " 0,", + " 1,", + " 1;"}); + ASSERT_TRUE(process_idf(idf_objects)); + state->init_state(*state); + + // call the factory with a valid name to trigger reading inputs + EIRPlantLoopHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpEIRCooling, "HP COOLING SIDE"); + + // verify the size of the vector and the processed condition + EXPECT_EQ(2u, state->dataEIRPlantLoopHeatPump->heatPumps.size()); + + // for now we know the order is maintained, so get each heat pump object + EIRPlantLoopHeatPump *thisCoolingPLHP = &state->dataEIRPlantLoopHeatPump->heatPumps[0]; + EIRPlantLoopHeatPump *thisHeatingPLHP = &state->dataEIRPlantLoopHeatPump->heatPumps[1]; + + // We'll set up two plant loops: a load and a source loop + state->dataPlnt->TotNumLoops = 1; + state->dataPlnt->PlantLoop.allocate(state->dataPlnt->TotNumLoops); + + state->dataPlnt->PlantLoop(1).FluidName = "WATER"; + state->dataPlnt->PlantLoop(1).glycol = Fluid::GetWater(*state); + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Demand).TotalBranches = 1; + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Demand).Branch.allocate(1); + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).TotalComponents = 1; + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp.allocate(1); + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).TotalBranches = 1; + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch.allocate(1); + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).TotalComponents = 2; + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp.allocate(2); + + state->dataSize->PlantSizData.allocate(2); + state->dataSize->PlantSizData(1).DeltaT = 25.0; + state->dataSize->PlantSizData(2).DeltaT = 20.0; + + auto &loop1supplyComponent1 = state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp(1); + auto &loop1supplyComponent2 = state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp(2); + + loop1supplyComponent1.Type = DataPlant::PlantEquipmentType::HeatPumpEIRHeating; + loop1supplyComponent2.Type = DataPlant::PlantEquipmentType::HeatPumpEIRCooling; + + loop1supplyComponent1.Name = thisHeatingPLHP->name; + loop1supplyComponent2.Name = thisCoolingPLHP->name; + + loop1supplyComponent1.NodeNumIn = thisHeatingPLHP->loadSideNodes.inlet; + loop1supplyComponent2.NodeNumIn = thisCoolingPLHP->loadSideNodes.inlet; + + // the init call expects a "from" calling point + PlantLocation myCoolingLoadLocation = PlantLocation(1, DataPlant::LoopSideLocation::Supply, 1, 1); + PlantLocation myHeatingLoadLocation = PlantLocation(1, DataPlant::LoopSideLocation::Supply, 1, 2); + + // set a couple global flags + state->dataGlobal->BeginEnvrnFlag = true; + + state->dataPlnt->PlantFinalSizesOkayToReport = true; + state->dataPlnt->PlantFirstSizesOkayToReport = true; + state->dataPlnt->PlantFirstSizesOkayToFinalize = true; + + // assign the plant sizing data + state->dataPlnt->PlantLoop(1).PlantSizNum = 1; + + // initialize so the components can find themselves on the plant + thisCoolingPLHP->onInitLoopEquip(*state, myCoolingLoadLocation); + thisHeatingPLHP->onInitLoopEquip(*state, myHeatingLoadLocation); + + Real64 expectedClgSourceFlow = 86.71; + Real64 expectedHtgSourceFlow = 21.68; // changed from 86.71 due to issue#10381; + EXPECT_NEAR(expectedClgSourceFlow, thisCoolingPLHP->sourceSideDesignVolFlowRate, 0.1); + EXPECT_NEAR(expectedHtgSourceFlow, thisHeatingPLHP->sourceSideDesignVolFlowRate, 0.1); +} + +TEST_F(EnergyPlusFixture, Test_DoPhysics) +{ + + std::string const idf_objects = delimited_string({"HeatPump:PlantLoop:EIR:Cooling,", + " hp cooling side,", + " node 1,", + " node 2,", + " WaterSource,", + " node 3,", + " node 4,", + " ,", + " ,", + " hp heating side,", + " 0.005,", + " 0.002,", + " ,", + " 20000,", + " 3.0,", + " 1,", + " CapCurveFuncTemp,", + " EIRCurveFuncTemp,", + " EIRCurveFuncPLR;", + "HeatPump:PlantLoop:EIR:Heating,", + " hp heating side,", + " node 5,", + " node 6,", + " WaterSource,", + " node 7,", + " node 8,", + " ,", + " ,", + " hp cooling side,", + " 0.005,", + " 0.002,", + " ,", + " 20000,", + " 3.0,", + " 1,", + " CapCurveFuncTemp,", + " EIRCurveFuncTemp,", + " EIRCurveFuncPLR;", + "Curve:Biquadratic,", + " CapCurveFuncTemp,", + " 1.0,", + " 0.0,", + " 0.0,", + " 0.0,", + " 0.0,", + " 0.0,", + " 5.0,", + " 10.0,", + " 24.0,", + " 35.0,", + " ,", + " ,", + " Temperature,", + " Temperature,", + " Dimensionless;", + "Curve:Biquadratic,", + " EIRCurveFuncTemp,", + " 1.0,", + " 0.0,", + " 0.0,", + " 1.0,", + " 0.0,", + " 0.0,", + " 5.0,", + " 10.0,", + " 24.0,", + " 35.0,", + " ,", + " ,", + " Temperature,", + " Temperature,", + " Dimensionless;", + "Curve:Quadratic,", + " EIRCurveFuncPLR,", + " 1.0,", + " 0.0,", + " 0.0,", + " 0.0,", + " 1.0;"}); + + ASSERT_TRUE(process_idf(idf_objects)); + state->init_state(*state); + + // set up the plant loops + // first the load side + state->dataPlnt->TotNumLoops = 2; + state->dataPlnt->PlantLoop.allocate(2); + + state->dataPlnt->PlantLoop(1).FluidName = "WATER"; + state->dataPlnt->PlantLoop(1).glycol = Fluid::GetWater(*state); + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).TotalBranches = 1; + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch.allocate(1); + 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 &PLHPPlantLoadSideLoop = state->dataPlnt->PlantLoop(1); + auto &PLHPPlantLoadSideComp = state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp(1); + PLHPPlantLoadSideComp.Type = DataPlant::PlantEquipmentType::HeatPumpEIRCooling; + // then the source side + + state->dataPlnt->PlantLoop(2).FluidName = "WATER"; + state->dataPlnt->PlantLoop(2).glycol = Fluid::GetWater(*state); + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).TotalBranches = 1; + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch.allocate(1); + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).TotalComponents = 1; + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp.allocate(1); + + auto &PLHPPlantLoadSourceComp = state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp(1); + PLHPPlantLoadSourceComp.Type = DataPlant::PlantEquipmentType::HeatPumpEIRCooling; + + // call the factory with a valid name to trigger reading inputs + EIRPlantLoopHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpEIRCooling, "HP COOLING SIDE"); + + // verify the size of the vector and the processed condition + EXPECT_EQ(2u, state->dataEIRPlantLoopHeatPump->heatPumps.size()); + + // for now we know the order is maintained, so get each heat pump object + EIRPlantLoopHeatPump *thisCoolingPLHP = &state->dataEIRPlantLoopHeatPump->heatPumps[0]; + + // do a little setup here + thisCoolingPLHP->loadSidePlantLoc.loopNum = 1; + thisCoolingPLHP->loadSidePlantLoc.loopSideNum = DataPlant::LoopSideLocation::Supply; + thisCoolingPLHP->loadSidePlantLoc.branchNum = 1; + thisCoolingPLHP->loadSidePlantLoc.compNum = 1; + PlantUtilities::SetPlantLocationLinks(*state, thisCoolingPLHP->loadSidePlantLoc); + thisCoolingPLHP->loadSideNodes.outlet = 1; + thisCoolingPLHP->sourceSidePlantLoc.loopNum = 2; + PlantUtilities::SetPlantLocationLinks(*state, thisCoolingPLHP->sourceSidePlantLoc); + + // the factory would've called GetOnlySingleNode for the in/out pairs on the PLHP, add another one for the loop + // outlet setpoint node + state->dataLoopNodes->Node.allocate(5); + PLHPPlantLoadSideLoop.TempSetPointNodeNum = 5; + + // set up the plant setpoint conditions and test for single setpoint operation + PLHPPlantLoadSideLoop.LoopDemandCalcScheme = DataPlant::LoopDemandCalcScheme::SingleSetPoint; + PLHPPlantLoadSideComp.CurOpSchemeType = DataPlant::OpScheme::CompSetPtBased; + state->dataLoopNodes->Node(thisCoolingPLHP->loadSideNodes.outlet).TempSetPoint = 3.141; + state->dataLoopNodes->Node(5).TempSetPoint = 2.718; + PLHPPlantLoadSideComp.CurOpSchemeType = DataPlant::OpScheme::CoolingRB; + + // test for dual setpoint operation + PLHPPlantLoadSideLoop.LoopDemandCalcScheme = DataPlant::LoopDemandCalcScheme::DualSetPointDeadBand; + PLHPPlantLoadSideComp.CurOpSchemeType = DataPlant::OpScheme::CompSetPtBased; + state->dataLoopNodes->Node(thisCoolingPLHP->loadSideNodes.outlet).TempSetPointHi = 6.282; + state->dataLoopNodes->Node(5).TempSetPointHi = 5.436; + PLHPPlantLoadSideComp.CurOpSchemeType = DataPlant::OpScheme::CoolingRB; + + state->dataHVACGlobal->TimeStepSys = 60; + state->dataHVACGlobal->TimeStepSysSec = state->dataHVACGlobal->TimeStepSys * Constant::rSecsInHour; + + Real64 curLoad = -10000; + + thisCoolingPLHP->loadSideMassFlowRate = 0.3; + thisCoolingPLHP->sourceSideMassFlowRate = 0.8; + thisCoolingPLHP->loadSideInletTemp = 20; + thisCoolingPLHP->sourceSideInletTemp = 20; + thisCoolingPLHP->doPhysics(*state, curLoad); + + EXPECT_NEAR(thisCoolingPLHP->loadSideOutletTemp, 12.00, 0.1); + EXPECT_NEAR(thisCoolingPLHP->sourceSideOutletTemp, 47.90, 0.1); +} + +TEST_F(EnergyPlusFixture, Test_DoPhysics_AWHP) +{ + + std::string const idf_objects = + delimited_string({"HeatPump:PlantLoop:EIR:Cooling,", + " hp cooling side,", + " node 1,", + " node 2,", + " AirSource,", + " node 3,", + " node 4,", + " ,", + " ,", + " hp heating side,", + " 0.005,", + " 0.002,", + " ,", + " 20000,", + " 3.0,", + " 1,", + " CapCurveFuncTemp,", + " EIRCurveFuncTemp,", + " EIRCurveFuncPLR;", + + "HeatPump:PlantLoop:EIR:Heating,", + " hp heating side,", + " node 5,", + " node 6,", + " AirSource,", + " node 7,", + " node 8,", + " ,", + " ,", + " hp cooling side,", + " 0.005,", + " 0.002,", + " ,", + " 20000,", + " 3.0,", + " 1,", + " CapCurveFuncTemp,", + " EIRCurveFuncTemp,", + " EIRCurveFuncPLR;", + + "HeatPump:AirToWater,", + "test_AWHP, !- Name", + ", !- Availability Schedule Name Heating", + ", !- Availability Schedule Name Cooling", + "Load , !- Operating Mode Control Method", + "SingleMode, !- Operating Mode Control Option for Multiple Unit", + ", !- Operating Mode Control Schedule Name", + ", !- Minimum Part Load Ratio", + "20 , !- Rated Inlet Air Temperature in Heating Mode", + "0.002, !- Rated Air Flow Rate in Heating Mode", + "50 , !- Rated Leaving Water Temperature in Heating Mode", + "0.005, !- Rated Water Flow Rate in Heating Mode", + "-20, !- Minimum Outdoor Air Temperature in Heating Mode", + "25, !- Maximum Outdoor Air Temperature in Heating Mode", + ", !- Minimum Leaving Water Temperature Curve Name in Heating Mode", + ", !- Maximum Leaving Water Temperature Curve Name in Heating Mode", + "1.0, !- Sizing Factor for Heating", + "25, !- Rated Inlet Air Temperature in Cooling Mode", + "0.002, !- Rated Air Flow Rate in Cooling Mode", + "22 , !- Rated Leaving Water Temperature in Cooling Mode", + "0.005, !- Rated Water Flow Rate in Cooling Mode", + "18 , !- Minimum Outdoor Air Temperature in Cooling Mode", + "40, !- Maximum Outdoor Air Temperature in Cooling Mode", + ", !- Minimum Leaving Water Temperature Curve Name in Cooling Mode", + ", !- Maximum Leaving Water Temperature Curve Name in Cooling Mode", + "0.9, !- Sizing Factor for Cooling", + "Outdoor Air Inlet Node , !- Air Inlet Node Name", + "Outdoor Air Outlet Node, !- Air Outlet Node Name", + "Heating Coil Load Loop Intermediate Node, !- Hot Water Inlet Node Name", + "Heating Coil Load Loop Supply Outlet Node, !- Hot Water Outlet Node Name", + "Cooling Coil Load Loop Intermediate Node , !- Chilled Water Inlet Node Name", + "Cooling Coil Load Loop Supply Outlet Node , !- Chilled Water Outlet Node Name", + "10, !- Maximum Outdoor Dry Bulb Temperature For Defrost Operation", + "Timed, !- Heat Pump Defrost Control", + "0.2, !- Defrost Time Period Fraction", + "150, !- Resistive Defrost Heater Capacity", + ", !- Defrost Energy Input Ratio Function of Temperature Curve Name", + "1 , !- Compressor Multiplier", + "FixedSpeed , !- Control Type", + "100 , !- Crankcase Heater Capacity", + "EIRCurveFuncPLR, !- Crankcase Heater Capacity Function of Temperature Curve Name", + "20 , !- Maximum Ambient Temperature for Crankcase Heater Operation", + "1 , !- Number of Speeds for Heating", + "20000, !- Rated Heating Capacity at Speed 1", + "3, !- Rated COP for Heating at Speed 1", + "CapCurveFuncTemp, !- Normalized Heating Capacity Function of Temperature Curve Name at Speed 1", + "EIRCurveFuncTemp, !- Heating Energy Input Ratio Function of Temperature Curve Name at Speed 1", + "EIRCurveFuncPLR, !- Heating Energy Input Ratio Function of PLR Curve Name at Speed 1", + ", !- Rated Heating Capacity at Speed 2", + ", !- Rated COP for Heating at Speed 2", + ", !- Normalized Heating Capacity Function of Temperature Curve Name at Speed 2", + ", !- Heating Energy Input Ratio Function of Temperature Curve Name at Speed 2", + ", !- Heating Energy Input Ratio Function of PLR Curve Name at Speed 2", + ", !- Rated Heating Capacity at Speed 3", + ", !- Rated COP for Heating at Speed 3", + ", !- Normalized Heating Capacity Function of Temperature Curve Name at Speed 3", + ", !- Heating Energy Input Ratio Function of Temperature Curve Name at Speed 3", + ", !- Heating Energy Input Ratio Function of PLR Curve Name at Speed 3", + ", !- Rated Heating Capacity at Speed 4", + ", !- Rated COP for Heating at Speed 4", + ", !- Normalized Heating Capacity Function of Temperature Curve Name at Speed 4", + ", !- Heating Energy Input Ratio Function of Temperature Curve Name at Speed 4", + ", !- Heating Energy Input Ratio Function of PLR Curve Name at Speed 4", + ", !- Rated Heating Capacity at Speed 5", + ", !- Rated COP for Heating at Speed 5", + ", !- Normalized Heating Capacity Function of Temperature Curve Name at Speed 5", + ", !- Heating Energy Input Ratio Function of Temperature Curve Name at Speed 5", + ", !- Heating Energy Input Ratio Function of PLR Curve Name at Speed 5", + ", !- Booster Mode On Heating", + ", !- Rated Heating Capacity in Booster Mode", + ", !- Rated Heating COP in Booster Mode", + ", !- Normalized Heating Capacity Function of Temperature Curve Name in Booster Mode", + ", !- Heating Energy Input Ratio Function of Temperature Curve Name in Booster Mode", + ", !- Heating Energy Input Ratio Function of PLR Curve Name in Booster Mode", + "1, !- Number of Speeds for Cooling", + "20000, !- Rated Cooling Capacity at Speed 1", + "3, !- Rated COP for Cooling at Speed 1", + "CapCurveFuncTemp, !- Normalized Cooling Capacity Function of Temperature Curve Name at Speed 1", + "EIRCurveFuncTemp, !- Cooling Energy Input Ratio Function of Temperature Curve Name at Speed 1", + "EIRCurveFuncPLR, !- Cooling Energy Input Ratio Function of PLR Curve Name at Speed 1", + ", !- Rated Cooling Capacity at Speed 2", + ", !- Rated COP for Cooling at Speed 2", + ", !- Normalized Cooling Capacity Function of Temperature Curve Name at Speed 2", + ", !- Cooling Energy Input Ratio Function of Temperature Curve Name at Speed 2", + ", !- Cooling Energy Input Ratio Function of PLR Curve Name at Speed 2", + ", !- Rated Cooling Capacity at Speed 3", + ", !- Rated COP for Cooling at Speed 3", + ", !- Normalized Cooling Capacity Function of Temperature Curve Name at Speed 3", + ", !- Cooling Energy Input Ratio Function of Temperature Curve Name at Speed 3", + ", !- Cooling Energy Input Ratio Function of PLR Curve Name at Speed 3", + ", !- Rated Cooling Capacity at Speed 4", + ", !- Rated COP for Cooling at Speed 4", + ", !- Normalized Cooling Capacity Function of Temperature Curve Name at Speed 4", + ", !- Cooling Energy Input Ratio Function of Temperature Curve Name at Speed 4", + ", !- Cooling Energy Input Ratio Function of PLR Curve Name at Speed 4", + ", !- Rated Cooling Capacity at Speed 5", + ", !- Rated COP for Cooling at Speed 5", + ", !- Normalized Cooling Capacity Function of Temperature Curve Name at Speed 5", + ", !- Cooling Energy Input Ratio Function of Temperature Curve Name at Speed 5", + ", !- Cooling Energy Input Ratio Function of PLR Curve Name at Speed 5", + ", !- Booster Mode On Cooling", + ", !- Rated Cooling Capacity in Booster Mode", + ", !- Rated Cooling COP in Booster Mode", + ", !- Normalized Cooling Capacity Function of Temperature Curve Name in Booster Mode", + ", !- Cooling Energy Input Ratio Function of Temperature Curve Name in Booster Mode", + "; !- Cooling Energy Input Ratio Function of PLR Curve Name in Booster Mode", + + "Curve:Biquadratic,", + " CapCurveFuncTemp,", + " 1.0,", + " 0.0,", + " 0.0,", + " 0.0,", + " 0.0,", + " 0.0,", + " 5.0,", + " 10.0,", + " 24.0,", + " 35.0,", + " ,", + " ,", + " Temperature,", + " Temperature,", + " Dimensionless;", + "Curve:Biquadratic,", + " EIRCurveFuncTemp,", + " 1.0,", + " 0.0,", + " 0.0,", + " 1.0,", + " 0.0,", + " 0.0,", + " 5.0,", + " 10.0,", + " 24.0,", + " 35.0,", + " ,", + " ,", + " Temperature,", + " Temperature,", + " Dimensionless;", + "Curve:Quadratic,", + " EIRCurveFuncPLR,", + " 1.0,", + " 0.0,", + " 0.0,", + " 0.0,", + " 1.0;"}); + ASSERT_TRUE(process_idf(idf_objects)); + state->init_state(*state); + + // set up the plant loops + // first the load side + state->dataPlnt->TotNumLoops = 5; + state->dataPlnt->PlantLoop.allocate(5); + + state->dataPlnt->PlantLoop(1).FluidName = "WATER"; + state->dataPlnt->PlantLoop(1).glycol = Fluid::GetWater(*state); + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).TotalBranches = 1; + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch.allocate(1); + 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 &AWHPPlantLoadSideLoop = state->dataPlnt->PlantLoop(1); + auto &AWHPPlantLoadSideComp = state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp(1); + AWHPPlantLoadSideComp.Type = DataPlant::PlantEquipmentType::HeatPumpAirToWaterCooling; + // then the source side + + state->dataPlnt->PlantLoop(2).FluidName = "Air"; + state->dataPlnt->PlantLoop(2).glycol = Fluid::GetWater(*state); + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).TotalBranches = 1; + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch.allocate(1); + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).TotalComponents = 1; + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp.allocate(1); + + // call the factory with a valid name to trigger reading inputs + DataPlant::PlantEquipmentType equipType = DataPlant::PlantEquipmentType::HeatPumpAirToWaterCooling; + HeatPumpAirToWater::factory(*state, equipType, "test_AWHP"); + + state->dataPlnt->PlantLoop(3).FluidName = "WATER"; + state->dataPlnt->PlantLoop(3).glycol = Fluid::GetWater(*state); + state->dataPlnt->PlantLoop(3).LoopSide(DataPlant::LoopSideLocation::Supply).TotalBranches = 1; + state->dataPlnt->PlantLoop(3).LoopSide(DataPlant::LoopSideLocation::Supply).Branch.allocate(1); + state->dataPlnt->PlantLoop(3).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).TotalComponents = 1; + state->dataPlnt->PlantLoop(3).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp.allocate(1); + auto &PLHPPlantLoadSideLoop = state->dataPlnt->PlantLoop(1); + auto &PLHPPlantLoadSideComp = state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp(1); + PLHPPlantLoadSideComp.Type = DataPlant::PlantEquipmentType::HeatPumpAirToWaterCooling; + // then the source side + + state->dataPlnt->PlantLoop(4).FluidName = "Air"; + state->dataPlnt->PlantLoop(4).glycol = Fluid::GetWater(*state); + state->dataPlnt->PlantLoop(4).LoopSide(DataPlant::LoopSideLocation::Demand).TotalBranches = 1; + state->dataPlnt->PlantLoop(4).LoopSide(DataPlant::LoopSideLocation::Demand).Branch.allocate(1); + state->dataPlnt->PlantLoop(4).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).TotalComponents = 1; + state->dataPlnt->PlantLoop(4).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp.allocate(1); + + auto &PLHPPlantLoadSourceComp = state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp(1); + PLHPPlantLoadSourceComp.Type = DataPlant::PlantEquipmentType::HeatPumpEIRCooling; + + state->dataPlnt->PlantLoop(5).FluidName = "WATER"; + state->dataPlnt->PlantLoop(5).glycol = Fluid::GetWater(*state); + state->dataPlnt->PlantLoop(5).LoopSide(DataPlant::LoopSideLocation::Supply).TotalBranches = 1; + state->dataPlnt->PlantLoop(5).LoopSide(DataPlant::LoopSideLocation::Supply).Branch.allocate(1); + state->dataPlnt->PlantLoop(5).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).TotalComponents = 1; + state->dataPlnt->PlantLoop(5).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp.allocate(1); + auto &AWHPHeatPlantLoadSideComp = state->dataPlnt->PlantLoop(5).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp(1); + AWHPHeatPlantLoadSideComp.Type = DataPlant::PlantEquipmentType::HeatPumpAirToWaterHeating; + + // call the factory with a valid name to trigger reading inputs + EIRPlantLoopHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpEIRCooling, "hp cooling side"); + + // verify the size of the vector and the processed condition + EXPECT_EQ(2u, state->dataHeatPumpAirToWater->heatPumps.size()); + EXPECT_EQ(2u, state->dataEIRPlantLoopHeatPump->heatPumps.size()); + + // for now we know the order is maintained, so get each heat pump object + HeatPumpAirToWater *thisCoolingAWHP = &state->dataHeatPumpAirToWater->heatPumps[0]; + EIRPlantLoopHeatPump *thisCoolingPLHP = &state->dataEIRPlantLoopHeatPump->heatPumps[0]; + HeatPumpAirToWater *thisHeatingAWHP = &state->dataHeatPumpAirToWater->heatPumps[1]; + thisCoolingAWHP->companionHeatPumpCoil = thisHeatingAWHP; + thisHeatingAWHP->companionHeatPumpCoil = thisCoolingAWHP; + + // do a little setup here + thisCoolingAWHP->loadSidePlantLoc.loopNum = 1; + thisCoolingAWHP->loadSidePlantLoc.loopSideNum = DataPlant::LoopSideLocation::Supply; + thisCoolingAWHP->loadSidePlantLoc.branchNum = 1; + thisCoolingAWHP->loadSidePlantLoc.compNum = 1; + thisHeatingAWHP->loadSidePlantLoc.loopNum = 5; + thisHeatingAWHP->loadSidePlantLoc.loopSideNum = DataPlant::LoopSideLocation::Supply; + thisHeatingAWHP->loadSidePlantLoc.branchNum = 1; + thisHeatingAWHP->loadSidePlantLoc.compNum = 1; + PlantUtilities::SetPlantLocationLinks(*state, thisCoolingAWHP->loadSidePlantLoc); + thisCoolingAWHP->loadSideNodes.outlet = 1; + thisCoolingAWHP->sourceSidePlantLoc.loopNum = 2; + PlantUtilities::SetPlantLocationLinks(*state, thisCoolingAWHP->sourceSidePlantLoc); + + thisCoolingPLHP->loadSidePlantLoc.loopNum = 1; + thisCoolingPLHP->loadSidePlantLoc.loopSideNum = DataPlant::LoopSideLocation::Supply; + thisCoolingPLHP->loadSidePlantLoc.branchNum = 1; + thisCoolingPLHP->loadSidePlantLoc.compNum = 1; + PlantUtilities::SetPlantLocationLinks(*state, thisCoolingPLHP->loadSidePlantLoc); + thisCoolingPLHP->loadSideNodes.outlet = 1; + thisCoolingPLHP->sourceSidePlantLoc.loopNum = 2; + PlantUtilities::SetPlantLocationLinks(*state, thisCoolingPLHP->sourceSidePlantLoc); + + // the factory would've called GetOnlySingleNode for the in/out pairs on the PLHP, add another one for the loop + // outlet setpoint node + state->dataLoopNodes->Node.allocate(10); + AWHPPlantLoadSideLoop.TempSetPointNodeNum = 5; + PLHPPlantLoadSideLoop.TempSetPointNodeNum = 10; + + // set up the plant setpoint conditions and test for single setpoint operation + AWHPPlantLoadSideLoop.LoopDemandCalcScheme = DataPlant::LoopDemandCalcScheme::SingleSetPoint; + AWHPPlantLoadSideComp.CurOpSchemeType = DataPlant::OpScheme::CompSetPtBased; + state->dataLoopNodes->Node(thisCoolingAWHP->loadSideNodes.outlet).TempSetPoint = 3.141; + state->dataLoopNodes->Node(5).TempSetPoint = 2.718; + AWHPPlantLoadSideComp.CurOpSchemeType = DataPlant::OpScheme::CoolingRB; + + // test for dual setpoint operation + AWHPPlantLoadSideLoop.LoopDemandCalcScheme = DataPlant::LoopDemandCalcScheme::DualSetPointDeadBand; + AWHPPlantLoadSideComp.CurOpSchemeType = DataPlant::OpScheme::CompSetPtBased; + state->dataLoopNodes->Node(thisCoolingAWHP->loadSideNodes.outlet).TempSetPointHi = 6.282; + state->dataLoopNodes->Node(5).TempSetPointHi = 5.436; + AWHPPlantLoadSideComp.CurOpSchemeType = DataPlant::OpScheme::CoolingRB; + + // set up the plant setpoint conditions and test for single setpoint operation + PLHPPlantLoadSideLoop.LoopDemandCalcScheme = DataPlant::LoopDemandCalcScheme::SingleSetPoint; + PLHPPlantLoadSideComp.CurOpSchemeType = DataPlant::OpScheme::CompSetPtBased; + state->dataLoopNodes->Node(thisCoolingPLHP->loadSideNodes.outlet).TempSetPoint = 3.141; + state->dataLoopNodes->Node(10).TempSetPoint = 2.718; + PLHPPlantLoadSideComp.CurOpSchemeType = DataPlant::OpScheme::CoolingRB; + + // test for dual setpoint operation + PLHPPlantLoadSideLoop.LoopDemandCalcScheme = DataPlant::LoopDemandCalcScheme::DualSetPointDeadBand; + PLHPPlantLoadSideComp.CurOpSchemeType = DataPlant::OpScheme::CompSetPtBased; + state->dataLoopNodes->Node(thisCoolingAWHP->loadSideNodes.outlet).TempSetPointHi = 6.282; + state->dataLoopNodes->Node(10).TempSetPointHi = 5.436; + PLHPPlantLoadSideComp.CurOpSchemeType = DataPlant::OpScheme::CoolingRB; + + state->dataHVACGlobal->TimeStepSys = 60; + state->dataHVACGlobal->TimeStepSysSec = state->dataHVACGlobal->TimeStepSys * Constant::rSecsInHour; + + Real64 curLoad = -10000; + + thisCoolingAWHP->loadSideMassFlowRate = 0.3; + thisCoolingAWHP->sourceSideMassFlowRate = 0.8; + thisCoolingAWHP->loadSideInletTemp = 20; + thisCoolingAWHP->sourceSideInletTemp = 20; + thisCoolingAWHP->doPhysics(*state, curLoad); + + thisCoolingPLHP->loadSideMassFlowRate = 0.3; + thisCoolingPLHP->sourceSideMassFlowRate = 0.8; + thisCoolingPLHP->loadSideInletTemp = 20; + thisCoolingPLHP->sourceSideInletTemp = 20; + thisCoolingPLHP->doPhysics(*state, curLoad); + + EXPECT_NEAR(thisCoolingAWHP->loadSideOutletTemp, thisCoolingPLHP->loadSideOutletTemp, 0.1); + // this value is from the Test_DoPhysics with WaterSource changed to AirSource + EXPECT_NEAR(thisCoolingAWHP->sourceSideOutletTemp, thisCoolingPLHP->sourceSideOutletTemp, 0.1); + // fixme: add cases with 2 or more speed levels +} + +TEST_F(EnergyPlusFixture, CoolingMetering) +{ + std::string const idf_objects = delimited_string({"HeatPump:PlantLoop:EIR:Cooling,", + " hp cooling side,", + " node 1,", + " node 2,", + " WaterSource,", + " node 3,", + " node 4,", + " ,", + " ,", + " ,", + " 0.0001,", + " 0.0001,", + " ,", + " 1000,", + " 3.14,", + " ,", + " dummyCurve,", + " dummyCurve,", + " dummyCurve;", + "Curve:Linear,", + " dummyCurve,", + " 0.95,", + " 0,", + " 1,", + " 1;"}); + ASSERT_TRUE(process_idf(idf_objects)); + state->init_state(*state); + + // set up the plant loops + // first the load side + state->dataPlnt->TotNumLoops = 2; + state->dataPlnt->PlantLoop.allocate(2); + + state->dataPlnt->PlantLoop(1).FluidName = "WATER"; + state->dataPlnt->PlantLoop(1).glycol = Fluid::GetWater(*state); + state->dataPlnt->PlantLoop(1).LoopDemandCalcScheme = DataPlant::LoopDemandCalcScheme::SingleSetPoint; + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).TotalBranches = 1; + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch.allocate(1); + 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 &PLHPPlantLoadSideComp = state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp(1); + PLHPPlantLoadSideComp.Type = DataPlant::PlantEquipmentType::HeatPumpEIRCooling; + PLHPPlantLoadSideComp.CurOpSchemeType = DataPlant::OpScheme::CompSetPtBased; + // then the source side + + state->dataPlnt->PlantLoop(2).FluidName = "WATER"; + state->dataPlnt->PlantLoop(2).glycol = Fluid::GetWater(*state); + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).TotalBranches = 1; + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch.allocate(1); + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).TotalComponents = 1; + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp.allocate(1); + auto &PLHPPlantLoadSourceComp = state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp(1); + PLHPPlantLoadSourceComp.Type = DataPlant::PlantEquipmentType::HeatPumpEIRCooling; + + // the init call expects a "from" calling point + PlantLocation myLoadLocation = PlantLocation(1, DataPlant::LoopSideLocation::Supply, 1, 1); + + // call the factory with a valid name to trigger reading inputs + EIRPlantLoopHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpEIRCooling, "HP COOLING SIDE"); + + // verify the size of the vector and the processed condition + EXPECT_EQ(1u, state->dataEIRPlantLoopHeatPump->heatPumps.size()); + + // for now we know the order is maintained, so get each heat pump object + EIRPlantLoopHeatPump *thisCoolingPLHP = &state->dataEIRPlantLoopHeatPump->heatPumps[0]; + + // do a bit of extra wiring up to the plant + PLHPPlantLoadSideComp.Name = thisCoolingPLHP->name; + PLHPPlantLoadSideComp.NodeNumIn = thisCoolingPLHP->loadSideNodes.inlet; + PLHPPlantLoadSourceComp.Name = thisCoolingPLHP->name; + PLHPPlantLoadSourceComp.NodeNumIn = thisCoolingPLHP->sourceSideNodes.inlet; + + // call for all initialization + state->dataGlobal->BeginEnvrnFlag = true; + state->dataPlnt->PlantFirstSizesOkayToFinalize = true; + thisCoolingPLHP->onInitLoopEquip(*state, myLoadLocation); + + int NumFound; + std::string TypeOfComp = "HeatPump:PlantLoop:EIR:Cooling"; + std::string NameOfComp = thisCoolingPLHP->name; + int NumVariables = GetNumMeteredVariables(*state, TypeOfComp, NameOfComp); + Array1D meteredVars(NumVariables); // Variable Types (1=integer, 2=real, 3=meter) + + NumFound = GetMeteredVariables(*state, NameOfComp, meteredVars); + + EXPECT_EQ(2, NumFound); + EXPECT_ENUM_EQ(meteredVars(1).resource, Constant::eResource::EnergyTransfer); // ENERGYTRANSFER + EXPECT_ENUM_EQ(meteredVars(1).endUseCat, OutputProcessor::EndUseCat::Invalid); + EXPECT_ENUM_EQ(meteredVars(1).group, OutputProcessor::Group::Plant); + EXPECT_ENUM_EQ(meteredVars(2).resource, Constant::eResource::Electricity); // Electric + EXPECT_ENUM_EQ(meteredVars(2).endUseCat, OutputProcessor::EndUseCat::Cooling); + EXPECT_ENUM_EQ(meteredVars(2).group, OutputProcessor::Group::Plant); +} + +TEST_F(EnergyPlusFixture, HeatingMetering) +{ + std::string const idf_objects = delimited_string({"HeatPump:PlantLoop:EIR:Heating,", + " hp heating side,", + " node 1,", + " node 2,", + " WaterSource,", + " node 3,", + " node 4,", + " ,", + " ,", + " ,", + " 0.0001,", + " 0.0001,", + " ,", + " 1000,", + " 3.14,", + " ,", + " dummyCurve,", + " dummyCurve,", + " dummyCurve;", + "Curve:Linear,", + " dummyCurve,", + " 0.95,", + " 0,", + " 1,", + " 1;"}); + ASSERT_TRUE(process_idf(idf_objects)); + state->init_state(*state); + + // set up the plant loops + // first the load side + state->dataPlnt->TotNumLoops = 2; + state->dataPlnt->PlantLoop.allocate(2); + + state->dataPlnt->PlantLoop(1).FluidName = "WATER"; + state->dataPlnt->PlantLoop(1).glycol = Fluid::GetWater(*state); + state->dataPlnt->PlantLoop(1).LoopDemandCalcScheme = DataPlant::LoopDemandCalcScheme::SingleSetPoint; + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).TotalBranches = 1; + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch.allocate(1); + 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 &PLHPPlantLoadSideComp = state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp(1); + PLHPPlantLoadSideComp.Type = DataPlant::PlantEquipmentType::HeatPumpEIRHeating; + PLHPPlantLoadSideComp.CurOpSchemeType = DataPlant::OpScheme::CompSetPtBased; + // then the source side + + state->dataPlnt->PlantLoop(2).FluidName = "WATER"; + state->dataPlnt->PlantLoop(2).glycol = Fluid::GetWater(*state); + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).TotalBranches = 1; + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch.allocate(1); + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).TotalComponents = 1; + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp.allocate(1); + auto &PLHPPlantLoadSourceComp = state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp(1); + PLHPPlantLoadSourceComp.Type = DataPlant::PlantEquipmentType::HeatPumpEIRHeating; + + // the init call expects a "from" calling point + PlantLocation myLoadLocation = PlantLocation(1, DataPlant::LoopSideLocation::Supply, 1, 1); + + // call the factory with a valid name to trigger reading inputs + EIRPlantLoopHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpEIRHeating, "HP HEATING SIDE"); + + // verify the size of the vector and the processed condition + EXPECT_EQ(1u, state->dataEIRPlantLoopHeatPump->heatPumps.size()); + + // for now we know the order is maintained, so get each heat pump object + EIRPlantLoopHeatPump *thisHeatingPLHP = &state->dataEIRPlantLoopHeatPump->heatPumps[0]; + + // do a bit of extra wiring up to the plant + PLHPPlantLoadSideComp.Name = thisHeatingPLHP->name; + PLHPPlantLoadSideComp.NodeNumIn = thisHeatingPLHP->loadSideNodes.inlet; + PLHPPlantLoadSourceComp.Name = thisHeatingPLHP->name; + PLHPPlantLoadSourceComp.NodeNumIn = thisHeatingPLHP->sourceSideNodes.inlet; + + // call for all initialization + state->dataGlobal->BeginEnvrnFlag = true; + state->dataPlnt->PlantFirstSizesOkayToFinalize = true; + thisHeatingPLHP->onInitLoopEquip(*state, myLoadLocation); + + int NumFound; + + std::string TypeOfComp = "HeatPump:PlantLoop:EIR:Heating"; + std::string NameOfComp = thisHeatingPLHP->name; + int NumVariables = GetNumMeteredVariables(*state, TypeOfComp, NameOfComp); + + Array1D_int VarIndexes(NumVariables); // Variable Numbers + Array1D meteredVars(NumVariables); // Variable Types (1=integer, 2=real, 3=meter) + + NumFound = GetMeteredVariables(*state, NameOfComp, meteredVars); + + EXPECT_EQ(2, NumFound); + EXPECT_ENUM_EQ(meteredVars(1).resource, Constant::eResource::EnergyTransfer); // ENERGYTRANSFER + EXPECT_ENUM_EQ(meteredVars(1).endUseCat, OutputProcessor::EndUseCat::Invalid); + EXPECT_ENUM_EQ(meteredVars(1).group, OutputProcessor::Group::Plant); + EXPECT_ENUM_EQ(meteredVars(2).resource, Constant::eResource::Electricity); // Electric + EXPECT_ENUM_EQ(meteredVars(2).endUseCat, OutputProcessor::EndUseCat::Heating); + EXPECT_ENUM_EQ(meteredVars(2).group, OutputProcessor::Group::Plant); +} + +TEST_F(EnergyPlusFixture, TestOperatingFlowRates_FullyAutosized_AirSource) +{ + std::string const idf_objects = + delimited_string({"HeatPump:PlantLoop:EIR:Cooling,", + " hp cooling side,", + " node 1,", + " node 2,", + " AirSource,", + " node 3,", + " node 4,", + " ,", + " ,", + " ,", + " Autosize,", + " Autosize,", + " ,", + " Autosize,", + " 3.14,", + " ,", + " dummyCurve,", + " dummyCurve,", + " dummyCurve,", + " ,", + " ,", + " ,", + " ,", + " ,", + " ,", + " ,", + " ,", + " ,", + " ,", + " ThermoCapFracCurve;", + + "Curve:Linear, ThermoCapFracCurve, 0.0, 0.06, 0.0, 10.0, 0.0, 1.0, Dimensionless, Dimensionless;", + "Curve:Linear,", + " dummyCurve,", + " 1,", + " 0,", + " 1,", + " 1;"}); + ASSERT_TRUE(process_idf(idf_objects)); + state->init_state(*state); + + bool firstHVACIteration = true; + // set up the plant loops + // first the load side + state->dataPlnt->TotNumLoops = 1; + state->dataPlnt->PlantLoop.allocate(1); + + state->dataPlnt->PlantLoop(1).FluidName = "WATER"; + state->dataPlnt->PlantLoop(1).glycol = Fluid::GetWater(*state); + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).TotalBranches = 1; + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch.allocate(1); + 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 &PLHPPlantLoadSideComp = state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp(1); + PLHPPlantLoadSideComp.Type = DataPlant::PlantEquipmentType::HeatPumpEIRCooling; + + // the init call expects a "from" calling point + PlantLocation myLocation = PlantLocation(1, DataPlant::LoopSideLocation::Supply, 1, 1); + + // call the factory with a valid name to trigger reading inputs + EIRPlantLoopHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpEIRCooling, "HP COOLING SIDE"); + + // verify the size of the vector and the processed condition + EXPECT_EQ(1u, state->dataEIRPlantLoopHeatPump->heatPumps.size()); + + // for now we know the order is maintained, so get each heat pump object + EIRPlantLoopHeatPump *thisCoolingPLHP = &state->dataEIRPlantLoopHeatPump->heatPumps[0]; + + // do a bit of extra wiring up to the plant + PLHPPlantLoadSideComp.Name = thisCoolingPLHP->name; + PLHPPlantLoadSideComp.NodeNumIn = thisCoolingPLHP->loadSideNodes.inlet; + + state->dataSize->PlantSizData.allocate(1); + state->dataSize->PlantSizData(1).DesVolFlowRate = 0.010; + state->dataSize->PlantSizData(1).DeltaT = 1.0; + + // call for all initialization + state->dataGlobal->BeginEnvrnFlag = true; + thisCoolingPLHP->onInitLoopEquip(*state, myLocation); + + state->dataPlnt->PlantFinalSizesOkayToReport = true; + state->dataPlnt->PlantFirstSizesOkayToReport = true; + state->dataPlnt->PlantFirstSizesOkayToFinalize = true; + + // assign the plant sizing data + state->dataPlnt->PlantLoop(1).PlantSizNum = 1; + + // call with run flag ON, flow locked at nonzero both + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).FlowLock = DataPlant::FlowLock::Locked; + state->dataLoopNodes->Node(thisCoolingPLHP->loadSideNodes.inlet).MassFlowRate = 1.0; + thisCoolingPLHP->running = true; + thisCoolingPLHP->sizeLoadSide(*state); + thisCoolingPLHP->sizeSrcSideASHP(*state); + Real64 constexpr currentLoad = 0.0; + thisCoolingPLHP->setOperatingFlowRatesASHP(*state, firstHVACIteration, currentLoad); + EXPECT_NEAR(1.0, thisCoolingPLHP->loadSideMassFlowRate, 0.001); + EXPECT_TRUE(thisCoolingPLHP->running); + + // test thermosiphon model + state->dataLoopNodes->Node(thisCoolingPLHP->loadSideNodes.inlet).Temp = 10.0; + state->dataLoopNodes->Node(thisCoolingPLHP->loadSideNodes.outlet).Temp = 6.0; + state->dataLoopNodes->Node(thisCoolingPLHP->loadSideNodes.outlet).TempSetPoint = 6.0; + state->dataLoopNodes->Node(thisCoolingPLHP->sourceSideNodes.inlet).Temp = 12.0; // condenser inlet temp > evap outlet temp + state->dataPlnt->PlantLoop(1).LoopDemandCalcScheme = DataPlant::LoopDemandCalcScheme::SingleSetPoint; + state->dataPlnt->PlantLoop(1).TempSetPointNodeNum = thisCoolingPLHP->loadSideNodes.outlet; + Real64 CurLoad = -20000.0; + bool RunFlag = true; + EnergyPlus::PlantLocation calledFromLocation = PlantLocation(1, DataPlant::LoopSideLocation::Supply, 1, 1); + + thisCoolingPLHP->simulate(*state, calledFromLocation, firstHVACIteration, CurLoad, RunFlag); + EXPECT_GT(thisCoolingPLHP->partLoadRatio, 0.4); // load is large + EXPECT_EQ(thisCoolingPLHP->thermosiphonStatus, 0); // thermosiphon is off + EXPECT_GT(thisCoolingPLHP->powerUsage, 6300.0); // power is non-zero + + state->dataLoopNodes->Node(thisCoolingPLHP->sourceSideNodes.inlet).Temp = 5.0; // condenser inlet temp < evap outlet temp + + thisCoolingPLHP->simulate(*state, calledFromLocation, firstHVACIteration, CurLoad, RunFlag); + EXPECT_GT(thisCoolingPLHP->partLoadRatio, 0.4); // load is large + EXPECT_EQ(thisCoolingPLHP->thermosiphonStatus, 0); // thermosiphon is off + EXPECT_GT(thisCoolingPLHP->powerUsage, 6300.0); // power is non-zero + + CurLoad /= 20.0; // reduce load such that thermosiphon can meet load + thisCoolingPLHP->simulate(*state, calledFromLocation, firstHVACIteration, CurLoad, RunFlag); + EXPECT_GT(thisCoolingPLHP->partLoadRatio, 0.02); // load is small + EXPECT_EQ(thisCoolingPLHP->thermosiphonStatus, 1); // thermosiphon is on + EXPECT_EQ(thisCoolingPLHP->powerUsage, 0.0); // power is zero +} + +TEST_F(EnergyPlusFixture, Test_Curve_Negative_Energy) +{ + + std::string const idf_objects = delimited_string({"HeatPump:PlantLoop:EIR:Cooling,", + " hp cooling side,", + " node 1,", + " node 2,", + " WaterSource,", + " node 3,", + " node 4,", + " ,", + " ,", + " hp heating side,", + " 0.005,", + " 0.002,", + " ,", + " 20000,", + " 3.0,", + " 1,", + " CapCurveFuncTemp,", + " EIRCurveFuncTemp,", + " badEIRCurveFuncPLR;", + "HeatPump:PlantLoop:EIR:Heating,", + " hp heating side,", + " node 5,", + " node 6,", + " WaterSource,", + " node 7,", + " node 8,", + " ,", + " ,", + " hp cooling side,", + " 0.005,", + " 0.002,", + " ,", + " 20000,", + " 3.0,", + " 1,", + " CapCurveFuncTemp,", + " EIRCurveFuncTemp,", + " EIRCurveFuncPLR;", + "Curve:Biquadratic,", + " CapCurveFuncTemp,", + " 1.0,", + " 0.0,", + " 0.0,", + " 0.0,", + " 0.0,", + " 0.0,", + " 5.0,", + " 10.0,", + " 24.0,", + " 35.0,", + " ,", + " ,", + " Temperature,", + " Temperature,", + " Dimensionless;", + "Curve:Biquadratic,", + " EIRCurveFuncTemp,", + " 1.0,", + " 0.0,", + " 0.0,", + " 1.0,", + " 0.0,", + " 0.0,", + " 5.0,", + " 10.0,", + " 24.0,", + " 35.0,", + " ,", + " ,", + " Temperature,", + " Temperature,", + " Dimensionless;", + "Curve:Quadratic,", + " EIRCurveFuncPLR,", + " 1.0,", + " 0.0,", + " 0.0,", + " 0.0,", + " 1.0;" + "Curve:Quadratic,", + " badEIRCurveFuncPLR,", + " -1.0,", + " 0.0,", + " 0.0,", + " 0.0,", + " 1.0;"}); + + ASSERT_TRUE(process_idf(idf_objects)); + state->init_state(*state); + + // set up the plant loops + // first the load side + state->dataPlnt->TotNumLoops = 2; + state->dataPlnt->PlantLoop.allocate(2); + + state->dataPlnt->PlantLoop(1).FluidName = "WATER"; + state->dataPlnt->PlantLoop(1).glycol = Fluid::GetWater(*state); + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).TotalBranches = 1; + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch.allocate(1); + 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 &PLHPPlantLoadSideLoop = state->dataPlnt->PlantLoop(1); + auto &PLHPPlantLoadSideComp = state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp(1); + PLHPPlantLoadSideComp.Type = DataPlant::PlantEquipmentType::HeatPumpEIRCooling; + // then the source side + + state->dataPlnt->PlantLoop(2).FluidName = "WATER"; + state->dataPlnt->PlantLoop(2).glycol = Fluid::GetWater(*state); + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).TotalBranches = 1; + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch.allocate(1); + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).TotalComponents = 1; + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp.allocate(1); + + auto &PLHPPlantLoadSourceComp = state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp(1); + PLHPPlantLoadSourceComp.Type = DataPlant::PlantEquipmentType::HeatPumpEIRCooling; + + // call the factory with a valid name to trigger reading inputs + EIRPlantLoopHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpEIRCooling, "HP COOLING SIDE"); + + // verify the size of the vector and the processed condition + EXPECT_EQ(2u, state->dataEIRPlantLoopHeatPump->heatPumps.size()); + + // for now we know the order is maintained, so get each heat pump object + EIRPlantLoopHeatPump *thisCoolingPLHP = &state->dataEIRPlantLoopHeatPump->heatPumps[0]; + + // do a little setup here + thisCoolingPLHP->loadSidePlantLoc.loopNum = 1; + thisCoolingPLHP->loadSidePlantLoc.loopSideNum = DataPlant::LoopSideLocation::Supply; + thisCoolingPLHP->loadSidePlantLoc.branchNum = 1; + thisCoolingPLHP->loadSidePlantLoc.compNum = 1; + PlantUtilities::SetPlantLocationLinks(*state, thisCoolingPLHP->loadSidePlantLoc); + thisCoolingPLHP->loadSideNodes.outlet = 1; + thisCoolingPLHP->sourceSidePlantLoc.loopNum = 2; + PlantUtilities::SetPlantLocationLinks(*state, thisCoolingPLHP->sourceSidePlantLoc); + + // the factory would've called GetOnlySingleNode for the in/out pairs on the PLHP, add another one for the loop + // outlet setpoint node + state->dataLoopNodes->Node.allocate(5); + PLHPPlantLoadSideLoop.TempSetPointNodeNum = 5; + + // set up the plant setpoint conditions and test for single setpoint operation + PLHPPlantLoadSideLoop.LoopDemandCalcScheme = DataPlant::LoopDemandCalcScheme::SingleSetPoint; + PLHPPlantLoadSideComp.CurOpSchemeType = DataPlant::OpScheme::CompSetPtBased; + state->dataLoopNodes->Node(thisCoolingPLHP->loadSideNodes.outlet).TempSetPoint = 3.141; + state->dataLoopNodes->Node(5).TempSetPoint = 2.718; + PLHPPlantLoadSideComp.CurOpSchemeType = DataPlant::OpScheme::CoolingRB; + + // test for dual setpoint operation + PLHPPlantLoadSideLoop.LoopDemandCalcScheme = DataPlant::LoopDemandCalcScheme::DualSetPointDeadBand; + PLHPPlantLoadSideComp.CurOpSchemeType = DataPlant::OpScheme::CompSetPtBased; + state->dataLoopNodes->Node(thisCoolingPLHP->loadSideNodes.outlet).TempSetPointHi = 6.282; + state->dataLoopNodes->Node(5).TempSetPointHi = 5.436; + PLHPPlantLoadSideComp.CurOpSchemeType = DataPlant::OpScheme::CoolingRB; + + state->dataHVACGlobal->TimeStepSys = 60; + state->dataHVACGlobal->TimeStepSysSec = state->dataHVACGlobal->TimeStepSys * Constant::rSecsInHour; + + Real64 curLoad = -10000; + + thisCoolingPLHP->loadSideMassFlowRate = 0.3; + thisCoolingPLHP->sourceSideMassFlowRate = 0.8; + thisCoolingPLHP->loadSideInletTemp = 20; + thisCoolingPLHP->sourceSideInletTemp = 20; + thisCoolingPLHP->doPhysics(*state, curLoad); + thisCoolingPLHP->report(*state); + + // Power and energy are now zero since the curve is reset with zero values + EXPECT_NEAR(thisCoolingPLHP->powerUsage, 0.000, 1e-3); + EXPECT_NEAR(thisCoolingPLHP->powerEnergy, 0.000, 1e-3); + + EXPECT_NEAR(thisCoolingPLHP->sourceSideHeatTransfer, 10000.000, 1e-3); + EXPECT_NEAR(thisCoolingPLHP->sourceSideEnergy, 2160000000.000, 1e-3); + + EXPECT_NEAR(thisCoolingPLHP->loadSideOutletTemp, 12.095, 1e-3); + + EXPECT_NEAR(thisCoolingPLHP->sourceSideOutletTemp, 22.989, 1e-3); + + EXPECT_EQ(thisCoolingPLHP->eirModFPLRErrorIndex, 1); + + EXPECT_EQ(state->dataErrTracking->TotalWarningErrors, 1); + EXPECT_EQ(state->dataErrTracking->TotalSevereErrors, 0); + EXPECT_EQ(state->dataErrTracking->LastSevereError, "HeatPump:PlantLoop:EIR:Cooling \"HP COOLING SIDE\":"); + + EXPECT_EQ(state->dataErrTracking->RecurringErrors(1).Count, 1); + EXPECT_EQ(state->dataErrTracking->RecurringErrors(1).Message, + " ** Warning ** HeatPump:PlantLoop:EIR:Cooling \"HP COOLING SIDE\": EIR Modifier curve (function of PLR) output is negative warning " + "continues..."); +} + +TEST_F(EnergyPlusFixture, GAHP_HeatingConstructionFullObjectsNoCompanion) +{ + std::string const idf_objects = delimited_string({"HeatPump:AirToWater:FuelFired:Heating,", + " Fuel Fired hp heating side, ! A1", + " node w1, ! A2", + " node w2, ! A3", + " node a3, ! A4", + " , ! A5 Comanion coil", + " NaturalGas, ! A6 fuel type", + " GAHP_Custom, ! A7 end use cat", + " 3000, ! N1 capacity", + " 1.5, ! N2 nominal COP", + " 0.005, ! N3 design flow rate", + " 60, ! N4 Design Supply Temp", + " 11.1, ! N5 Design Lift", + " 1.0, ! N6 sizing factor", + " NotModulated, ! A8 flow mode", + " DryBulb, ! A9 oa temp var type", + " EnteringCondenser, ! A10 oa temp var type", + " CapCurveFuncTemp, ! A11 CapFoT", + " EIRCurveFuncTemp, ! A12 EIRFoT", + " EIRCurveFuncPLR, ! A13 EIRFoPLR", + " 0.2, ! N7 minPLR", + " 1.0, ! N8 maxPLR", + " OnDemand, ! A14 defrost control type", + " , ! N9 defrost time frac", + " , ! A15 EIRdefrost curve", + " 3.0, ! N10 max oa DBT for defrost", + " , ! N11 resistive defrost heater capacity", + " uniCRFCurve5, ! A16 crf curve name", + " 500, ! N12 nominal aux elec power", + " EIRCurveFuncTemp, ! A17 EIRAuxFoT", + " uniAuxElecEIRFoPLRCurve6, ! A18 EIRAuxFoPLR", + " 20; ! N13 standby elec power", + + "Curve:Biquadratic,", + " CapCurveFuncTemp,", + " 1.0,", + " 0.0,", + " 0.0,", + " 0.0,", + " 0.0,", + " 0.0,", + " 5.0,", + " 10.0,", + " 24.0,", + " 35.0,", + " ,", + " ,", + " Temperature,", + " Temperature,", + " Dimensionless;", + "Curve:Biquadratic,", + " EIRCurveFuncTemp,", + " 1.0,", + " 0.0,", + " 0.0,", + " 1.0,", + " 0.0,", + " 0.0,", + " 5.0,", + " 10.0,", + " 24.0,", + " 35.0,", + " ,", + " ,", + " Temperature,", + " Temperature,", + " Dimensionless;", + "Curve:Quadratic,", + " EIRCurveFuncPLR,", + " 1.0,", + " 0.0,", + " 0.0,", + " 0.0,", + " 1.0;" + + "Curve:Linear,", + " uniDefrostCurve4,", + " 1,", + " 0,", + " 1,", + " 1;" + "Curve:Linear,", + " uniCRFCurve5,", + " 1,", + " 0,", + " 1,", + " 1;" + "Curve:Linear,", + " uniAuxElecEIRFoPLRCurve6,", + " 1,", + " 0,", + " 1,", + " 1;"}); + ASSERT_TRUE(process_idf(idf_objects)); + state->init_state(*state); + + // call the factory with a valid name to trigger reading inputs + EIRFuelFiredHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpFuelFiredHeating, "FUEL FIRED HP HEATING SIDE"); + + // verify the size of the vector and the processed condition + EXPECT_EQ(1u, state->dataEIRFuelFiredHeatPump->heatPumps.size()); + + // for now we know the order is maintained, so get each heat pump object + EIRFuelFiredHeatPump *thisHeatingPLHP = &state->dataEIRFuelFiredHeatPump->heatPumps[0]; + + // validate the heating side + EXPECT_EQ("FUEL FIRED HP HEATING SIDE", thisHeatingPLHP->name); + EXPECT_ENUM_EQ(DataPlant::PlantEquipmentType::HeatPumpFuelFiredHeating, thisHeatingPLHP->EIRHPType); + EXPECT_EQ(nullptr, thisHeatingPLHP->companionHeatPumpCoil); + EXPECT_EQ(1, thisHeatingPLHP->capFuncTempCurveIndex); + EXPECT_EQ(2, thisHeatingPLHP->powerRatioFuncTempCurveIndex); + EXPECT_EQ(3, thisHeatingPLHP->powerRatioFuncPLRCurveIndex); + EXPECT_EQ(0, thisHeatingPLHP->defrostEIRCurveIndex); + EXPECT_EQ(5, thisHeatingPLHP->cycRatioCurveIndex); + EXPECT_EQ(2, thisHeatingPLHP->auxElecEIRFoTempCurveIndex); + EXPECT_EQ(6, thisHeatingPLHP->auxElecEIRFoPLRCurveIndex); + + EXPECT_EQ(500.0, thisHeatingPLHP->nominalAuxElecPower); + EXPECT_EQ(20.0, thisHeatingPLHP->standbyElecPower); + + // calling the factory with an invalid name or type will call ShowFatalError, which will trigger a runtime exception + EXPECT_THROW(EIRFuelFiredHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpFuelFiredHeating, "fake"), std::runtime_error); + EXPECT_THROW(EIRFuelFiredHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpFuelFiredCooling, "FUEL FIRED HP HEATING SIDE"), + std::runtime_error); +} + +TEST_F(EnergyPlusFixture, GAHP_HeatingConstructionFullObjectsNoCompanion_with_Defrost) +{ + std::string const idf_objects = delimited_string({"HeatPump:AirToWater:FuelFired:Heating,", + " Fuel Fired hp heating side, ! A1", + " node w1, ! A2", + " node w2, ! A3", + " node a3, ! A4", + " , ! A5 Comanion coil", + " NaturalGas, ! A6 fuel type", + " GAHP_Custom, ! A7 end use cat", + " 3000, ! N1 capacity", + " 1.5, ! N2 nominal COP", + " 0.005, ! N3 design flow rate", + " 60, ! N4 Design Supply Temp", + " 11.1, ! N5 Design Lift", + " 1.0, ! N6 sizing factor", + " NotModulated, ! A8 flow mode", + " DryBulb, ! A9 oa temp var type", + " EnteringCondenser, ! A10 oa temp var type", + " CapCurveFuncTemp, ! A11 CapFoT", + " EIRCurveFuncTemp, ! A12 EIRFoT", + " EIRCurveFuncPLR, ! A13 EIRFoPLR", + " 0.2, ! N7 minPLR", + " 1.0, ! N8 maxPLR", + " OnDemand, ! A14 defrost control type", + " , ! N9 defrost time frac", + " uniDefrostCurve4, ! A15 EIRdefrost curve", + " 3.0, ! N10 max oa DBT for defrost", + " , ! N11 resistive defrost heater capacity", + " uniCRFCurve5, ! A16 crf curve name", + " 500, ! N12 nominal aux elec power", + " EIRCurveFuncTemp, ! A17 EIRAuxFoT", + " uniAuxElecEIRFoPLRCurve6, ! A18 EIRAuxFoPLR", + " 20; ! N13 standby elec power", + + "Curve:Biquadratic,", + " CapCurveFuncTemp,", + " 1.0,", + " 0.0,", + " 0.0,", + " 0.0,", + " 0.0,", + " 0.0,", + " 5.0,", + " 10.0,", + " 24.0,", + " 35.0,", + " ,", + " ,", + " Temperature,", + " Temperature,", + " Dimensionless;", + "Curve:Biquadratic,", + " EIRCurveFuncTemp,", + " 1.0,", + " 0.0,", + " 0.0,", + " 1.0,", + " 0.0,", + " 0.0,", + " 5.0,", + " 10.0,", + " 24.0,", + " 35.0,", + " ,", + " ,", + " Temperature,", + " Temperature,", + " Dimensionless;", + "Curve:Quadratic,", + " EIRCurveFuncPLR,", + " 1.0,", + " 0.0,", + " 0.0,", + " 0.0,", + " 1.0;" + + "Curve:Linear,", + " uniDefrostCurve4,", + " 1,", + " 0,", + " 1,", + " 1;" + "Curve:Linear,", + " uniCRFCurve5,", + " 1,", + " 0,", + " 1,", + " 1;" + "Curve:Linear,", + " uniAuxElecEIRFoPLRCurve6,", + " 1,", + " 0,", + " 1,", + " 1;"}); + ASSERT_TRUE(process_idf(idf_objects)); + state->init_state(*state); + + // call the factory with a valid name to trigger reading inputs + EIRFuelFiredHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpFuelFiredHeating, "FUEL FIRED HP HEATING SIDE"); + + // verify the size of the vector and the processed condition + EXPECT_EQ(1u, state->dataEIRFuelFiredHeatPump->heatPumps.size()); + + // for now we know the order is maintained, so get each heat pump object + EIRFuelFiredHeatPump *thisHeatingPLHP = &state->dataEIRFuelFiredHeatPump->heatPumps[0]; + + // validate the heating side + EXPECT_EQ("FUEL FIRED HP HEATING SIDE", thisHeatingPLHP->name); + EXPECT_ENUM_EQ(DataPlant::PlantEquipmentType::HeatPumpFuelFiredHeating, thisHeatingPLHP->EIRHPType); + EXPECT_EQ(nullptr, thisHeatingPLHP->companionHeatPumpCoil); + EXPECT_EQ(1, thisHeatingPLHP->capFuncTempCurveIndex); + EXPECT_EQ(2, thisHeatingPLHP->powerRatioFuncTempCurveIndex); + EXPECT_EQ(3, thisHeatingPLHP->powerRatioFuncPLRCurveIndex); + EXPECT_EQ(4, thisHeatingPLHP->defrostEIRCurveIndex); + EXPECT_EQ(5, thisHeatingPLHP->cycRatioCurveIndex); + EXPECT_EQ(2, thisHeatingPLHP->auxElecEIRFoTempCurveIndex); + EXPECT_EQ(6, thisHeatingPLHP->auxElecEIRFoPLRCurveIndex); + + EXPECT_EQ(500.0, thisHeatingPLHP->nominalAuxElecPower); + EXPECT_EQ(20.0, thisHeatingPLHP->standbyElecPower); + + // calling the factory with an invalid name or type will call ShowFatalError, which will trigger a runtime exception + EXPECT_THROW(EIRFuelFiredHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpFuelFiredHeating, "fake"), std::runtime_error); + EXPECT_THROW(EIRFuelFiredHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpFuelFiredCooling, "FUEL FIRED HP HEATING SIDE"), + std::runtime_error); +} + +TEST_F(EnergyPlusFixture, GAHP_Initialization_Test) +{ + std::string const idf_objects = delimited_string({"HeatPump:AirToWater:FuelFired:Heating,", + " Fuel Fired hp heating side, ! A1", + " node w1, ! A2", + " node w2, ! A3", + " node a3, ! A4", + " , ! A5 Comanion coil", + " NaturalGas, ! A6 fuel type", + " GAHP_Custom, ! A7 end use cat", + " 3000, ! N1 capacity", + " 1.5, ! N2 nominal COP", + " 0.005, ! N3 design flow rate", + " 60, ! N4 Design Supply Temp", + " 11.1, ! N5 Design Lift", + " 1.0, ! N6 sizing factor", + " NotModulated, ! A8 flow mode", + " DryBulb, ! A9 oa temp var type", + " EnteringCondenser, ! A10 oa temp var type", + " CapCurveFuncTemp, ! A11 CapFoT", + " EIRCurveFuncTemp, ! A12 EIRFoT", + " EIRCurveFuncPLR, ! A13 EIRFoPLR", + " 0.2, ! N7 minPLR", + " 1.0, ! N8 maxPLR", + " OnDemand, ! A14 defrost control type", + " , ! N9 defrost time frac", + " , ! A15 EIRdefrost curve", + " , ! N10 resistive defrost heater capacity", + " 3.0, ! N11 max oa DBT for defrost", + " uniCRFCurve5, ! A16 crf curve name", + " 500, ! N12 nominal aux elec power", + " EIRCurveFuncTemp, ! A17 EIRAuxFoT", + " uniAuxElecEIRFoPLRCurve6, ! A18 EIRAuxFoPLR", + " 20; ! N13 standby elec power", + + "Curve:Biquadratic,", + " CapCurveFuncTemp,", + " 1.0,", + " 0.0,", + " 0.0,", + " 0.0,", + " 0.0,", + " 0.0,", + " 5.0,", + " 10.0,", + " 24.0,", + " 35.0,", + " ,", + " ,", + " Temperature,", + " Temperature,", + " Dimensionless;", + "Curve:Biquadratic,", + " EIRCurveFuncTemp,", + " 1.0,", + " 0.0,", + " 0.0,", + " 1.0,", + " 0.0,", + " 0.0,", + " 5.0,", + " 10.0,", + " 24.0,", + " 35.0,", + " ,", + " ,", + " Temperature,", + " Temperature,", + " Dimensionless;", + "Curve:Quadratic,", + " EIRCurveFuncPLR,", + " 1.0,", + " 0.0,", + " 0.0,", + " 0.0,", + " 1.0;" + + "Curve:Linear,", + " uniDefrostCurve4,", + " 1,", + " 0,", + " 1,", + " 1;" + "Curve:Linear,", + " uniCRFCurve5,", + " 1,", + " 0,", + " 1,", + " 1;" + "Curve:Linear,", + " uniAuxElecEIRFoPLRCurve6,", + " 1,", + " 0,", + " 1,", + " 1;"}); + ASSERT_TRUE(process_idf(idf_objects)); + state->init_state(*state); + + bool firstHVACIteration = true; + // set up the plant loops + // first the load side + state->dataPlnt->TotNumLoops = 1; + state->dataPlnt->PlantLoop.allocate(1); + + state->dataPlnt->PlantLoop(1).FluidName = "WATER"; + state->dataPlnt->PlantLoop(1).glycol = Fluid::GetWater(*state); + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).TotalBranches = 1; + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch.allocate(1); + 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 &PLHPPlantLoadSideComp = state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp(1); + PLHPPlantLoadSideComp.Type = DataPlant::PlantEquipmentType::HeatPumpFuelFiredHeating; + + // the init call expects a "from" calling point + PlantLocation myLocation = PlantLocation(1, DataPlant::LoopSideLocation::Supply, 1, 1); + + // call the factory with a valid name to trigger reading inputs + EIRFuelFiredHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpFuelFiredHeating, "FUEL FIRED HP HEATING SIDE"); + + // verify the size of the vector and the processed condition + EXPECT_EQ(1u, state->dataEIRFuelFiredHeatPump->heatPumps.size()); + + // for now we know the order is maintained, so get each heat pump object + EIRFuelFiredHeatPump *thisHeatingPLHP = &state->dataEIRFuelFiredHeatPump->heatPumps[0]; + + // do a bit of extra wiring up to the plant + PLHPPlantLoadSideComp.Name = thisHeatingPLHP->name; + PLHPPlantLoadSideComp.NodeNumIn = thisHeatingPLHP->loadSideNodes.inlet; + + // call for all initialization + state->dataGlobal->BeginEnvrnFlag = true; + state->dataPlnt->PlantFirstSizesOkayToFinalize = true; + thisHeatingPLHP->onInitLoopEquip(*state, myLocation); + + // call with run flag off, loose limits on node min/max + thisHeatingPLHP->running = false; + Real64 constexpr currentLoad = 0.0; + thisHeatingPLHP->setOperatingFlowRatesASHP(*state, firstHVACIteration, currentLoad); + EXPECT_NEAR(0.0, thisHeatingPLHP->loadSideMassFlowRate, 0.001); + EXPECT_NEAR(0.0, thisHeatingPLHP->sourceSideMassFlowRate, 0.001); + + // call with run flag off, nonzero minimums + state->dataLoopNodes->Node(thisHeatingPLHP->loadSideNodes.inlet).MassFlowRateMinAvail = 0.1; + thisHeatingPLHP->running = false; + thisHeatingPLHP->setOperatingFlowRatesASHP(*state, firstHVACIteration, currentLoad); + EXPECT_NEAR(0.1, thisHeatingPLHP->loadSideMassFlowRate, 0.001); + EXPECT_NEAR(0, thisHeatingPLHP->sourceSideMassFlowRate, 0.001); + + // call with run flag off, load side flow locked + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).FlowLock = DataPlant::FlowLock::Locked; + state->dataLoopNodes->Node(thisHeatingPLHP->loadSideNodes.inlet).MassFlowRate = 0.24; + thisHeatingPLHP->running = false; + thisHeatingPLHP->setOperatingFlowRatesASHP(*state, firstHVACIteration, currentLoad); + EXPECT_NEAR(0.24, thisHeatingPLHP->loadSideMassFlowRate, 0.001); + EXPECT_NEAR(0.0, thisHeatingPLHP->sourceSideMassFlowRate, 0.001); + + // call with run flag ON, flow locked at zero on load side + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).FlowLock = DataPlant::FlowLock::Locked; + state->dataLoopNodes->Node(thisHeatingPLHP->loadSideNodes.inlet).MassFlowRate = 0.0; + thisHeatingPLHP->running = true; + thisHeatingPLHP->setOperatingFlowRatesASHP(*state, firstHVACIteration, currentLoad); + EXPECT_NEAR(0.0, thisHeatingPLHP->loadSideMassFlowRate, 0.001); + EXPECT_NEAR(0, thisHeatingPLHP->sourceSideMassFlowRate, 0.001); + + // call with run flag ON, flow locked at zero on source side + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).FlowLock = DataPlant::FlowLock::Locked; + state->dataLoopNodes->Node(thisHeatingPLHP->loadSideNodes.inlet).MassFlowRate = 0.2; + thisHeatingPLHP->running = true; + thisHeatingPLHP->setOperatingFlowRatesASHP(*state, firstHVACIteration, currentLoad); + EXPECT_NEAR(0.2, thisHeatingPLHP->loadSideMassFlowRate, 0.001); + EXPECT_NEAR(1.29, thisHeatingPLHP->sourceSideMassFlowRate, 0.1); + + // call with run flag ON, flow locked at zero on both sides + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).FlowLock = DataPlant::FlowLock::Locked; + state->dataLoopNodes->Node(thisHeatingPLHP->loadSideNodes.inlet).MassFlowRate = 0.0; + thisHeatingPLHP->running = true; + thisHeatingPLHP->setOperatingFlowRatesASHP(*state, firstHVACIteration, currentLoad); + EXPECT_NEAR(0.0, thisHeatingPLHP->loadSideMassFlowRate, 0.001); + EXPECT_NEAR(0.0, thisHeatingPLHP->sourceSideMassFlowRate, 0.001); + + // call with run flag ON, flow locked at nonzero both + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).FlowLock = DataPlant::FlowLock::Locked; + state->dataLoopNodes->Node(thisHeatingPLHP->loadSideNodes.inlet).MassFlowRate = 0.14; + thisHeatingPLHP->running = true; + thisHeatingPLHP->setOperatingFlowRatesASHP(*state, firstHVACIteration, currentLoad); + EXPECT_NEAR(0.14, thisHeatingPLHP->loadSideMassFlowRate, 0.001); + EXPECT_NEAR(1.29, thisHeatingPLHP->sourceSideMassFlowRate, 0.1); +} + +TEST_F(EnergyPlusFixture, GAHP_HeatingSimulate_AirSource) +{ + std::string const idf_objects = delimited_string({"HeatPump:AirToWater:FuelFired:Heating,", + " Fuel Fired hp heating side, ! A1", + " node w1, ! A2", + " node w2, ! A3", + " node a3, ! A4", + " , ! A5 Comanion coil", + " NaturalGas, ! A6 fuel type", + " GAHP_Custom, ! A7 end use cat", + " 3000, ! N1 capacity", + " 1.5, ! N2 nominal COP", + " 0.005, ! N3 design flow rate", + " 60, ! N4 Design Supply Temp", + " 11.1, ! N5 Design Lift", + " 1.0, ! N6 sizing factor", + " NotModulated, ! A8 flow mode", + " DryBulb, ! A9 oa temp var type", + " EnteringCondenser, ! A10 oa temp var type", + " CapCurveFuncTemp, ! A11 CapFoT", + " EIRCurveFuncTemp, ! A12 EIRFoT", + " EIRCurveFuncPLR, ! A13 EIRFoPLR", + " 0.2, ! N7 minPLR", + " 1.0, ! N8 maxPLR", + " OnDemand, ! A14 defrost control type", + " , ! N9 defrost time frac", + " , ! A15 EIRdefrost curve", + " , ! N10 resistive defrost heater capacity", + " 3.0, ! N11 max oa DBT for defrost", + " uniCRFCurve5, ! A16 crf curve name", + " 500, ! N12 nominal aux elec power", + " EIRCurveFuncTemp, ! A17 EIRAuxFoT", + " uniAuxElecEIRFoPLRCurve6, ! A18 EIRAuxFoPLR", + " 20; ! N13 standby elec power", + + "Curve:Biquadratic,", + " CapCurveFuncTemp,", + " 1.0,", + " 0.0,", + " 0.0,", + " 0.0,", + " 0.0,", + " 0.0,", + " 5.0,", + " 10.0,", + " 24.0,", + " 35.0,", + " ,", + " ,", + " Temperature,", + " Temperature,", + " Dimensionless;", + "Curve:Biquadratic,", + " EIRCurveFuncTemp,", + " 1.0,", + " 0.0,", + " 0.0,", + " 1.0,", + " 0.0,", + " 0.0,", + " 5.0,", + " 10.0,", + " 24.0,", + " 35.0,", + " ,", + " ,", + " Temperature,", + " Temperature,", + " Dimensionless;", + "Curve:Quadratic,", + " EIRCurveFuncPLR,", + " 1.0,", + " 0.0,", + " 0.0,", + " 0.0,", + " 1.0;" + + "Curve:Linear,", + " uniDefrostCurve4,", + " 1,", + " 0,", + " 1,", + " 1;" + "Curve:Linear,", + " uniCRFCurve5,", + " 1,", + " 0,", + " 1,", + " 1;" + "Curve:Linear,", + " uniAuxElecEIRFoPLRCurve6,", + " 1,", + " 0,", + " 1,", + " 1;"}); + + ASSERT_TRUE(process_idf(idf_objects)); + state->init_state(*state); + + // set up the plant loops + // first the load side + state->dataPlnt->TotNumLoops = 1; + state->dataPlnt->PlantLoop.allocate(1); + + state->dataPlnt->PlantLoop(1).FluidName = "WATER"; + state->dataPlnt->PlantLoop(1).glycol = Fluid::GetWater(*state); + state->dataPlnt->PlantLoop(1).LoopDemandCalcScheme = DataPlant::LoopDemandCalcScheme::SingleSetPoint; + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).TotalBranches = 1; + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch.allocate(1); + 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 &PLHPPlantLoadSideComp = state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp(1); + PLHPPlantLoadSideComp.Type = DataPlant::PlantEquipmentType::HeatPumpFuelFiredHeating; + PLHPPlantLoadSideComp.CurOpSchemeType = DataPlant::OpScheme::CompSetPtBased; + + // the init call expects a "from" calling point + PlantLocation myLoadLocation = PlantLocation(1, DataPlant::LoopSideLocation::Supply, 1, 1); + + // call the factory with a valid name to trigger reading inputs + EIRFuelFiredHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpFuelFiredHeating, "FUEL FIRED HP HEATING SIDE"); + + // verify the size of the vector and the processed condition + EXPECT_EQ(1u, state->dataEIRFuelFiredHeatPump->heatPumps.size()); + + // for now we know the order is maintained, so get each heat pump object + EIRFuelFiredHeatPump *thisHeatingPLHP = &state->dataEIRFuelFiredHeatPump->heatPumps[0]; + + // do a bit of extra wiring up to the plant + PLHPPlantLoadSideComp.Name = thisHeatingPLHP->name; + PLHPPlantLoadSideComp.NodeNumIn = thisHeatingPLHP->loadSideNodes.inlet; + + // call for all initialization + state->dataGlobal->BeginEnvrnFlag = true; + state->dataPlnt->PlantFirstSizesOkayToFinalize = true; + thisHeatingPLHP->onInitLoopEquip(*state, myLoadLocation); + + // do a runflag = false to test out execution order + { + bool firstHVAC = true; + Real64 curLoad = -900; + bool runFlag = false; + Real64 constexpr specifiedLoadSetpoint = 45; + Real64 constexpr loadInletTemp = 46; + state->dataLoopNodes->Node(thisHeatingPLHP->loadSideNodes.outlet).TempSetPoint = specifiedLoadSetpoint; + state->dataLoopNodes->Node(thisHeatingPLHP->loadSideNodes.inlet).Temp = loadInletTemp; + state->dataLoopNodes->Node(thisHeatingPLHP->sourceSideNodes.inlet).Temp = 30; + thisHeatingPLHP->simulate(*state, myLoadLocation, firstHVAC, curLoad, runFlag); + } + + // call it from the load side, but this time there is a negative (cooling) load - shouldn't try to run + { + bool firstHVAC = true; + Real64 curLoad = -900; + bool runFlag = true; // plant actually shouldn't do this but the component can be smart enough to handle it + Real64 constexpr specifiedLoadSetpoint = 45; + Real64 constexpr loadInletTemp = 46; + state->dataLoopNodes->Node(thisHeatingPLHP->loadSideNodes.outlet).TempSetPoint = specifiedLoadSetpoint; + state->dataLoopNodes->Node(thisHeatingPLHP->loadSideNodes.inlet).Temp = loadInletTemp; + state->dataLoopNodes->Node(thisHeatingPLHP->sourceSideNodes.inlet).Temp = 30; + thisHeatingPLHP->simulate(*state, myLoadLocation, firstHVAC, curLoad, runFlag); + // expect it to meet setpoint and have some pre-evaluated conditions + EXPECT_NEAR(loadInletTemp, thisHeatingPLHP->loadSideOutletTemp, 0.001); + EXPECT_NEAR(0.0, thisHeatingPLHP->loadSideHeatTransfer, 0.001); + } + + // call it from the load side, but this time there is load (still firsthvac, unit can meet load) + { + bool firstHVAC = true; + Real64 curLoad = 800; + bool runFlag = true; + Real64 constexpr expectedLoadMassFlowRate = 0.09999; + Real64 constexpr expectedCp = 4180; + Real64 constexpr specifiedLoadSetpoint = 45; + Real64 const calculatedLoadInletTemp = specifiedLoadSetpoint - curLoad / (expectedLoadMassFlowRate * expectedCp); + state->dataLoopNodes->Node(thisHeatingPLHP->loadSideNodes.outlet).TempSetPoint = specifiedLoadSetpoint; + state->dataLoopNodes->Node(thisHeatingPLHP->loadSideNodes.inlet).Temp = calculatedLoadInletTemp; + state->dataLoopNodes->Node(thisHeatingPLHP->sourceSideNodes.inlet).Temp = 30; + thisHeatingPLHP->simulate(*state, myLoadLocation, 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); + } + + // now we can call it again from the load side, but this time there is load (still firsthvac, unit cannot meet load) + { + bool firstHVAC = true; + Real64 curLoad = 1200; + // Real64 availableCapacity = 950.0; + bool runFlag = true; + Real64 constexpr expectedLoadMassFlowRate = 0.09999; + Real64 constexpr expectedCp = 4180; + Real64 constexpr specifiedLoadSetpoint = 45; + Real64 const calculatedLoadInletTemp = specifiedLoadSetpoint - curLoad / (expectedLoadMassFlowRate * expectedCp); + state->dataLoopNodes->Node(thisHeatingPLHP->loadSideNodes.outlet).TempSetPoint = specifiedLoadSetpoint; + state->dataLoopNodes->Node(thisHeatingPLHP->loadSideNodes.inlet).Temp = calculatedLoadInletTemp; + state->dataLoopNodes->Node(thisHeatingPLHP->sourceSideNodes.inlet).Temp = 30; + thisHeatingPLHP->simulate(*state, myLoadLocation, firstHVAC, curLoad, runFlag); + // expect it to miss setpoint and be at max capacity + // EXPECT_NEAR(44.402, thisHeatingPLHP->loadSideOutletTemp, 0.001); + // EXPECT_NEAR(availableCapacity, thisHeatingPLHP->loadSideHeatTransfer, 0.001); + } + + // now we can call it again from the load side, but this time there is no load (still firsthvac) + { + bool firstHVAC = true; + Real64 curLoad = 0.0; + bool runFlag = true; + Real64 constexpr expectedLoadMassFlowRate = 0.09999; + Real64 constexpr expectedCp = 4180; + Real64 constexpr specifiedLoadSetpoint = 45; + Real64 const calculatedLoadInletTemp = specifiedLoadSetpoint - curLoad / (expectedLoadMassFlowRate * expectedCp); + state->dataLoopNodes->Node(thisHeatingPLHP->loadSideNodes.outlet).TempSetPoint = specifiedLoadSetpoint; + state->dataLoopNodes->Node(thisHeatingPLHP->loadSideNodes.inlet).Temp = calculatedLoadInletTemp; + state->dataLoopNodes->Node(thisHeatingPLHP->sourceSideNodes.inlet).Temp = 30; + thisHeatingPLHP->simulate(*state, myLoadLocation, firstHVAC, curLoad, runFlag); + // expect it to miss setpoint and be at max capacity + EXPECT_NEAR(45.0, thisHeatingPLHP->loadSideOutletTemp, 0.001); + EXPECT_NEAR(30.0, thisHeatingPLHP->sourceSideOutletTemp, 0.001); + } + + // Test cycling calcs + { + bool firstHVAC = true; + Real64 curLoad = 500.0; + bool runFlag = true; + Real64 constexpr expectedLoadMassFlowRate = 0.09999; + Real64 constexpr expectedCp = 4180; + Real64 constexpr specifiedLoadSetpoint = 45; + Real64 const calculatedLoadInletTemp = specifiedLoadSetpoint - curLoad / (expectedLoadMassFlowRate * expectedCp); + state->dataLoopNodes->Node(thisHeatingPLHP->loadSideNodes.outlet).TempSetPoint = specifiedLoadSetpoint; + state->dataLoopNodes->Node(thisHeatingPLHP->loadSideNodes.inlet).Temp = calculatedLoadInletTemp; + state->dataLoopNodes->Node(thisHeatingPLHP->sourceSideNodes.inlet).Temp = 30; + // Use user specified curve + thisHeatingPLHP->simulate(*state, myLoadLocation, firstHVAC, curLoad, runFlag); + EXPECT_NEAR(1.0, thisHeatingPLHP->cyclingRatioFraction, 0.001); + // Use default assumptions + thisHeatingPLHP->cycRatioCurveIndex = 0; + thisHeatingPLHP->simulate(*state, myLoadLocation, firstHVAC, curLoad, runFlag); + EXPECT_NEAR(0.861, thisHeatingPLHP->cyclingRatioFraction, 0.001); + } + + // call it from the load side, very low load + { + bool firstHVAC = true; + Real64 curLoad = 100; + bool runFlag = true; + Real64 constexpr specifiedLoadSetpoint = 45; + state->dataLoopNodes->Node(thisHeatingPLHP->loadSideNodes.outlet).TempSetPoint = specifiedLoadSetpoint; + state->dataLoopNodes->Node(thisHeatingPLHP->loadSideNodes.inlet).Temp = 40; + state->dataLoopNodes->Node(thisHeatingPLHP->sourceSideNodes.inlet).Temp = 30; + thisHeatingPLHP->simulate(*state, myLoadLocation, firstHVAC, curLoad, runFlag); + EXPECT_TRUE(thisHeatingPLHP->fuelRate > 0); + EXPECT_NEAR(5.0, thisHeatingPLHP->loadSideMassFlowRate, 0.001); + } + + { + bool firstHVAC = false; + Real64 curLoad = 2000; + bool runFlag = true; + Real64 constexpr expectedLoadMassFlowRate = 0.0478; + Real64 constexpr specifiedLoadSetpoint = 45; + state->dataLoopNodes->Node(thisHeatingPLHP->loadSideNodes.outlet).TempSetPoint = specifiedLoadSetpoint; + state->dataLoopNodes->Node(thisHeatingPLHP->loadSideNodes.inlet).Temp = 35; + state->dataLoopNodes->Node(thisHeatingPLHP->sourceSideNodes.inlet).Temp = 30; + thisHeatingPLHP->flowMode = DataPlant::FlowMode::LeavingSetpointModulated; + thisHeatingPLHP->simulate(*state, myLoadLocation, firstHVAC, curLoad, runFlag); + EXPECT_NEAR(expectedLoadMassFlowRate, thisHeatingPLHP->loadSideMassFlowRate, 0.001); + } +} + +TEST_F(EnergyPlusFixture, GAHP_HeatingSimulate_AirSource_with_Defrost) +{ + std::string const idf_objects = delimited_string({"HeatPump:AirToWater:FuelFired:Heating,", + " Fuel Fired hp heating side, ! A1", + " node w1, ! A2", + " node w2, ! A3", + " node a3, ! A4", + " , ! A5 Comanion coil", + " NaturalGas, ! A6 fuel type", + " GAHP_Custom, ! A7 end use cat", + " 3000, ! N1 capacity", + " 1.5, ! N2 nominal COP", + " 0.005, ! N3 design flow rate", + " 60, ! N4 Design Supply Temp", + " 11.1, ! N5 Design Lift", + " 1.0, ! N6 sizing factor", + " NotModulated, ! A8 flow mode", + " DryBulb, ! A9 oa temp var type", + " EnteringCondenser, ! A10 oa temp var type", + " CapCurveFuncTemp, ! A11 CapFoT", + " EIRCurveFuncTemp, ! A12 EIRFoT", + " EIRCurveFuncPLR, ! A13 EIRFoPLR", + " 0.2, ! N7 minPLR", + " 1.0, ! N8 maxPLR", + " OnDemand, ! A14 defrost control type", + " , ! N9 defrost time frac", + " uniDefrostCurve4, ! A15 EIRdefrost curve", + " , ! N10 resistive defrost heater capacity", + " 3.0, ! N11 max oa DBT for defrost", + " uniCRFCurve5, ! A16 crf curve name", + " 500, ! N12 nominal aux elec power", + " EIRCurveFuncTemp, ! A17 EIRAuxFoT", + " uniAuxElecEIRFoPLRCurve6, ! A18 EIRAuxFoPLR", + " 20; ! N13 standby elec power", + + "Curve:Biquadratic,", + " CapCurveFuncTemp,", + " 1.0,", + " 0.0,", + " 0.0,", + " 0.0,", + " 0.0,", + " 0.0,", + " 5.0,", + " 10.0,", + " 24.0,", + " 35.0,", + " ,", + " ,", + " Temperature,", + " Temperature,", + " Dimensionless;", + "Curve:Biquadratic,", + " EIRCurveFuncTemp,", + " 1.0,", + " 0.0,", + " 0.0,", + " 1.0,", + " 0.0,", + " 0.0,", + " 5.0,", + " 10.0,", + " 24.0,", + " 35.0,", + " ,", + " ,", + " Temperature,", + " Temperature,", + " Dimensionless;", + "Curve:Quadratic,", + " EIRCurveFuncPLR,", + " 1.0,", + " 0.0,", + " 0.0,", + " 0.0,", + " 1.0;" + + "Curve:Linear,", + " uniDefrostCurve4,", + " 1,", + " 0,", + " 1,", + " 1;" + "Curve:Linear,", + " uniCRFCurve5,", + " 1,", + " 0,", + " 1,", + " 1;" + "Curve:Linear,", + " uniAuxElecEIRFoPLRCurve6,", + " 1,", + " 0,", + " 1,", + " 1;"}); + + ASSERT_TRUE(process_idf(idf_objects)); + state->init_state(*state); + + state->dataHVACGlobal->TimeStepSys = 0.25; + state->dataHVACGlobal->TimeStepSysSec = state->dataHVACGlobal->TimeStepSys * Constant::rSecsInHour; + + state->init_state(*state); + + // set up the plant loops + // first the load side + state->dataPlnt->TotNumLoops = 1; + state->dataPlnt->PlantLoop.allocate(1); + + state->dataPlnt->PlantLoop(1).FluidName = "WATER"; + state->dataPlnt->PlantLoop(1).glycol = Fluid::GetWater(*state); + state->dataPlnt->PlantLoop(1).LoopDemandCalcScheme = DataPlant::LoopDemandCalcScheme::SingleSetPoint; + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).TotalBranches = 1; + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch.allocate(1); + 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 &PLHPPlantLoadSideComp = state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp(1); + PLHPPlantLoadSideComp.Type = DataPlant::PlantEquipmentType::HeatPumpFuelFiredHeating; + PLHPPlantLoadSideComp.CurOpSchemeType = DataPlant::OpScheme::CompSetPtBased; + + // the init call expects a "from" calling point + PlantLocation myLoadLocation = PlantLocation(1, DataPlant::LoopSideLocation::Supply, 1, 1); + + // call the factory with a valid name to trigger reading inputs + EIRFuelFiredHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpFuelFiredHeating, "FUEL FIRED HP HEATING SIDE"); + + // verify the size of the vector and the processed condition + EXPECT_EQ(1u, state->dataEIRFuelFiredHeatPump->heatPumps.size()); + + // for now we know the order is maintained, so get each heat pump object + EIRFuelFiredHeatPump *thisHeatingPLHP = &state->dataEIRFuelFiredHeatPump->heatPumps[0]; + auto thisEIRPlantLoopHP = &(*(EIRPlantLoopHeatPump *)thisHeatingPLHP); + + // do a bit of extra wiring up to the plant + PLHPPlantLoadSideComp.Name = thisHeatingPLHP->name; + PLHPPlantLoadSideComp.NodeNumIn = thisHeatingPLHP->loadSideNodes.inlet; + + // call for all initialization + state->dataGlobal->BeginEnvrnFlag = true; + state->dataPlnt->PlantFirstSizesOkayToFinalize = true; + thisHeatingPLHP->onInitLoopEquip(*state, myLoadLocation); + + // do a runflag = false to test out execution order + { + bool firstHVAC = true; + Real64 curLoad = -900; + bool runFlag = false; + Real64 constexpr specifiedLoadSetpoint = 45; + Real64 constexpr loadInletTemp = 46; + state->dataLoopNodes->Node(thisHeatingPLHP->loadSideNodes.outlet).TempSetPoint = specifiedLoadSetpoint; + state->dataLoopNodes->Node(thisHeatingPLHP->loadSideNodes.inlet).Temp = loadInletTemp; + state->dataLoopNodes->Node(thisHeatingPLHP->sourceSideNodes.inlet).Temp = 30; + thisHeatingPLHP->simulate(*state, myLoadLocation, firstHVAC, curLoad, runFlag); + } + + // call it from the load side, but this time there is a negative (cooling) load - shouldn't try to run + { + bool firstHVAC = true; + Real64 curLoad = -900; + bool runFlag = true; // plant actually shouldn't do this but the component can be smart enough to handle it + Real64 constexpr specifiedLoadSetpoint = 45; + Real64 constexpr loadInletTemp = 46; + state->dataLoopNodes->Node(thisHeatingPLHP->loadSideNodes.outlet).TempSetPoint = specifiedLoadSetpoint; + state->dataLoopNodes->Node(thisHeatingPLHP->loadSideNodes.inlet).Temp = loadInletTemp; + state->dataLoopNodes->Node(thisHeatingPLHP->sourceSideNodes.inlet).Temp = 30; + thisHeatingPLHP->simulate(*state, myLoadLocation, firstHVAC, curLoad, runFlag); + // expect it to meet setpoint and have some pre-evaluated conditions + EXPECT_NEAR(loadInletTemp, thisHeatingPLHP->loadSideOutletTemp, 0.001); + EXPECT_NEAR(0.0, thisHeatingPLHP->loadSideHeatTransfer, 0.001); + } + + // call it from the load side, but this time there is load (still firsthvac, unit can meet load) + { + bool firstHVAC = true; + Real64 curLoad = 800; + bool runFlag = true; + Real64 constexpr expectedLoadMassFlowRate = 0.09999; + Real64 constexpr expectedCp = 4180; + Real64 constexpr specifiedLoadSetpoint = 45; + Real64 const calculatedLoadInletTemp = specifiedLoadSetpoint - curLoad / (expectedLoadMassFlowRate * expectedCp); + state->dataLoopNodes->Node(thisHeatingPLHP->loadSideNodes.outlet).TempSetPoint = specifiedLoadSetpoint; + state->dataLoopNodes->Node(thisHeatingPLHP->loadSideNodes.inlet).Temp = calculatedLoadInletTemp; + state->dataLoopNodes->Node(thisHeatingPLHP->sourceSideNodes.inlet).Temp = 30; + thisHeatingPLHP->simulate(*state, myLoadLocation, firstHVAC, curLoad, runFlag); + EXPECT_NEAR(16533.333, thisHeatingPLHP->fuelRate, 0.001); + EXPECT_NEAR(14880000.0, thisHeatingPLHP->fuelEnergy, 0.001); + // 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); + EXPECT_NEAR(15520.0, thisEIRPlantLoopHP->powerUsage, 0.001); + } + + // now we can call it again from the load side, but this time there is load (still firsthvac, unit cannot meet load) + { + bool firstHVAC = true; + Real64 curLoad = 1200; + // Real64 availableCapacity = 950.0; + bool runFlag = true; + Real64 constexpr expectedLoadMassFlowRate = 0.09999; + Real64 constexpr expectedCp = 4180; + Real64 constexpr specifiedLoadSetpoint = 45; + Real64 const calculatedLoadInletTemp = specifiedLoadSetpoint - curLoad / (expectedLoadMassFlowRate * expectedCp); + state->dataLoopNodes->Node(thisHeatingPLHP->loadSideNodes.outlet).TempSetPoint = specifiedLoadSetpoint; + state->dataLoopNodes->Node(thisHeatingPLHP->loadSideNodes.inlet).Temp = calculatedLoadInletTemp; + state->dataLoopNodes->Node(thisHeatingPLHP->sourceSideNodes.inlet).Temp = 30; + thisHeatingPLHP->simulate(*state, myLoadLocation, firstHVAC, curLoad, runFlag); + EXPECT_NEAR(24800.0, thisHeatingPLHP->fuelRate, 0.001); + EXPECT_NEAR(22320000.0, thisHeatingPLHP->fuelEnergy, 0.001); + EXPECT_NEAR(15520.0, thisEIRPlantLoopHP->powerUsage, 0.001); + // expect it to miss setpoint and be at max capacity + // EXPECT_NEAR(44.402, thisHeatingPLHP->loadSideOutletTemp, 0.001); + // EXPECT_NEAR(availableCapacity, thisHeatingPLHP->loadSideHeatTransfer, 0.001); + } + + // now we can call it again from the load side, but this time there is no load (still firsthvac) + { + bool firstHVAC = true; + Real64 curLoad = 0.0; + bool runFlag = true; + Real64 constexpr expectedLoadMassFlowRate = 0.09999; + Real64 constexpr expectedCp = 4180; + Real64 constexpr specifiedLoadSetpoint = 45; + Real64 const calculatedLoadInletTemp = specifiedLoadSetpoint - curLoad / (expectedLoadMassFlowRate * expectedCp); + state->dataLoopNodes->Node(thisHeatingPLHP->loadSideNodes.outlet).TempSetPoint = specifiedLoadSetpoint; + state->dataLoopNodes->Node(thisHeatingPLHP->loadSideNodes.inlet).Temp = calculatedLoadInletTemp; + state->dataLoopNodes->Node(thisHeatingPLHP->sourceSideNodes.inlet).Temp = 30; + thisHeatingPLHP->simulate(*state, myLoadLocation, firstHVAC, curLoad, runFlag); + // expect it to miss setpoint and be at max capacity + EXPECT_NEAR(45.0, thisHeatingPLHP->loadSideOutletTemp, 0.001); + EXPECT_NEAR(30.0, thisHeatingPLHP->sourceSideOutletTemp, 0.001); + EXPECT_NEAR(0.0, thisEIRPlantLoopHP->powerUsage, 0.001); + } +} + +TEST_F(EnergyPlusFixture, Test_HeatRecoveryGetInputs_AirSource) +{ + std::string const idf_objects = delimited_string({"HeatPump:PlantLoop:EIR:Cooling,", + " hp cooling side,", + " node 1,", + " node 2,", + " AirSource,", + " node 3,", + " node 4,", + " node 5,", + " node 6,", + " hp heating side,", + " 0.005,", + " Autosize,", + " Autosize,", + " Autosize,", + " 1.0,", + " 1,", + " dummyCurve,", + " dummyCurve,", + " dummyCurve;", + "HeatPump:PlantLoop:EIR:Heating,", + " hp heating side,", + " node 7,", + " node 8,", + " AirSource,", + " node 9,", + " node 10,", + " node 11,", + " node 12,", + " hp cooling side,", + " 0.005,", + " Autosize,", + " Autosize,", + " Autosize,", + " 2.0,", + " 1,", + " dummyCurve,", + " dummyCurve,", + " dummyCurve;", + "Curve:Linear,", + " dummyCurve,", + " 1,", + " 0,", + " 1,", + " 1;"}); + ASSERT_TRUE(process_idf(idf_objects)); + state->init_state(*state); + + // call the factory with a valid name to trigger reading inputs + EIRPlantLoopHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpEIRCooling, "HP COOLING SIDE"); + + // verify the size of the vector and the processed condition + EXPECT_EQ(2u, state->dataEIRPlantLoopHeatPump->heatPumps.size()); + + // for now we know the order is maintained, so get each heat pump object + EIRPlantLoopHeatPump *thisCoolingPLHP = &state->dataEIRPlantLoopHeatPump->heatPumps[0]; + EIRPlantLoopHeatPump *thisHeatingPLHP = &state->dataEIRPlantLoopHeatPump->heatPumps[1]; + + // check heat recovery input fields + EXPECT_TRUE(thisCoolingPLHP->heatRecoveryAvailable); + EXPECT_TRUE(thisHeatingPLHP->heatRecoveryAvailable); + EXPECT_TRUE(thisCoolingPLHP->heatRecoveryDesignVolFlowRateWasAutoSized); + EXPECT_TRUE(thisHeatingPLHP->heatRecoveryDesignVolFlowRateWasAutoSized); + EXPECT_EQ(thisCoolingPLHP->maxHeatRecoveryTempLimit, 60.0); + EXPECT_EQ(thisHeatingPLHP->minHeatRecoveryTempLimit, 4.5); +} + +TEST_F(EnergyPlusFixture, Test_HeatRecoveryFlowSizing_AirSource) +{ + std::string const idf_objects = delimited_string({"HeatPump:PlantLoop:EIR:Cooling,", + " hp cooling side,", + " node 1,", + " node 2,", + " AirSource,", + " node 3,", + " node 4,", + " node 5,", + " node 6,", + " hp heating side,", + " 0.005,", + " Autosize,", + " Autosize,", + " Autosize,", + " 1.0,", + " 1,", + " dummyCurve,", + " dummyCurve,", + " dummyCurve;", + "HeatPump:PlantLoop:EIR:Heating,", + " hp heating side,", + " node 7,", + " node 8,", + " AirSource,", + " node 9,", + " node 10,", + " node 11,", + " node 12,", + " hp cooling side,", + " 0.005,", + " Autosize,", + " Autosize,", + " Autosize,", + " 2.0,", + " 1,", + " dummyCurve,", + " dummyCurve,", + " dummyCurve;", + "Curve:Linear,", + " dummyCurve,", + " 1,", + " 0,", + " 1,", + " 1;"}); + ASSERT_TRUE(process_idf(idf_objects)); + state->init_state(*state); + + // call the factory with a valid name to trigger reading inputs + EIRPlantLoopHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpEIRCooling, "HP COOLING SIDE"); + + // verify the size of the vector and the processed condition + EXPECT_EQ(2u, state->dataEIRPlantLoopHeatPump->heatPumps.size()); + + // for now we know the order is maintained, so get each heat pump object + EIRPlantLoopHeatPump *thisCoolingPLHP = &state->dataEIRPlantLoopHeatPump->heatPumps[0]; + EIRPlantLoopHeatPump *thisHeatingPLHP = &state->dataEIRPlantLoopHeatPump->heatPumps[1]; + + // check heat recovery input fields + EXPECT_TRUE(thisCoolingPLHP->heatRecoveryAvailable); + EXPECT_TRUE(thisHeatingPLHP->heatRecoveryAvailable); + EXPECT_TRUE(thisCoolingPLHP->heatRecoveryDesignVolFlowRateWasAutoSized); + EXPECT_TRUE(thisHeatingPLHP->heatRecoveryDesignVolFlowRateWasAutoSized); + EXPECT_EQ(thisHeatingPLHP->minHeatRecoveryTempLimit, 4.5); + EXPECT_EQ(thisCoolingPLHP->maxHeatRecoveryTempLimit, 60.0); + + // We'll set up two plant loops: load heating loop and load side cooling loop + state->dataPlnt->TotNumLoops = 2; + state->dataPlnt->PlantLoop.allocate(state->dataPlnt->TotNumLoops); + state->dataSize->PlantSizData.allocate(2); + // chilled water plant loop + state->dataPlnt->PlantLoop(1).FluidName = "WATER"; + state->dataPlnt->PlantLoop(1).glycol = Fluid::GetWater(*state); + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Demand).TotalBranches = 1; + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Demand).Branch.allocate(1); + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).TotalComponents = 1; + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp.allocate(1); + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).TotalBranches = 1; + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch.allocate(1); + 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 &loop1supplyComponent1 = state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp(1); + auto &loop1demandComponent1 = state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp(1); + loop1supplyComponent1.Type = DataPlant::PlantEquipmentType::HeatPumpEIRCooling; + loop1supplyComponent1.Name = thisCoolingPLHP->name; + loop1supplyComponent1.NodeNumIn = thisCoolingPLHP->loadSideNodes.inlet; + // heat recovery component on the demand side of loop2 + loop1demandComponent1.Type = DataPlant::PlantEquipmentType::HeatPumpEIRHeating; + loop1demandComponent1.Name = thisHeatingPLHP->name; + loop1demandComponent1.NodeNumIn = thisHeatingPLHP->heatRecoveryNodes.inlet; + // assign the CW plant sizing data + state->dataPlnt->PlantLoop(1).PlantSizNum = 1; + state->dataSize->PlantSizData(1).DeltaT = 6.67; + + // hot water plant loop + state->dataPlnt->PlantLoop(2).FluidName = "WATER"; + state->dataPlnt->PlantLoop(2).glycol = Fluid::GetWater(*state); + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).TotalBranches = 1; + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch.allocate(1); + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).TotalComponents = 1; + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp.allocate(1); + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Supply).TotalBranches = 1; + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Supply).Branch.allocate(1); + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).TotalComponents = 1; + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp.allocate(1); + auto &loop2supplyComponent1 = state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp(1); + auto &loop2demandComponent1 = state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp(1); + loop2supplyComponent1.Type = DataPlant::PlantEquipmentType::HeatPumpEIRHeating; + loop2supplyComponent1.Name = thisHeatingPLHP->name; + loop2supplyComponent1.NodeNumIn = thisHeatingPLHP->loadSideNodes.inlet; + // heat recovery component on the demand side of loop1 + loop2demandComponent1.Type = DataPlant::PlantEquipmentType::HeatPumpEIRCooling; + loop2demandComponent1.Name = thisCoolingPLHP->name; + loop2demandComponent1.NodeNumIn = thisCoolingPLHP->heatRecoveryNodes.inlet; + // assign the HW plant sizing data + state->dataPlnt->PlantLoop(2).PlantSizNum = 2; + state->dataSize->PlantSizData(2).DeltaT = 11.11; + + // the init call expects a "from" calling point + PlantLocation myCoolingLoadLocation = PlantLocation(1, DataPlant::LoopSideLocation::Supply, 1, 1); + PlantLocation myHWHeatRecoveryLocation = PlantLocation(2, DataPlant::LoopSideLocation::Demand, 1, 1); + PlantLocation myHeatingLoadLocation = PlantLocation(2, DataPlant::LoopSideLocation::Supply, 1, 1); + PlantLocation myCWHeatRecoveryLocation = PlantLocation(1, DataPlant::LoopSideLocation::Demand, 1, 1); + // set a couple global flags + state->dataGlobal->BeginEnvrnFlag = true; + state->dataPlnt->PlantFinalSizesOkayToReport = true; + state->dataPlnt->PlantFirstSizesOkayToReport = true; + state->dataPlnt->PlantFirstSizesOkayToFinalize = true; + // initialize so the components can find themselves on the plant + thisCoolingPLHP->onInitLoopEquip(*state, myCoolingLoadLocation); + thisHeatingPLHP->onInitLoopEquip(*state, myHeatingLoadLocation); + + // size the HW heat-recovery flow rate + // set properties at design HW temp (60.0C) + Real64 rhoHR = 983.20; + Real64 CpHR = 4185.00; + Real64 designHWHeatRecoveryHeatTransfer = thisCoolingPLHP->referenceCapacity * (1 + 1 / thisCoolingPLHP->referenceCOP); + Real64 expectedHWHeatRecoveryFlow = designHWHeatRecoveryHeatTransfer / (state->dataSize->PlantSizData(2).DeltaT * CpHR * rhoHR); + // size the CW heat-recovery flow rate + // reset properties at design CW temp (5.5C) + rhoHR = 999.90; + CpHR = 4197.93; + Real64 designCWHeatRecoveryHeatTransfer = thisHeatingPLHP->referenceCapacity * (1 - 1 / thisHeatingPLHP->referenceCOP); + Real64 expectedCWHeatRecoveryFlow = designCWHeatRecoveryHeatTransfer / (state->dataSize->PlantSizData(1).DeltaT * CpHR * rhoHR); + // check autosized heat recovery flow rates + EXPECT_NEAR(expectedHWHeatRecoveryFlow, thisCoolingPLHP->heatRecoveryDesignVolFlowRate, 0.00001); // 0.00612 + EXPECT_NEAR(expectedCWHeatRecoveryFlow, thisHeatingPLHP->heatRecoveryDesignVolFlowRate, 0.00001); // 0.00250 +} + +TEST_F(EnergyPlusFixture, CoolingwithHeatRecoverySimulate_AirSource) +{ + std::string const idf_objects = delimited_string({"HeatPump:PlantLoop:EIR:Cooling,", + " hp cooling side,", + " node 1,", + " node 2,", + " AirSource,", + " node 3,", + " node 4,", + " node 5,", + " node 6,", + " hp heating side,", + " 0.005,", + " Autosize,", + " Autosize,", + " Autosize,", + " 3.0,", + " 1,", + " dummyCurve,", + " dummyCurve,", + " dummyCurve;", + "HeatPump:PlantLoop:EIR:Heating,", + " hp heating side,", + " node 7,", + " node 8,", + " AirSource,", + " node 9,", + " node 10,", + " node 11,", + " node 12,", + " hp cooling side,", + " 0.005,", + " Autosize,", + " Autosize,", + " Autosize,", + " 3.0,", + " 1,", + " dummyCurve,", + " dummyCurve,", + " dummyCurve;", + "Curve:Linear,", + " dummyCurve,", + " 1,", + " 0,", + " 1,", + " 1;"}); + ASSERT_TRUE(process_idf(idf_objects)); + state->init_state(*state); + + // call the factory with a valid name to trigger reading inputs + EIRPlantLoopHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpEIRCooling, "HP COOLING SIDE"); + + // verify the size of the vector and the processed condition + EXPECT_EQ(2u, state->dataEIRPlantLoopHeatPump->heatPumps.size()); + + // for now we know the order is maintained, so get each heat pump object + EIRPlantLoopHeatPump *thisCoolingPLHP = &state->dataEIRPlantLoopHeatPump->heatPumps[0]; + EIRPlantLoopHeatPump *thisHeatingPLHP = &state->dataEIRPlantLoopHeatPump->heatPumps[1]; + + // check heat recovery input fields + EXPECT_TRUE(thisCoolingPLHP->heatRecoveryAvailable); + EXPECT_TRUE(thisHeatingPLHP->heatRecoveryAvailable); + EXPECT_TRUE(thisCoolingPLHP->heatRecoveryDesignVolFlowRateWasAutoSized); + EXPECT_TRUE(thisHeatingPLHP->heatRecoveryDesignVolFlowRateWasAutoSized); + EXPECT_EQ(thisHeatingPLHP->minHeatRecoveryTempLimit, 4.5); + EXPECT_EQ(thisCoolingPLHP->maxHeatRecoveryTempLimit, 60.0); + + // We'll set up two plant loops: load heating loop and load side cooling loop + state->dataPlnt->TotNumLoops = 2; + state->dataPlnt->PlantLoop.allocate(state->dataPlnt->TotNumLoops); + state->dataSize->PlantSizData.allocate(2); + // chilled water plant loop + state->dataPlnt->PlantLoop(1).FluidName = "WATER"; + state->dataPlnt->PlantLoop(1).glycol = Fluid::GetWater(*state); + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Demand).TotalBranches = 1; + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Demand).Branch.allocate(1); + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).TotalComponents = 1; + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp.allocate(1); + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).TotalBranches = 1; + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch.allocate(1); + 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 &loop1supplyComponent1 = state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp(1); + auto &loop1demandComponent1 = state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp(1); + loop1supplyComponent1.Type = DataPlant::PlantEquipmentType::HeatPumpEIRCooling; + loop1supplyComponent1.Name = thisCoolingPLHP->name; + loop1supplyComponent1.NodeNumIn = thisCoolingPLHP->loadSideNodes.inlet; + // heat recovery component on the demand side of loop2 + loop1demandComponent1.Type = DataPlant::PlantEquipmentType::HeatPumpEIRHeating; + loop1demandComponent1.Name = thisHeatingPLHP->name; + loop1demandComponent1.NodeNumIn = thisHeatingPLHP->heatRecoveryNodes.inlet; + // assign the CW plant sizing data + state->dataPlnt->PlantLoop(1).PlantSizNum = 1; + state->dataSize->PlantSizData(1).DeltaT = 6.67; + + state->dataPlnt->PlantLoop(1).LoopDemandCalcScheme = DataPlant::LoopDemandCalcScheme::SingleSetPoint; + auto &PLHPPlantLoadSideComp = state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp(1); + PLHPPlantLoadSideComp.Type = DataPlant::PlantEquipmentType::HeatPumpEIRCooling; + PLHPPlantLoadSideComp.CurOpSchemeType = DataPlant::OpScheme::CompSetPtBased; + + // hot water plant loop + state->dataPlnt->PlantLoop(2).FluidName = "WATER"; + state->dataPlnt->PlantLoop(2).glycol = Fluid::GetWater(*state); + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).TotalBranches = 1; + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch.allocate(1); + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).TotalComponents = 1; + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp.allocate(1); + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Supply).TotalBranches = 1; + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Supply).Branch.allocate(1); + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).TotalComponents = 1; + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp.allocate(1); + auto &loop2supplyComponent1 = state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp(1); + auto &loop2demandComponent1 = state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp(1); + loop2supplyComponent1.Type = DataPlant::PlantEquipmentType::HeatPumpEIRHeating; + loop2supplyComponent1.Name = thisHeatingPLHP->name; + loop2supplyComponent1.NodeNumIn = thisHeatingPLHP->loadSideNodes.inlet; + // heat recovery component on the demand side of loop1 + loop2demandComponent1.Type = DataPlant::PlantEquipmentType::HeatPumpEIRCooling; + loop2demandComponent1.Name = thisCoolingPLHP->name; + loop2demandComponent1.NodeNumIn = thisCoolingPLHP->heatRecoveryNodes.inlet; + // assign the HW plant sizing data + state->dataPlnt->PlantLoop(2).PlantSizNum = 2; + state->dataSize->PlantSizData(2).DeltaT = 11.11; + + // the init call expects a "from" calling point + PlantLocation myCoolingLoadLocation = PlantLocation(1, DataPlant::LoopSideLocation::Supply, 1, 1); + PlantLocation myHWHeatRecoveryLocation = PlantLocation(2, DataPlant::LoopSideLocation::Demand, 1, 1); + PlantLocation myHeatingLoadLocation = PlantLocation(2, DataPlant::LoopSideLocation::Supply, 1, 1); + PlantLocation myCWHeatRecoveryLocation = PlantLocation(1, DataPlant::LoopSideLocation::Demand, 1, 1); + // set a couple global flags + state->dataGlobal->BeginEnvrnFlag = true; + state->dataPlnt->PlantFinalSizesOkayToReport = true; + state->dataPlnt->PlantFirstSizesOkayToReport = true; + state->dataPlnt->PlantFirstSizesOkayToFinalize = true; + // initialize so the components can find themselves on the plant + thisCoolingPLHP->onInitLoopEquip(*state, myCoolingLoadLocation); + thisHeatingPLHP->onInitLoopEquip(*state, myHeatingLoadLocation); + + thisCoolingPLHP->setPointNodeNum = thisCoolingPLHP->loadSideNodes.outlet; + thisHeatingPLHP->setPointNodeNum = thisHeatingPLHP->loadSideNodes.outlet; + + // call from load side location, firsthvac, no load, not running, verify the unit doesn't have any values lingering + thisCoolingPLHP->loadSideHeatTransfer = 2000; + thisCoolingPLHP->loadSideInletTemp = 23.0; + thisCoolingPLHP->loadSideOutletTemp = 43.0; + thisCoolingPLHP->powerUsage = 100.0; + thisCoolingPLHP->sourceSideHeatTransfer = 60.0; + thisCoolingPLHP->sourceSideInletTemp = 33.0; + thisCoolingPLHP->sourceSideOutletTemp = 43.0; + thisCoolingPLHP->heatRecoveryInletTemp = 45.0; + thisCoolingPLHP->heatRecoveryOutletTemp = 55.0; + thisCoolingPLHP->sysControlType = ControlType::Setpoint; + bool firstHVAC = true; + Real64 curLoad = 0.0; + bool runFlag = false; + thisCoolingPLHP->simulate(*state, myCoolingLoadLocation, firstHVAC, curLoad, runFlag); + EXPECT_NEAR(0.0, thisCoolingPLHP->loadSideHeatTransfer, 0.001); + EXPECT_NEAR(0.0, thisCoolingPLHP->sourceSideHeatTransfer, 0.001); + EXPECT_NEAR(0.0, thisCoolingPLHP->powerUsage, 0.001); + EXPECT_NEAR(0.0, thisCoolingPLHP->heatRecoveryRate, 0.001); + EXPECT_NEAR(thisCoolingPLHP->loadSideInletTemp, thisCoolingPLHP->loadSideOutletTemp, 0.001); + EXPECT_NEAR(thisCoolingPLHP->sourceSideInletTemp, thisCoolingPLHP->sourceSideOutletTemp, 0.001); + EXPECT_NEAR(thisCoolingPLHP->heatRecoveryInletTemp, thisCoolingPLHP->heatRecoveryOutletTemp, 0.001); + + // now we can call it again from the load side, but this time there is load + { + firstHVAC = true; + curLoad = -69993.3; // current cooling load + runFlag = true; + Real64 expectedLoadMassFlowRate = thisCoolingPLHP->loadSideDesignMassFlowRate; + Real64 constexpr expectedCp = 4182.3220354805; + Real64 constexpr specifiedLoadSetpoint = 15.0; + Real64 const calculatedLoadInletTemp = specifiedLoadSetpoint - curLoad / (expectedLoadMassFlowRate * expectedCp); + state->dataLoopNodes->Node(thisCoolingPLHP->loadSideNodes.outlet).TempSetPoint = specifiedLoadSetpoint; + state->dataLoopNodes->Node(thisCoolingPLHP->loadSideNodes.inlet).Temp = calculatedLoadInletTemp; + state->dataLoopNodes->Node(thisCoolingPLHP->sourceSideNodes.inlet).Temp = 30; + state->dataLoopNodes->Node(thisCoolingPLHP->heatRecoveryNodes.inlet).Temp = 45.0; + thisCoolingPLHP->simulate(*state, myCoolingLoadLocation, firstHVAC, curLoad, runFlag); + // expect it to meet the setpoint while operating at part load + EXPECT_NEAR(15.0, thisCoolingPLHP->loadSideOutletTemp, 0.001); + EXPECT_NEAR(0.5, thisCoolingPLHP->partLoadRatio, 0.001); + EXPECT_NEAR(69993.3, thisCoolingPLHP->loadSideHeatTransfer, 0.001); + EXPECT_NEAR(23331.1, thisCoolingPLHP->powerUsage, 0.001); + EXPECT_NEAR(93324.4, thisCoolingPLHP->heatRecoveryRate, 0.001); + EXPECT_NEAR(0.0, thisCoolingPLHP->sourceSideHeatTransfer, 0.001); + EXPECT_NEAR(45.0, thisCoolingPLHP->heatRecoveryInletTemp, 0.001); + EXPECT_NEAR(50.469, thisCoolingPLHP->heatRecoveryOutletTemp, 0.001); + } + + // now we can call it again from the load side, but this time there is load + // higher heat recovery temperature + { + firstHVAC = true; + curLoad = -69993.3; // current cooling load + runFlag = true; + Real64 expectedLoadMassFlowRate = thisCoolingPLHP->loadSideDesignMassFlowRate; + Real64 constexpr expectedCp = 4182.3220354805; + Real64 constexpr specifiedLoadSetpoint = 15.0; + Real64 const calculatedLoadInletTemp = specifiedLoadSetpoint - curLoad / (expectedLoadMassFlowRate * expectedCp); + state->dataLoopNodes->Node(thisCoolingPLHP->loadSideNodes.outlet).TempSetPoint = specifiedLoadSetpoint; + state->dataLoopNodes->Node(thisCoolingPLHP->loadSideNodes.inlet).Temp = calculatedLoadInletTemp; + state->dataLoopNodes->Node(thisCoolingPLHP->sourceSideNodes.inlet).Temp = 30; + state->dataLoopNodes->Node(thisCoolingPLHP->heatRecoveryNodes.inlet).Temp = 58.0; + thisCoolingPLHP->simulate(*state, myCoolingLoadLocation, firstHVAC, curLoad, runFlag); + // expect it to meet the setpoint while operating at part load + EXPECT_NEAR(15.0, thisCoolingPLHP->loadSideOutletTemp, 0.001); + EXPECT_NEAR(0.5, thisCoolingPLHP->partLoadRatio, 0.001); + EXPECT_NEAR(69993.3, thisCoolingPLHP->loadSideHeatTransfer, 0.001); + EXPECT_NEAR(23331.1, thisCoolingPLHP->powerUsage, 0.001); + // energy balance or energy conservation at the condenser + Real64 energyBalanceCondenser = thisCoolingPLHP->loadSideHeatTransfer + thisCoolingPLHP->powerUsage; + EXPECT_NEAR(93324.4, energyBalanceCondenser, 0.001); + // heat rejected is split b/n heat recovery and source side heat transfer due to tem limit + EXPECT_NEAR(34164.275, thisCoolingPLHP->heatRecoveryRate, 0.001); + EXPECT_NEAR(59160.125, thisCoolingPLHP->sourceSideHeatTransfer, 0.001); + Real64 totalHeatRejected = thisCoolingPLHP->heatRecoveryRate + thisCoolingPLHP->sourceSideHeatTransfer; + EXPECT_NEAR(93324.4, totalHeatRejected, 0.001); + // total heat rejected == energy balance at the condenser + EXPECT_NEAR(energyBalanceCondenser, totalHeatRejected, 0.001); + EXPECT_NEAR(58.0, thisCoolingPLHP->heatRecoveryInletTemp, 0.001); + // heat recovery outlet temperature is capped @ 60C. + EXPECT_NEAR(60.0, thisCoolingPLHP->heatRecoveryOutletTemp, 0.001); + } +} + +TEST_F(EnergyPlusFixture, HeatingwithHeatRecoverySimulate_AirSource) +{ + std::string const idf_objects = delimited_string({"HeatPump:PlantLoop:EIR:Cooling,", + " hp cooling side,", + " node 1,", + " node 2,", + " AirSource,", + " node 3,", + " node 4,", + " node 5,", + " node 6,", + " hp heating side,", + " 0.005,", + " Autosize,", + " Autosize,", + " Autosize,", + " 3.0,", + " 1,", + " dummyCurve,", + " dummyCurve,", + " dummyCurve;", + "HeatPump:PlantLoop:EIR:Heating,", + " hp heating side,", + " node 7,", + " node 8,", + " AirSource,", + " node 9,", + " node 10,", + " node 11,", + " node 12,", + " hp cooling side,", + " 0.005,", + " Autosize,", + " Autosize,", + " Autosize,", + " 3.0,", + " 1,", + " dummyCurve,", + " dummyCurve,", + " dummyCurve;", + "Curve:Linear,", + " dummyCurve,", + " 1,", + " 0,", + " 1,", + " 1;"}); + ASSERT_TRUE(process_idf(idf_objects)); + state->init_state(*state); + + // call the factory with a valid name to trigger reading inputs + EIRPlantLoopHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpEIRCooling, "HP COOLING SIDE"); + + // verify the size of the vector and the processed condition + EXPECT_EQ(2u, state->dataEIRPlantLoopHeatPump->heatPumps.size()); + + // for now we know the order is maintained, so get each heat pump object + EIRPlantLoopHeatPump *thisCoolingPLHP = &state->dataEIRPlantLoopHeatPump->heatPumps[0]; + EIRPlantLoopHeatPump *thisHeatingPLHP = &state->dataEIRPlantLoopHeatPump->heatPumps[1]; + + // check heat recovery input fields + EXPECT_TRUE(thisCoolingPLHP->heatRecoveryAvailable); + EXPECT_TRUE(thisHeatingPLHP->heatRecoveryAvailable); + EXPECT_TRUE(thisCoolingPLHP->heatRecoveryDesignVolFlowRateWasAutoSized); + EXPECT_TRUE(thisHeatingPLHP->heatRecoveryDesignVolFlowRateWasAutoSized); + EXPECT_EQ(thisHeatingPLHP->minHeatRecoveryTempLimit, 4.5); + EXPECT_EQ(thisCoolingPLHP->maxHeatRecoveryTempLimit, 60.0); + + // We'll set up two plant loops: load heating loop and load side cooling loop + state->dataPlnt->TotNumLoops = 2; + state->dataPlnt->PlantLoop.allocate(state->dataPlnt->TotNumLoops); + state->dataSize->PlantSizData.allocate(2); + // chilled water plant loop + state->dataPlnt->PlantLoop(1).FluidName = "WATER"; + state->dataPlnt->PlantLoop(1).glycol = Fluid::GetWater(*state); + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Demand).TotalBranches = 1; + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Demand).Branch.allocate(1); + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).TotalComponents = 1; + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp.allocate(1); + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).TotalBranches = 1; + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch.allocate(1); + 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 &loop1supplyComponent1 = state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp(1); + auto &loop1demandComponent1 = state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp(1); + loop1supplyComponent1.Type = DataPlant::PlantEquipmentType::HeatPumpEIRCooling; + loop1supplyComponent1.Name = thisCoolingPLHP->name; + loop1supplyComponent1.NodeNumIn = thisCoolingPLHP->loadSideNodes.inlet; + // heat recovery component on the demand side of loop1 + loop1demandComponent1.Type = DataPlant::PlantEquipmentType::HeatPumpEIRHeating; + loop1demandComponent1.Name = thisHeatingPLHP->name; + loop1demandComponent1.NodeNumIn = thisHeatingPLHP->heatRecoveryNodes.inlet; + // assign the CW plant sizing data + state->dataPlnt->PlantLoop(1).PlantSizNum = 1; + state->dataSize->PlantSizData(1).DeltaT = 6.67; + + state->dataPlnt->PlantLoop(1).LoopDemandCalcScheme = DataPlant::LoopDemandCalcScheme::SingleSetPoint; + auto &PLHPPlantLoadSideComp = state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp(1); + PLHPPlantLoadSideComp.Type = DataPlant::PlantEquipmentType::HeatPumpEIRCooling; + PLHPPlantLoadSideComp.CurOpSchemeType = DataPlant::OpScheme::CompSetPtBased; + + // hot water plant loop + state->dataPlnt->PlantLoop(2).FluidName = "WATER"; + state->dataPlnt->PlantLoop(2).glycol = Fluid::GetWater(*state); + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).TotalBranches = 1; + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch.allocate(1); + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).TotalComponents = 1; + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp.allocate(1); + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Supply).TotalBranches = 1; + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Supply).Branch.allocate(1); + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).TotalComponents = 1; + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp.allocate(1); + auto &loop2supplyComponent1 = state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp(1); + auto &loop2demandComponent1 = state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp(1); + loop2supplyComponent1.Type = DataPlant::PlantEquipmentType::HeatPumpEIRHeating; + loop2supplyComponent1.Name = thisHeatingPLHP->name; + loop2supplyComponent1.NodeNumIn = thisHeatingPLHP->loadSideNodes.inlet; + // heat recovery component on the demand side of loop2 + loop2demandComponent1.Type = DataPlant::PlantEquipmentType::HeatPumpEIRCooling; + loop2demandComponent1.Name = thisCoolingPLHP->name; + loop2demandComponent1.NodeNumIn = thisCoolingPLHP->heatRecoveryNodes.inlet; + // assign the HW plant sizing data + state->dataPlnt->PlantLoop(2).PlantSizNum = 2; + state->dataSize->PlantSizData(2).DeltaT = 11.11; + + // the init call expects a "from" calling point + PlantLocation myCoolingLoadLocation = PlantLocation(1, DataPlant::LoopSideLocation::Supply, 1, 1); + PlantLocation myHWHeatRecoveryLocation = PlantLocation(2, DataPlant::LoopSideLocation::Demand, 1, 1); + PlantLocation myHeatingLoadLocation = PlantLocation(2, DataPlant::LoopSideLocation::Supply, 1, 1); + PlantLocation myCWHeatRecoveryLocation = PlantLocation(1, DataPlant::LoopSideLocation::Demand, 1, 1); + + state->dataPlnt->PlantLoop(2).LoopDemandCalcScheme = DataPlant::LoopDemandCalcScheme::SingleSetPoint; + auto &PLHPPlantLoadSideComp2 = state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp(1); + PLHPPlantLoadSideComp2.Type = DataPlant::PlantEquipmentType::HeatPumpEIRHeating; + PLHPPlantLoadSideComp2.CurOpSchemeType = DataPlant::OpScheme::CompSetPtBased; + + // set a couple global flags + state->dataGlobal->BeginEnvrnFlag = true; + state->dataPlnt->PlantFinalSizesOkayToReport = true; + state->dataPlnt->PlantFirstSizesOkayToReport = true; + state->dataPlnt->PlantFirstSizesOkayToFinalize = true; + // initialize so the components can find themselves on the plant + thisCoolingPLHP->onInitLoopEquip(*state, myCoolingLoadLocation); + thisHeatingPLHP->onInitLoopEquip(*state, myHeatingLoadLocation); + + thisCoolingPLHP->setPointNodeNum = thisCoolingPLHP->loadSideNodes.outlet; + thisHeatingPLHP->setPointNodeNum = thisHeatingPLHP->loadSideNodes.outlet; + + // call from load side location, firsthvac, no load, not running, verify the unit doesn't have any values lingering + thisHeatingPLHP->loadSideHeatTransfer = 5000; + thisHeatingPLHP->loadSideInletTemp = 43.0; + thisHeatingPLHP->loadSideOutletTemp = 53.0; + thisHeatingPLHP->powerUsage = 200.0; + thisHeatingPLHP->sourceSideHeatTransfer = 5200.0; + thisHeatingPLHP->sourceSideInletTemp = 13.0; + thisHeatingPLHP->sourceSideOutletTemp = 8.0; + thisHeatingPLHP->heatRecoveryInletTemp = 15.0; + thisHeatingPLHP->heatRecoveryOutletTemp = 10.0; + thisHeatingPLHP->sysControlType = ControlType::Setpoint; + bool firstHVAC = true; + Real64 curLoad = 0.0; + bool runFlag = false; + thisHeatingPLHP->simulate(*state, myHeatingLoadLocation, firstHVAC, curLoad, runFlag); + EXPECT_NEAR(0.0, thisHeatingPLHP->loadSideHeatTransfer, 0.001); + EXPECT_NEAR(0.0, thisHeatingPLHP->sourceSideHeatTransfer, 0.001); + EXPECT_NEAR(0.0, thisHeatingPLHP->powerUsage, 0.001); + EXPECT_NEAR(0.0, thisHeatingPLHP->heatRecoveryRate, 0.001); + EXPECT_NEAR(thisHeatingPLHP->loadSideInletTemp, thisHeatingPLHP->loadSideOutletTemp, 0.001); + EXPECT_NEAR(thisHeatingPLHP->sourceSideInletTemp, thisHeatingPLHP->sourceSideOutletTemp, 0.001); + EXPECT_NEAR(thisHeatingPLHP->heatRecoveryInletTemp, thisHeatingPLHP->heatRecoveryOutletTemp, 0.001); + + // now we can call it again from the load side, but this time there is heating load + { + firstHVAC = true; + curLoad = 69993.3; // current heating load + runFlag = true; + Real64 expectedLoadMassFlowRate = thisHeatingPLHP->loadSideDesignMassFlowRate; + Real64 constexpr expectedCp = 4182.3220354805; + Real64 constexpr specifiedLoadSetpoint = 55.0; + Real64 const calculatedLoadInletTemp = specifiedLoadSetpoint - curLoad / (expectedLoadMassFlowRate * expectedCp); + state->dataLoopNodes->Node(thisHeatingPLHP->loadSideNodes.outlet).TempSetPoint = specifiedLoadSetpoint; + state->dataLoopNodes->Node(thisHeatingPLHP->loadSideNodes.inlet).Temp = calculatedLoadInletTemp; + state->dataLoopNodes->Node(thisHeatingPLHP->sourceSideNodes.inlet).Temp = 13; + state->dataLoopNodes->Node(thisHeatingPLHP->heatRecoveryNodes.inlet).Temp = 15.0; + thisHeatingPLHP->simulate(*state, myHeatingLoadLocation, firstHVAC, curLoad, runFlag); + // expect it to meet the setpoint while operating at part load + EXPECT_NEAR(55.0, thisHeatingPLHP->loadSideOutletTemp, 0.001); + EXPECT_NEAR(0.5, thisHeatingPLHP->partLoadRatio, 0.001); + EXPECT_NEAR(69982.238, thisHeatingPLHP->loadSideHeatTransfer, 0.001); + EXPECT_NEAR(23327.413, thisHeatingPLHP->powerUsage, 0.001); + EXPECT_NEAR(46654.825, thisHeatingPLHP->heatRecoveryRate, 0.001); + EXPECT_NEAR(0.0, thisHeatingPLHP->sourceSideHeatTransfer, 0.001); + EXPECT_NEAR(15.0, thisHeatingPLHP->heatRecoveryInletTemp, 0.001); + EXPECT_NEAR(11.655, thisHeatingPLHP->heatRecoveryOutletTemp, 0.001); + } + + // now we can call it again from the load side, but this time there is heating load + { + firstHVAC = true; + curLoad = 69993.3; // current heating load + runFlag = true; + Real64 expectedLoadMassFlowRate = thisHeatingPLHP->loadSideDesignMassFlowRate; + Real64 constexpr expectedCp = 4182.3220354805; + Real64 constexpr specifiedLoadSetpoint = 55.0; + Real64 const calculatedLoadInletTemp = specifiedLoadSetpoint - curLoad / (expectedLoadMassFlowRate * expectedCp); + state->dataLoopNodes->Node(thisHeatingPLHP->loadSideNodes.outlet).TempSetPoint = specifiedLoadSetpoint; + state->dataLoopNodes->Node(thisHeatingPLHP->loadSideNodes.inlet).Temp = calculatedLoadInletTemp; + state->dataLoopNodes->Node(thisHeatingPLHP->sourceSideNodes.inlet).Temp = 13; + state->dataLoopNodes->Node(thisHeatingPLHP->heatRecoveryNodes.inlet).Temp = 7.0; + thisHeatingPLHP->simulate(*state, myHeatingLoadLocation, firstHVAC, curLoad, runFlag); + // expect it to meet the setpoint while operating at part load + EXPECT_NEAR(55.0, thisHeatingPLHP->loadSideOutletTemp, 0.001); + EXPECT_NEAR(0.5, thisHeatingPLHP->partLoadRatio, 0.001); + EXPECT_NEAR(69982.238, thisHeatingPLHP->loadSideHeatTransfer, 0.001); + EXPECT_NEAR(23327.413, thisHeatingPLHP->powerUsage, 0.001); + EXPECT_NEAR(34956.434, thisHeatingPLHP->heatRecoveryRate, 0.001); + EXPECT_NEAR(11698.391, thisHeatingPLHP->sourceSideHeatTransfer, 0.001); + // heat balance or energy conservation applied at the evaporator (source side) + Real64 heatBalanceEvap = thisHeatingPLHP->loadSideHeatTransfer - thisHeatingPLHP->powerUsage; + EXPECT_NEAR(46654.825, heatBalanceEvap, 0.001); + // heat rejected is split b/n heat recovery and source side heat transfer due to temp limit + EXPECT_NEAR(34956.434, thisHeatingPLHP->heatRecoveryRate, 0.001); + EXPECT_NEAR(11698.391, thisHeatingPLHP->sourceSideHeatTransfer, 0.001); + // check the heat recovered at the evaporator (source) side + Real64 chilledWaterEnergyRecovered = thisHeatingPLHP->heatRecoveryRate + thisHeatingPLHP->sourceSideHeatTransfer; + EXPECT_NEAR(46654.825, chilledWaterEnergyRecovered, 0.001); + // check energy balance + EXPECT_NEAR(heatBalanceEvap, chilledWaterEnergyRecovered, 0.001); + EXPECT_NEAR(7.0, thisHeatingPLHP->heatRecoveryInletTemp, 0.001); + // heat recovery outlet temperature is capped @ 4.5C. + EXPECT_NEAR(4.5, thisHeatingPLHP->heatRecoveryOutletTemp, 0.001); + } +} + +TEST_F(EnergyPlusFixture, CoolingSimulate_WSHP_SourceSideOutletTemp) +{ + std::string const idf_objects = delimited_string({"HeatPump:PlantLoop:EIR:Cooling,", + " hp cooling side,", + " node 1,", + " node 2,", + " WaterSource,", + " node 3,", + " node 4,", + " ,", + " ,", + " ,", + " 0.0002,", + " 0.0002,", + " ,", + " 2000,", + " 3.00,", + " ,", + " dummyCurve,", + " dummyCurve,", + " dummyCurve;", + "Curve:Linear,", + " dummyCurve,", + " 0.95,", + " 0,", + " 1,", + " 1;"}); + ASSERT_TRUE(process_idf(idf_objects)); + state->init_state(*state); + + // set up the plant loops + // first the load side + state->dataPlnt->TotNumLoops = 2; + state->dataPlnt->PlantLoop.allocate(2); + + state->dataPlnt->PlantLoop(1).FluidName = "WATER"; + state->dataPlnt->PlantLoop(1).glycol = Fluid::GetWater(*state); + state->dataPlnt->PlantLoop(1).LoopDemandCalcScheme = DataPlant::LoopDemandCalcScheme::SingleSetPoint; + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).TotalBranches = 1; + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch.allocate(1); + 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 &PLHPPlantLoadSideComp = state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp(1); + PLHPPlantLoadSideComp.Type = DataPlant::PlantEquipmentType::HeatPumpEIRCooling; + PLHPPlantLoadSideComp.CurOpSchemeType = DataPlant::OpScheme::CompSetPtBased; + // then the source side + + state->dataPlnt->PlantLoop(2).FluidName = "WATER"; + state->dataPlnt->PlantLoop(2).glycol = Fluid::GetWater(*state); + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).TotalBranches = 1; + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch.allocate(1); + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).TotalComponents = 1; + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp.allocate(1); + auto &PLHPPlantLoadSourceComp = state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp(1); + PLHPPlantLoadSourceComp.Type = DataPlant::PlantEquipmentType::HeatPumpEIRCooling; + + // the init call expects a "from" calling point + PlantLocation myLoadLocation = PlantLocation(1, DataPlant::LoopSideLocation::Supply, 1, 1); + PlantLocation mySourceLocation = PlantLocation(2, DataPlant::LoopSideLocation::Demand, 1, 1); + + // call the factory with a valid name to trigger reading inputs + EIRPlantLoopHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpEIRCooling, "HP COOLING SIDE"); + + // verify the size of the vector and the processed condition + EXPECT_EQ(1u, state->dataEIRPlantLoopHeatPump->heatPumps.size()); + + // for now we know the order is maintained, so get each heat pump object + EIRPlantLoopHeatPump *thisCoolingPLHP = &state->dataEIRPlantLoopHeatPump->heatPumps[0]; + + // do a bit of extra wiring up to the plant + PLHPPlantLoadSideComp.Name = thisCoolingPLHP->name; + PLHPPlantLoadSideComp.NodeNumIn = thisCoolingPLHP->loadSideNodes.inlet; + PLHPPlantLoadSourceComp.Name = thisCoolingPLHP->name; + PLHPPlantLoadSourceComp.NodeNumIn = thisCoolingPLHP->sourceSideNodes.inlet; + + // call for all initialization + state->dataGlobal->BeginEnvrnFlag = true; + state->dataPlnt->PlantFirstSizesOkayToFinalize = true; + thisCoolingPLHP->onInitLoopEquip(*state, myLoadLocation); + + // call from load side location, firsthvac, no load, not running, verify the unit doesn't have any values lingering + thisCoolingPLHP->loadSideHeatTransfer = 2000; + thisCoolingPLHP->loadSideInletTemp = 23.0; + thisCoolingPLHP->loadSideOutletTemp = 42.0; + thisCoolingPLHP->powerUsage = 400.0; + thisCoolingPLHP->sourceSideHeatTransfer = 2000.0; + thisCoolingPLHP->sourceSideInletTemp = 45.0; + thisCoolingPLHP->sourceSideOutletTemp = 83.0; + bool firstHVAC = true; + Real64 curLoad = 0.0; + bool runFlag = false; + thisCoolingPLHP->heatRecoveryHeatPump = true; + thisCoolingPLHP->simulate(*state, myLoadLocation, firstHVAC, curLoad, runFlag); + EXPECT_NEAR(0.0, thisCoolingPLHP->loadSideHeatTransfer, 0.001); + EXPECT_NEAR(0.0, thisCoolingPLHP->sourceSideHeatTransfer, 0.001); + EXPECT_NEAR(0.0, thisCoolingPLHP->powerUsage, 0.001); + EXPECT_NEAR(thisCoolingPLHP->loadSideInletTemp, thisCoolingPLHP->loadSideOutletTemp, 0.001); + EXPECT_NEAR(thisCoolingPLHP->sourceSideInletTemp, thisCoolingPLHP->sourceSideOutletTemp, 0.001); + + // call from source side location, firsthvac, no load, not running, connected loop should be triggered to resimulate + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).SimLoopSideNeeded = false; + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).SimLoopSideNeeded = false; + thisCoolingPLHP->simulate(*state, mySourceLocation, firstHVAC, curLoad, runFlag); + EXPECT_TRUE(state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).SimLoopSideNeeded); + + thisCoolingPLHP->setPointNodeNum = thisCoolingPLHP->loadSideNodes.outlet; + + // now we can call it again from the load side, but this time there is load (still firsthvac, unit can meet load) + { + firstHVAC = true; + curLoad = -1900; + runFlag = true; + Real64 constexpr expectedLoadMassFlowRate = 0.200; + state->dataLoopNodes->Node(thisCoolingPLHP->loadSideNodes.inlet).MassFlowRate = expectedLoadMassFlowRate; + state->dataLoopNodes->Node(thisCoolingPLHP->sourceSideNodes.inlet).MassFlowRate = expectedLoadMassFlowRate; + Real64 constexpr expectedCp = 4183; + Real64 constexpr specifiedLoadSetpoint = 15; + Real64 const calculatedLoadInletTemp = specifiedLoadSetpoint - curLoad / (expectedLoadMassFlowRate * expectedCp); + state->dataLoopNodes->Node(thisCoolingPLHP->setPointNodeNum).TempSetPoint = specifiedLoadSetpoint; + state->dataLoopNodes->Node(thisCoolingPLHP->loadSideNodes.inlet).Temp = calculatedLoadInletTemp; + state->dataLoopNodes->Node(thisCoolingPLHP->sourceSideNodes.inlet).Temp = 45; + thisCoolingPLHP->maxSourceTempLimit = 50.0; + thisCoolingPLHP->simulate(*state, myLoadLocation, firstHVAC, curLoad, runFlag); + // expect it to meet setpoint and have some pre-evaluated conditions + EXPECT_NEAR(1.000, thisCoolingPLHP->partLoadRatio, 0.01); + EXPECT_NEAR(specifiedLoadSetpoint, thisCoolingPLHP->loadSideOutletTemp, 0.01); + EXPECT_NEAR(-curLoad, thisCoolingPLHP->loadSideHeatTransfer, 0.01); + EXPECT_NEAR(2471.583, thisCoolingPLHP->sourceSideHeatTransfer, 0.01); + EXPECT_NEAR(47.957, thisCoolingPLHP->sourceSideOutletTemp, 0.01); + } + + // now we can call it again from the load side, but this time there is source side temperature limit exceeded + { + firstHVAC = true; + curLoad = -1900; + runFlag = true; + Real64 constexpr expectedLoadMassFlowRate = 0.200; + state->dataLoopNodes->Node(thisCoolingPLHP->loadSideNodes.inlet).MassFlowRate = expectedLoadMassFlowRate; + state->dataLoopNodes->Node(thisCoolingPLHP->sourceSideNodes.inlet).MassFlowRate = expectedLoadMassFlowRate; + Real64 constexpr expectedCp = 4183; + Real64 constexpr specifiedLoadSetpoint = 15; + Real64 const calculatedLoadInletTemp = specifiedLoadSetpoint - curLoad / (expectedLoadMassFlowRate * expectedCp); + state->dataLoopNodes->Node(thisCoolingPLHP->setPointNodeNum).TempSetPoint = specifiedLoadSetpoint; + state->dataLoopNodes->Node(thisCoolingPLHP->loadSideNodes.inlet).Temp = calculatedLoadInletTemp; + state->dataLoopNodes->Node(thisCoolingPLHP->sourceSideNodes.inlet).Temp = 48; + thisCoolingPLHP->maxSourceTempLimit = 50.0; + thisCoolingPLHP->simulate(*state, myLoadLocation, firstHVAC, curLoad, runFlag); + // expect it to meet setpoint and have some pre-evaluated conditions + // reduced PLR to meet the source side outlet temperature limit specified + EXPECT_NEAR(0.68, thisCoolingPLHP->partLoadRatio, 0.01); + EXPECT_NEAR(15.73, thisCoolingPLHP->loadSideOutletTemp, 0.01); + EXPECT_NEAR(1285.38, thisCoolingPLHP->loadSideHeatTransfer, 0.01); + EXPECT_NEAR(386.69, thisCoolingPLHP->powerUsage, 0.01); + EXPECT_NEAR(1672.07, thisCoolingPLHP->loadSideHeatTransfer + thisCoolingPLHP->powerUsage, 0.01); + EXPECT_NEAR(1672.07, thisCoolingPLHP->sourceSideHeatTransfer, 0.01); + 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)); + state->init_state(*state); + + // set up the plant loops + // first the load side + state->dataPlnt->TotNumLoops = 1; + state->dataPlnt->PlantLoop.allocate(1); + + state->dataPlnt->PlantLoop(1).FluidName = "WATER"; + state->dataPlnt->PlantLoop(1).glycol = Fluid::GetWater(*state); + 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 hardcoded 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->curves(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->curves(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->curves(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->curves(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->curves(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->curves(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->curves(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->curves(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->curves(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->curves(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 +// EnergyPlus, Copyright (c) 1996-present, The Board of Trustees of the University of Illinois, +// The Regents of the University of California, through Lawrence Berkeley National Laboratory +// (subject to receipt of any required approvals from the U.S. Dept. of Energy), Oak Ridge +// National Laboratory, managed by UT-Battelle, Alliance for Energy Innovation, LLC, and other +// contributors. All rights reserved. +// +// NOTICE: This Software was developed under funding from the U.S. Department of Energy and the +// U.S. Government consequently retains certain rights. As such, the U.S. Government has been +// granted for itself and others acting on its behalf a paid-up, nonexclusive, irrevocable, +// worldwide license in the Software to reproduce, distribute copies to the public, prepare +// derivative works, and perform publicly and display publicly, and to permit others to do so. +// +// Redistribution and use in source and binary forms, with or without modification, are permitted +// provided that the following conditions are met: +// +// (1) Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// (2) Redistributions in binary form must reproduce the above copyright notice, this list of +// conditions and the following disclaimer in the documentation and/or other materials +// provided with the distribution. +// +// (3) Neither the name of the University of California, Lawrence Berkeley National Laboratory, +// the University of Illinois, U.S. Dept. of Energy nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific prior +// written permission. +// +// (4) Use of EnergyPlus(TM) Name. If Licensee (i) distributes the software in stand-alone form +// without changes from the version obtained under this License, or (ii) Licensee makes a +// reference solely to the software portion of its product, Licensee must refer to the +// software as "EnergyPlus version X" software, where "X" is the version number Licensee +// obtained under this License and may not use a different name for the software. Except as +// specifically required in this Section (4), Licensee shall not use in a company name, a +// product name, in advertising, publicity, or other promotional activities any name, trade +// name, trademark, logo, or other designation of "EnergyPlus", "E+", "e+" or confusingly +// similar designation, without the U.S. Department of Energy's prior written consent. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR +// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +#pragma clang diagnostic push +#pragma ide diagnostic ignored "OCDFAInspection" +#pragma ide diagnostic ignored "cert-err58-cpp" +#pragma ide diagnostic ignored "modernize-use-equals-delete" + +#include + +// Google Test Headers +#include + +// EnergyPlus Headers +#include "Fixtures/EnergyPlusFixture.hh" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace EnergyPlus; +using namespace EnergyPlus::EIRPlantLoopHeatPumps; + +TEST_F(EnergyPlusFixture, ConstructionFullObjectsHeatingAndCooling_WaterSource) +{ + std::string const idf_objects = delimited_string({"HeatPump:PlantLoop:EIR:Heating,", + " hp heating side,", + " node 1,", + " node 2,", + " WaterSource,", + " node 3,", + " node 4,", + " ,", + " ,", + " hp cooling side,", + " 0.001,", + " 0.001,", + " ,", + " 1000,", + " 3.14,", + " 2,", + " dummyCurve,", + " dummyCurve,", + " dummyCurve;", + "HeatPump:PlantLoop:EIR:Cooling,", + " hp cooling side,", + " node 1,", + " node 2,", + " WaterSource,", + " node 3,", + " node 4,", + " ,", + " ,", + " hp heating side,", + " 0.001,", + " 0.001,", + " ,", + " 1000,", + " 3.14,", + " 2,", + " dummyCurve,", + " dummyCurve,", + " dummyCurve;", + "Curve:Linear,", + " dummyCurve,", + " 1,", + " 0,", + " 1,", + " 1;"}); + ASSERT_TRUE(process_idf(idf_objects)); + state->init_state(*state); + + // call the factory with a valid name to trigger reading inputs + EIRPlantLoopHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpEIRHeating, "HP HEATING SIDE"); + + // verify the size of the vector and the processed condition + EXPECT_EQ(2u, state->dataEIRPlantLoopHeatPump->heatPumps.size()); + + // for now we know the order is maintained, so get each heat pump object + EIRPlantLoopHeatPump *thisHeatingPLHP = &state->dataEIRPlantLoopHeatPump->heatPumps[1]; + EIRPlantLoopHeatPump *thisCoolingPLHP = &state->dataEIRPlantLoopHeatPump->heatPumps[0]; + + // validate the heating side + EXPECT_EQ("HP HEATING SIDE", thisHeatingPLHP->name); + EXPECT_ENUM_EQ(DataPlant::PlantEquipmentType::HeatPumpEIRHeating, thisHeatingPLHP->EIRHPType); + EXPECT_EQ(thisCoolingPLHP, thisHeatingPLHP->companionHeatPumpCoil); + EXPECT_EQ(1, thisHeatingPLHP->capFuncTempCurveIndex); + EXPECT_EQ(1, thisHeatingPLHP->powerRatioFuncTempCurveIndex); + EXPECT_EQ(1, thisHeatingPLHP->powerRatioFuncPLRCurveIndex); + + // validate the cooling side + EXPECT_EQ("HP COOLING SIDE", thisCoolingPLHP->name); + EXPECT_ENUM_EQ(DataPlant::PlantEquipmentType::HeatPumpEIRCooling, thisCoolingPLHP->EIRHPType); + EXPECT_EQ(thisHeatingPLHP, thisCoolingPLHP->companionHeatPumpCoil); + EXPECT_EQ(1, thisCoolingPLHP->capFuncTempCurveIndex); + EXPECT_EQ(1, thisCoolingPLHP->powerRatioFuncTempCurveIndex); + EXPECT_EQ(1, thisCoolingPLHP->powerRatioFuncPLRCurveIndex); + + // calling the factory with an invalid name or type will call ShowFatalError, which will trigger a runtime exception + EXPECT_THROW(EIRPlantLoopHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpEIRHeating, "fake"), std::runtime_error); + EXPECT_THROW(EIRPlantLoopHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpEIRCooling, "HP HEATING SIDE"), std::runtime_error); + EXPECT_THROW(EIRPlantLoopHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpEIRCooling, "fake"), std::runtime_error); + EXPECT_THROW(EIRPlantLoopHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpEIRHeating, "HP COOLING SIDE"), std::runtime_error); +} + +TEST_F(EnergyPlusFixture, PairingCompanionCoils) +{ + state->dataEIRPlantLoopHeatPump->heatPumps.resize(2); + EIRPlantLoopHeatPump *coil1 = &state->dataEIRPlantLoopHeatPump->heatPumps[0]; + EIRPlantLoopHeatPump *coil2 = &state->dataEIRPlantLoopHeatPump->heatPumps[1]; + + { + // a successful try + coil1->name = "name1"; + coil1->companionCoilName = "name2"; + coil1->EIRHPType = DataPlant::PlantEquipmentType::HeatPumpEIRCooling; + coil1->companionHeatPumpCoil = nullptr; + coil2->name = "name2"; + coil2->companionCoilName = "name1"; + coil2->EIRHPType = DataPlant::PlantEquipmentType::HeatPumpEIRHeating; + coil2->companionHeatPumpCoil = nullptr; + EIRPlantLoopHeatPumps::EIRPlantLoopHeatPump::pairUpCompanionCoils(*state); + EXPECT_EQ(coil2, coil1->companionHeatPumpCoil); + EXPECT_EQ(coil1, coil2->companionHeatPumpCoil); + } + + { + // but what if we can't find a companion! + coil1->name = "name1"; + coil1->companionCoilName = "name6"; + coil1->EIRHPType = DataPlant::PlantEquipmentType::HeatPumpEIRCooling; + coil1->companionHeatPumpCoil = nullptr; + coil2->name = "name2"; + coil2->companionCoilName = "name1"; + coil2->EIRHPType = DataPlant::PlantEquipmentType::HeatPumpEIRHeating; + coil2->companionHeatPumpCoil = nullptr; + EXPECT_THROW(EIRPlantLoopHeatPumps::EIRPlantLoopHeatPump::pairUpCompanionCoils(*state), std::runtime_error); + } + + { + // or what if we find a companion but it's the same coil type + coil1->name = "name1"; + coil1->companionCoilName = "name2"; + coil1->EIRHPType = DataPlant::PlantEquipmentType::HeatPumpEIRCooling; + coil1->companionHeatPumpCoil = nullptr; + coil2->name = "name2"; + coil2->companionCoilName = "name1"; + coil2->EIRHPType = DataPlant::PlantEquipmentType::HeatPumpEIRCooling; + coil2->companionHeatPumpCoil = nullptr; + EXPECT_THROW(EIRPlantLoopHeatPumps::EIRPlantLoopHeatPump::pairUpCompanionCoils(*state), std::runtime_error); + } +} + +TEST_F(EnergyPlusFixture, HeatingConstructionFullObjectsNoCompanion) +{ + std::string const idf_objects = delimited_string({"HeatPump:PlantLoop:EIR:Heating,", + " hp heating side,", + " node 1,", + " node 2,", + " WaterSource,", + " node 3,", + " node 4,", + " ,", + " ,", + " ,", + " 0.001,", + " 0.001,", + " ,", + " 1000,", + " 3.14,", + " 1,", + " dummyCurve,", + " dummyCurve,", + " dummyCurve;", + "Curve:Linear,", + " dummyCurve,", + " 1,", + " 0,", + " 1,", + " 1;"}); + ASSERT_TRUE(process_idf(idf_objects)); + state->init_state(*state); + + // call the factory with a valid name to trigger reading inputs + EIRPlantLoopHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpEIRHeating, "HP HEATING SIDE"); + + // verify the size of the vector and the processed condition + EXPECT_EQ(1u, state->dataEIRPlantLoopHeatPump->heatPumps.size()); + + // for now we know the order is maintained, so get each heat pump object + EIRPlantLoopHeatPump *thisHeatingPLHP = &state->dataEIRPlantLoopHeatPump->heatPumps[0]; + + // validate the heating side + EXPECT_EQ("HP HEATING SIDE", thisHeatingPLHP->name); + EXPECT_ENUM_EQ(DataPlant::PlantEquipmentType::HeatPumpEIRHeating, thisHeatingPLHP->EIRHPType); + EXPECT_EQ(nullptr, thisHeatingPLHP->companionHeatPumpCoil); + EXPECT_EQ(1, thisHeatingPLHP->capFuncTempCurveIndex); + EXPECT_EQ(1, thisHeatingPLHP->powerRatioFuncTempCurveIndex); + EXPECT_EQ(1, thisHeatingPLHP->powerRatioFuncPLRCurveIndex); + + // calling the factory with an invalid name or type will call ShowFatalError, which will trigger a runtime exception + EXPECT_THROW(EIRPlantLoopHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpEIRHeating, "fake"), std::runtime_error); + EXPECT_THROW(EIRPlantLoopHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpEIRCooling, "HP HEATING SIDE"), std::runtime_error); +} + +TEST_F(EnergyPlusFixture, CoolingConstructionFullObjectsNoCompanion) +{ + std::string const idf_objects = delimited_string({"HeatPump:PlantLoop:EIR:Cooling,", + " hp cooling side,", + " node 1,", + " node 2,", + " WaterSource,", + " node 3,", + " node 4,", + " ,", + " ,", + " ,", + " 0.001,", + " 0.001,", + " ,", + " 1000,", + " 3.14,", + " 1,", + " dummyCurve,", + " dummyCurve,", + " dummyCurve;", + "Curve:Linear,", + " dummyCurve,", + " 1,", + " 0,", + " 1,", + " 1;"}); + ASSERT_TRUE(process_idf(idf_objects)); + state->init_state(*state); + + // call the factory with a valid name to trigger reading inputs + EIRPlantLoopHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpEIRCooling, "HP COOLING SIDE"); + + // verify the size of the vector and the processed condition + EXPECT_EQ(1u, state->dataEIRPlantLoopHeatPump->heatPumps.size()); + + // for now we know the order is maintained, so get each heat pump object + EIRPlantLoopHeatPump *thisCoolingPLHP = &state->dataEIRPlantLoopHeatPump->heatPumps[0]; + + // validate the cooling side + EXPECT_EQ("HP COOLING SIDE", thisCoolingPLHP->name); + EXPECT_ENUM_EQ(DataPlant::PlantEquipmentType::HeatPumpEIRCooling, thisCoolingPLHP->EIRHPType); + EXPECT_EQ(nullptr, thisCoolingPLHP->companionHeatPumpCoil); + EXPECT_EQ(1, thisCoolingPLHP->capFuncTempCurveIndex); + EXPECT_EQ(1, thisCoolingPLHP->powerRatioFuncTempCurveIndex); + EXPECT_EQ(1, thisCoolingPLHP->powerRatioFuncPLRCurveIndex); + + // calling the factory with an invalid name or type will call ShowFatalError, which will trigger a runtime exception + EXPECT_THROW(EIRPlantLoopHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpEIRCooling, "fake"), std::runtime_error); + EXPECT_THROW(EIRPlantLoopHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpEIRHeating, "HP COOLING SIDE"), std::runtime_error); +} + +TEST_F(EnergyPlusFixture, CoolingConstructionFullObjectWithDefaults) +{ + std::string const idf_objects = delimited_string({"HeatPump:PlantLoop:EIR:Cooling,", + " hp cooling side,", + " node 1,", + " node 2,", + " WaterSource,", + " node 3,", + " node 4,", + " ,", + " ,", + " ,", + " 0.001,", + " 0.001,", + " ,", + " 1000,", + " ,", + " ,", + " dummyCurve,", + " dummyCurve,", + " dummyCurve;", + "Curve:Linear,", + " dummyCurve,", + " 1,", + " 0,", + " 1,", + " 1;"}); + ASSERT_TRUE(process_idf(idf_objects)); + state->init_state(*state); + + // call the factory with a valid name to trigger reading inputs + EIRPlantLoopHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpEIRCooling, "HP COOLING SIDE"); + + // verify the size of the vector and the processed condition + EXPECT_EQ(1u, state->dataEIRPlantLoopHeatPump->heatPumps.size()); + + // for now we know the order is maintained, so get each heat pump object + EIRPlantLoopHeatPump *thisCoolingPLHP = &state->dataEIRPlantLoopHeatPump->heatPumps[0]; + + // validate the cooling side + EXPECT_EQ("HP COOLING SIDE", thisCoolingPLHP->name); + EXPECT_ENUM_EQ(DataPlant::PlantEquipmentType::HeatPumpEIRCooling, thisCoolingPLHP->EIRHPType); + EXPECT_NEAR(1, thisCoolingPLHP->sizingFactor, 0.001); +} + +TEST_F(EnergyPlusFixture, CoolingConstructionFullyAutoSized_WaterSource) +{ + std::string const idf_objects = delimited_string({"HeatPump:PlantLoop:EIR:Cooling,", + " hp cooling side,", + " node 1,", + " node 2,", + " WaterSource,", + " node 3,", + " node 4,", + " ,", + " ,", + " ,", + " Autosize,", + " Autosize,", + " ,", + " Autosize,", + " ,", + " 1,", + " dummyCurve,", + " dummyCurve,", + " dummyCurve;", + "Curve:Linear,", + " dummyCurve,", + " 1,", + " 0,", + " 1,", + " 1;"}); + ASSERT_TRUE(process_idf(idf_objects)); + state->init_state(*state); + + // call the factory with a valid name to trigger reading inputs + EIRPlantLoopHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpEIRCooling, "HP COOLING SIDE"); + + // verify the size of the vector and the processed condition + EXPECT_EQ(1u, state->dataEIRPlantLoopHeatPump->heatPumps.size()); + + // for now we know the order is maintained, so get each heat pump object + EIRPlantLoopHeatPump *thisCoolingPLHP = &state->dataEIRPlantLoopHeatPump->heatPumps[0]; + + // validate the cooling side + EXPECT_EQ("HP COOLING SIDE", thisCoolingPLHP->name); + EXPECT_ENUM_EQ(DataPlant::PlantEquipmentType::HeatPumpEIRCooling, thisCoolingPLHP->EIRHPType); + EXPECT_EQ(nullptr, thisCoolingPLHP->companionHeatPumpCoil); + EXPECT_EQ(1, thisCoolingPLHP->capFuncTempCurveIndex); + EXPECT_EQ(1, thisCoolingPLHP->powerRatioFuncTempCurveIndex); + EXPECT_EQ(1, thisCoolingPLHP->powerRatioFuncPLRCurveIndex); + + // calling the factory with an invalid name or type will call ShowFatalError, which will trigger a runtime exception + EXPECT_THROW(EIRPlantLoopHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpEIRCooling, "fake"), std::runtime_error); + EXPECT_THROW(EIRPlantLoopHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpEIRHeating, "HP COOLING SIDE"), std::runtime_error); +} + +TEST_F(EnergyPlusFixture, CatchErrorsOnBadCurves) +{ + std::string const idf_objects = delimited_string({"HeatPump:PlantLoop:EIR:Cooling,", + " hp cooling side,", + " node 1,", + " node 2,", + " WaterSource,", + " node 3,", + " node 4,", + " ,", + " ,", + " ,", + " Autosize,", + " Autosize,", + " ,", + " Autosize,", + " ,", + " 1,", + " dummyCurveA,", + " dummyCurveB,", + " dummyCurveC;"}); + ASSERT_TRUE(process_idf(idf_objects)); + state->init_state(*state); + + // call the factory with a valid name to trigger reading inputs, it should throw for the bad curves + EXPECT_THROW(EIRPlantLoopHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpEIRCooling, "HP COOLING SIDE"), std::runtime_error); +} + +TEST_F(EnergyPlusFixture, HeatingSimulate_AirSource_AWHP) +{ + std::string const idf_objects = delimited_string({"HeatPump:AirToWater,", + "test_AWHP, !- Name", + ", !- Availability Schedule Name Heating", + ", !- Availability Schedule Name Cooling", + "Load , !- Operating Mode Control Method", + "SingleMode, !- Operating Mode Control Option for Multiple Unit", + ", !- Operating Mode Control Schedule Name", + ", !- Minimum Part Load Ratio", + "20 , !- Rated Inlet Air Temperature in Heating Mode", + "1.0, !- Rated Air Flow Rate in Heating Mode", + "50 , !- Rated Leaving Water Temperature in Heating Mode", + "0.0001, !- Rated Water Flow Rate in Heating Mode", + ", !- Minimum Outdoor Air Temperature in Heating Mode", + ", !- Maximum Outdoor Air Temperature in Heating Mode", + ", !- Minimum Leaving Water Temperature Curve Name in Heating Mode", + ", !- Maximum Leaving Water Temperature Curve Name in Heating Mode", + "1.0, !- Sizing Factor for Heating", + "25, !- Rated Inlet Air Temperature in Cooling Mode", + "0.002, !- Rated Air Flow Rate in Cooling Mode", + "22 , !- Rated Leaving Water Temperature in Cooling Mode", + "0.005, !- Rated Water Flow Rate in Cooling Mode", + ", !- Minimum Outdoor Air Temperature in Cooling Mode", + ", !- Maximum Outdoor Air Temperature in Cooling Mode", + ", !- Minimum Leaving Water Temperature Curve Name in Cooling Mode", + ", !- Maximum Leaving Water Temperature Curve Name in Cooling Mode", + "0.9, !- Sizing Factor for Cooling", + "Outdoor Air Inlet Node , !- Air Inlet Node Name", + "Outdoor Air Outlet Node, !- Air Outlet Node Name", + "Heating Coil Load Loop Intermediate Node, !- Hot Water Inlet Node Name", + "Heating Coil Load Loop Supply Outlet Node, !- Hot Water Outlet Node Name", + "Cooling Coil Load Loop Intermediate Node , !- Chilled Water Inlet Node Name", + "Cooling Coil Load Loop Supply Outlet Node , !- Chilled Water Outlet Node Name", + ", !- Maximum Outdoor Dry Bulb Temperature For Defrost Operation", + ", !- Heat Pump Defrost Control", + ", !- Defrost Time Period Fraction", + ", !- Resistive Defrost Heater Capacity", + ", !- Defrost Energy Input Ratio Function of Temperature Curve Name", + "1 , !- Compressor Multiplier", + "FixedSpeed , !- Control Type", + "100 , !- Crankcase Heater Capacity", + "EIRCurveFuncPLR, !- Crankcase Heater Capacity Function of Temperature Curve Name", + "20 , !- Maximum Ambient Temperature for Crankcase Heater Operation", + "1 , !- Number of Speeds for Heating", + "1000, !- Rated Heating Capacity at Speed 1", + "3.14, !- Rated COP for Heating at Speed 1", + "dummyCurve, !- Normalized Heating Capacity Function of Temperature Curve Name at Speed 1", + "dummyCurve, !- Heating Energy Input Ratio Function of Temperature Curve Name at Speed 1", + "dummyCurve, !- Heating Energy Input Ratio Function of PLR Curve Name at Speed 1", + ", !- Rated Heating Capacity at Speed 2", + ", !- Rated COP for Heating at Speed 2", + ", !- Normalized Heating Capacity Function of Temperature Curve Name at Speed 2", + ", !- Heating Energy Input Ratio Function of Temperature Curve Name at Speed 2", + ", !- Heating Energy Input Ratio Function of PLR Curve Name at Speed 2", + ", !- Rated Heating Capacity at Speed 3", + ", !- Rated COP for Heating at Speed 3", + ", !- Normalized Heating Capacity Function of Temperature Curve Name at Speed 3", + ", !- Heating Energy Input Ratio Function of Temperature Curve Name at Speed 3", + ", !- Heating Energy Input Ratio Function of PLR Curve Name at Speed 3", + ", !- Rated Heating Capacity at Speed 4", + ", !- Rated COP for Heating at Speed 4", + ", !- Normalized Heating Capacity Function of Temperature Curve Name at Speed 4", + ", !- Heating Energy Input Ratio Function of Temperature Curve Name at Speed 4", + ", !- Heating Energy Input Ratio Function of PLR Curve Name at Speed 4", + ", !- Rated Heating Capacity at Speed 5", + ", !- Rated COP for Heating at Speed 5", + ", !- Normalized Heating Capacity Function of Temperature Curve Name at Speed 5", + ", !- Heating Energy Input Ratio Function of Temperature Curve Name at Speed 5", + ", !- Heating Energy Input Ratio Function of PLR Curve Name at Speed 5", + ", !- Booster Mode On Heating", + ", !- Rated Heating Capacity in Booster Mode", + ", !- Rated Heating COP in Booster Mode", + ", !- Normalized Heating Capacity Function of Temperature Curve Name in Booster Mode", + ", !- Heating Energy Input Ratio Function of Temperature Curve Name in Booster Mode", + ", !- Heating Energy Input Ratio Function of PLR Curve Name in Booster Mode", + "1, !- Number of Speeds for Cooling", + "20000, !- Rated Cooling Capacity at Speed 1", + "3, !- Rated COP for Cooling at Speed 1", + "dummyCurve, !- Normalized Cooling Capacity Function of Temperature Curve Name at Speed 1", + "dummyCurve, !- Cooling Energy Input Ratio Function of Temperature Curve Name at Speed 1", + "dummyCurve, !- Cooling Energy Input Ratio Function of PLR Curve Name at Speed 1", + ", !- Rated Cooling Capacity at Speed 2", + ", !- Rated COP for Cooling at Speed 2", + ", !- Normalized Cooling Capacity Function of Temperature Curve Name at Speed 2", + ", !- Cooling Energy Input Ratio Function of Temperature Curve Name at Speed 2", + ", !- Cooling Energy Input Ratio Function of PLR Curve Name at Speed 2", + ", !- Rated Cooling Capacity at Speed 3", + ", !- Rated COP for Cooling at Speed 3", + ", !- Normalized Cooling Capacity Function of Temperature Curve Name at Speed 3", + ", !- Cooling Energy Input Ratio Function of Temperature Curve Name at Speed 3", + ", !- Cooling Energy Input Ratio Function of PLR Curve Name at Speed 3", + ", !- Rated Cooling Capacity at Speed 4", + ", !- Rated COP for Cooling at Speed 4", + ", !- Normalized Cooling Capacity Function of Temperature Curve Name at Speed 4", + ", !- Cooling Energy Input Ratio Function of Temperature Curve Name at Speed 4", + ", !- Cooling Energy Input Ratio Function of PLR Curve Name at Speed 4", + ", !- Rated Cooling Capacity at Speed 5", + ", !- Rated COP for Cooling at Speed 5", + ", !- Normalized Cooling Capacity Function of Temperature Curve Name at Speed 5", + ", !- Cooling Energy Input Ratio Function of Temperature Curve Name at Speed 5", + ", !- Cooling Energy Input Ratio Function of PLR Curve Name at Speed 5", + ", !- Booster Mode On Cooling", + ", !- Rated Cooling Capacity in Booster Mode", + ", !- Rated Cooling COP in Booster Mode", + ", !- Normalized Cooling Capacity Function of Temperature Curve Name in Booster Mode", + ", !- Cooling Energy Input Ratio Function of Temperature Curve Name in Booster Mode", + "; !- Cooling Energy Input Ratio Function of PLR Curve Name in Booster Mode", + + "Curve:Linear,", + " dummyCurve,", + " 0.95,", + " 0,", + " 1,", + " 1;"}); + ASSERT_TRUE(process_idf(idf_objects)); + state->init_state(*state); + + // set up the plant loops + // first the load side + state->dataPlnt->TotNumLoops = 2; + state->dataPlnt->PlantLoop.allocate(state->dataPlnt->TotNumLoops); + + state->dataPlnt->PlantLoop(1).FluidName = "WATER"; + state->dataPlnt->PlantLoop(1).glycol = Fluid::GetWater(*state); + state->dataPlnt->PlantLoop(1).LoopDemandCalcScheme = DataPlant::LoopDemandCalcScheme::SingleSetPoint; + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).TotalBranches = 1; + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch.allocate(1); + 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::HeatPumpAirToWaterHeating; + PLHPPlantLoadSideCompHeating.CurOpSchemeType = DataPlant::OpScheme::CompSetPtBased; + + state->dataPlnt->PlantLoop(2).FluidName = "WATER"; + state->dataPlnt->PlantLoop(2).glycol = Fluid::GetWater(*state); + state->dataPlnt->PlantLoop(2).LoopDemandCalcScheme = DataPlant::LoopDemandCalcScheme::SingleSetPoint; + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Supply).TotalBranches = 1; + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Supply).Branch.allocate(1); + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).TotalComponents = 1; + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp.allocate(1); + auto &PLHPPlantLoadSideCompCooling = state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp(1); + PLHPPlantLoadSideCompCooling.Type = DataPlant::PlantEquipmentType::HeatPumpAirToWaterCooling; + PLHPPlantLoadSideCompCooling.CurOpSchemeType = DataPlant::OpScheme::CompSetPtBased; + // the init call expects a "from" calling point + PlantLocation myLoadLocation = PlantLocation(1, DataPlant::LoopSideLocation::Supply, 1, 1); + + // call the factory with a valid name to trigger reading inputs + DataPlant::PlantEquipmentType equipType = DataPlant::PlantEquipmentType::HeatPumpAirToWaterHeating; + HeatPumpAirToWater::factory(*state, equipType, "TEST_AWHP"); + + // verify the size of the vector and the processed condition + EXPECT_EQ(2u, state->dataHeatPumpAirToWater->heatPumps.size()); + + // for now we know the order is maintained, so get each heat pump object + HeatPumpAirToWater *thisAWHPHeating = &state->dataHeatPumpAirToWater->heatPumps[1]; + HeatPumpAirToWater *thisAWHPCooling = &state->dataHeatPumpAirToWater->heatPumps[0]; + + // do a bit of extra wiring up to the plant + PLHPPlantLoadSideCompHeating.Name = thisAWHPHeating->name; + PLHPPlantLoadSideCompHeating.NodeNumIn = thisAWHPHeating->loadSideNodes.inlet; + PLHPPlantLoadSideCompCooling.Name = thisAWHPCooling->name; + PLHPPlantLoadSideCompCooling.NodeNumIn = thisAWHPCooling->loadSideNodes.inlet; + + // call for all initialization + state->dataGlobal->BeginEnvrnFlag = true; + state->dataPlnt->PlantFirstSizesOkayToFinalize = true; + thisAWHPHeating->onInitLoopEquip(*state, myLoadLocation); + thisAWHPHeating->loadSidePlantLoc.loopNum = 1; + thisAWHPHeating->loadSidePlantLoc.loopSideNum = EnergyPlus::DataPlant::LoopSideLocation::Supply; + thisAWHPHeating->loadSidePlantLoc.branchNum = 1; + thisAWHPHeating->loadSidePlantLoc.compNum = 1; + thisAWHPCooling->loadSidePlantLoc.loopNum = 2; + thisAWHPCooling->loadSidePlantLoc.loopSideNum = EnergyPlus::DataPlant::LoopSideLocation::Supply; + thisAWHPCooling->loadSidePlantLoc.branchNum = 1; + thisAWHPCooling->loadSidePlantLoc.compNum = 1; + + thisAWHPHeating->setPointNodeNum = thisAWHPHeating->loadSideNodes.outlet; + + // call it from the load side, but this time there is a negative (cooling) load - shouldn't try to run + { + bool firstHVAC = true; + Real64 curLoad = -900; + bool runFlag = true; // plant actually shouldn't do this but the component can be smart enough to handle it + Real64 constexpr specifiedLoadSetpoint = 45; + Real64 constexpr loadInletTemp = 46; + state->dataLoopNodes->Node(thisAWHPHeating->setPointNodeNum).TempSetPoint = specifiedLoadSetpoint; + state->dataLoopNodes->Node(thisAWHPHeating->loadSideNodes.inlet).Temp = loadInletTemp; + state->dataLoopNodes->Node(thisAWHPHeating->sourceSideNodes.inlet).Temp = 30; + thisAWHPHeating->simulate(*state, myLoadLocation, firstHVAC, curLoad, runFlag); + // expect it to meet setpoint and have some pre-evaluated conditions + EXPECT_NEAR(loadInletTemp, thisAWHPHeating->loadSideOutletTemp, 0.001); + EXPECT_NEAR(0.0, thisAWHPHeating->loadSideHeatTransfer, 0.001); + } + + // call it from the load side, but this time there is load (still firsthvac, unit can meet load) + { + bool firstHVAC = true; + Real64 curLoad = 800; + bool runFlag = true; + Real64 constexpr expectedLoadMassFlowRate = 0.09999; + Real64 constexpr expectedCp = 4180; + Real64 constexpr specifiedLoadSetpoint = 45; + Real64 const calculatedLoadInletTemp = specifiedLoadSetpoint - curLoad / (expectedLoadMassFlowRate * expectedCp); + state->dataLoopNodes->Node(thisAWHPHeating->setPointNodeNum).TempSetPoint = specifiedLoadSetpoint; + state->dataLoopNodes->Node(thisAWHPHeating->loadSideNodes.inlet).Temp = calculatedLoadInletTemp; + state->dataLoopNodes->Node(thisAWHPHeating->sourceSideNodes.inlet).Temp = 30; + // if need to test defrost, uncomment this + // state->dataEnvrn->OutBaroPress = 98934; + thisAWHPHeating->simulate(*state, myLoadLocation, firstHVAC, curLoad, runFlag); + // expect it to meet setpoint and have some pre-evaluated conditions + EXPECT_NEAR(specifiedLoadSetpoint, thisAWHPHeating->loadSideOutletTemp, 0.001); + EXPECT_NEAR(curLoad, thisAWHPHeating->loadSideHeatTransfer, 0.001); + } + + // now we can call it again from the load side, but this time there is load (still firsthvac, unit cannot meet load) + { + bool firstHVAC = true; + Real64 curLoad = 1200; + Real64 availableCapacity = 950.0; + bool runFlag = true; + Real64 constexpr expectedLoadMassFlowRate = 0.09999; + Real64 constexpr expectedCp = 4180; + Real64 constexpr specifiedLoadSetpoint = 45; + Real64 const calculatedLoadInletTemp = specifiedLoadSetpoint - curLoad / (expectedLoadMassFlowRate * expectedCp); + state->dataLoopNodes->Node(thisAWHPHeating->setPointNodeNum).TempSetPoint = specifiedLoadSetpoint; + state->dataLoopNodes->Node(thisAWHPHeating->loadSideNodes.inlet).Temp = calculatedLoadInletTemp; + state->dataLoopNodes->Node(thisAWHPHeating->sourceSideNodes.inlet).Temp = 30; + thisAWHPHeating->simulate(*state, myLoadLocation, firstHVAC, curLoad, runFlag); + // expect it to miss setpoint and be at max capacity + EXPECT_NEAR(44.402, thisAWHPHeating->loadSideOutletTemp, 0.001); + EXPECT_NEAR(availableCapacity, thisAWHPHeating->loadSideHeatTransfer, 0.001); + } + + // now we can call it again from the load side, but this time there is no load (still firsthvac) + { + bool firstHVAC = true; + Real64 curLoad = 0.0; + bool runFlag = true; + Real64 constexpr expectedLoadMassFlowRate = 0.09999; + Real64 constexpr expectedCp = 4180; + Real64 constexpr specifiedLoadSetpoint = 45; + Real64 const calculatedLoadInletTemp = specifiedLoadSetpoint - curLoad / (expectedLoadMassFlowRate * expectedCp); + state->dataLoopNodes->Node(thisAWHPHeating->setPointNodeNum).TempSetPoint = specifiedLoadSetpoint; + state->dataLoopNodes->Node(thisAWHPHeating->loadSideNodes.inlet).Temp = calculatedLoadInletTemp; + state->dataLoopNodes->Node(thisAWHPHeating->sourceSideNodes.inlet).Temp = 30; + thisAWHPHeating->simulate(*state, myLoadLocation, firstHVAC, curLoad, runFlag); + // expect it to miss setpoint and be at max capacity + EXPECT_NEAR(45.0, thisAWHPHeating->loadSideOutletTemp, 0.001); + EXPECT_NEAR(30.0, thisAWHPHeating->sourceSideOutletTemp, 0.001); + } +} + +TEST_F(EnergyPlusFixture, processInputForEIRPLHP_AWHP) +{ + std::string const idf_objects = delimited_string({ + "HeatPump:AirToWater,", + "test_AWHP, !- Name", + ", !- Availability Schedule Name Heating", + ", !- Availability Schedule Name Cooling", + "Load , !- Operating Mode Control Method", + "SingleMode, !- Operating Mode Control Option for Multiple Unit", + ", !- Operating Mode Control Schedule Name", + ", !- Minimum Part Load Ratio", + "20 , !- Rated Inlet Air Temperature in Heating Mode", + "0.1 , !- Rated Air Flow Rate in Heating Mode", + "50 , !- Rated Leaving Water Temperature in Heating Mode", + "0.02 , !- Rated Water Flow Rate in Heating Mode", + "-20, !- Minimum Outdoor Air Temperature in Heating Mode", + "25, !- Maximum Outdoor Air Temperature in Heating Mode", + "MinLWTvsOAT, !- Minimum Leaving Water Temperature Curve Name in Heating Mode", + "MaxLWTvsOAT, !- Maximum Leaving Water Temperature Curve Name in Heating Mode", + "1.0, !- Sizing Factor for Heating", + "25, !- Rated Inlet Air Temperature in Cooling Mode", + "0.1 , !- Rated Air Flow Rate in Cooling Mode", + "22 , !- Rated Leaving Water Temperature in Cooling Mode", + "0.05 , !- Rated Water Flow Rate in Cooling Mode", + "18 , !- Minimum Outdoor Air Temperature in Cooling Mode", + "40, !- Maximum Outdoor Air Temperature in Cooling Mode", + "MinLWTvsOAT, !- Minimum Leaving Water Temperature Curve Name in Cooling Mode", + "MaxLWTvsOAT, !- Maximum Leaving Water Temperature Curve Name in Cooling Mode", + "0.9, !- Sizing Factor for Cooling", + "Outdoor Air Inlet Node , !- Air Inlet Node Name", + "Outdoor Air Outlet Node, !- Air Outlet Node Name", + "Heating Coil Load Loop Intermediate Node, !- Hot Water Inlet Node Name", + "Heating Coil Load Loop Supply Outlet Node, !- Hot Water Outlet Node Name", + "Cooling Coil Load Loop Intermediate Node , !- Chilled Water Inlet Node Name", + "Cooling Coil Load Loop Supply Outlet Node , !- Chilled Water Outlet Node Name", + "10, !- Maximum Outdoor Dry Bulb Temperature For Defrost Operation", + "Timed, !- Heat Pump Defrost Control", + "0.2, !- Defrost Time Period Fraction", + "150, !- Resistive Defrost Heater Capacity", + ", !- Defrost Energy Input Ratio Function of Temperature Curve Name", + "1 , !- Compressor Multiplier", + "FixedSpeed , !- Control Type", + "100 , !- Crankcase Heater Capacity", + "EIRCurveFuncPLR, !- Crankcase Heater Capacity Function of Temperature Curve Name", + "20 , !- Maximum Ambient Temperature for Crankcase Heater Operation", + "2 , !- Number of Speeds for Heating", + "100 , !- Rated Heating Capacity at Speed 1", + "3, !- Rated COP for Heating at Speed 1", + "CapCurveFuncTemp, !- Normalized Heating Capacity Function of Temperature Curve Name at Speed 1", + "EIRCurveFuncTemp, !- Heating Energy Input Ratio Function of Temperature Curve Name at Speed 1", + "EIRCurveFuncPLR, !- Heating Energy Input Ratio Function of PLR Curve Name at Speed 1", + "200, !- Rated Heating Capacity at Speed 2", + "3.5, !- Rated COP for Heating at Speed 2", + "CapCurveFuncTemp, !- Normalized Heating Capacity Function of Temperature Curve Name at Speed 2", + "EIRCurveFuncTemp, !- Heating Energy Input Ratio Function of Temperature Curve Name at Speed 2", + "EIRCurveFuncPLR, !- Heating Energy Input Ratio Function of PLR Curve Name at Speed 2", + ", !- Rated Heating Capacity at Speed 3", + ", !- Rated COP for Heating at Speed 3", + ", !- Normalized Heating Capacity Function of Temperature Curve Name at Speed 3", + ", !- Heating Energy Input Ratio Function of Temperature Curve Name at Speed 3", + ", !- Heating Energy Input Ratio Function of PLR Curve Name at Speed 3", + ", !- Rated Heating Capacity at Speed 4", + ", !- Rated COP for Heating at Speed 4", + ", !- Normalized Heating Capacity Function of Temperature Curve Name at Speed 4", + ", !- Heating Energy Input Ratio Function of Temperature Curve Name at Speed 4", + ", !- Heating Energy Input Ratio Function of PLR Curve Name at Speed 4", + ", !- Rated Heating Capacity at Speed 5", + ", !- Rated COP for Heating at Speed 5", + ", !- Normalized Heating Capacity Function of Temperature Curve Name at Speed 5", + ", !- Heating Energy Input Ratio Function of Temperature Curve Name at Speed 5", + ", !- Heating Energy Input Ratio Function of PLR Curve Name at Speed 5", + "Yes, !- Booster Mode On Heating", + "50000, !- Rated Heating Capacity in Booster Mode", + "2.5, !- Rated Heating COP in Booster Mode", + "CapCurveFuncTemp, !- Normalized Heating Capacity Function of Temperature Curve Name in Booster Mode", + "EIRCurveFuncTemp, !- Heating Energy Input Ratio Function of Temperature Curve Name in Booster Mode", + "EIRCurveFuncPLR, !- Heating Energy Input Ratio Function of PLR Curve Name in Booster Mode", + "2, !- Number of Speeds for Cooling", + "120 , !- Rated Cooling Capacity at Speed 1", + "4, !- Rated COP for Cooling at Speed 1", + "CapCurveFuncTemp, !- Normalized Cooling Capacity Function of Temperature Curve Name at Speed 1", + "EIRCurveFuncTemp, !- Cooling Energy Input Ratio Function of Temperature Curve Name at Speed 1", + "EIRCurveFuncPLR, !- Cooling Energy Input Ratio Function of PLR Curve Name at Speed 1", + "240, !- Rated Cooling Capacity at Speed 2", + "3.5, !- Rated COP for Cooling at Speed 2", + "CapCurveFuncTemp, !- Normalized Cooling Capacity Function of Temperature Curve Name at Speed 2", + "EIRCurveFuncTemp, !- Cooling Energy Input Ratio Function of Temperature Curve Name at Speed 2", + "EIRCurveFuncPLR, !- Cooling Energy Input Ratio Function of PLR Curve Name at Speed 2", + ", !- Rated Cooling Capacity at Speed 3", + ", !- Rated COP for Cooling at Speed 3", + ", !- Normalized Cooling Capacity Function of Temperature Curve Name at Speed 3", + ", !- Cooling Energy Input Ratio Function of Temperature Curve Name at Speed 3", + ", !- Cooling Energy Input Ratio Function of PLR Curve Name at Speed 3", + ", !- Rated Cooling Capacity at Speed 4", + ", !- Rated COP for Cooling at Speed 4", + ", !- Normalized Cooling Capacity Function of Temperature Curve Name at Speed 4", + ", !- Cooling Energy Input Ratio Function of Temperature Curve Name at Speed 4", + ", !- Cooling Energy Input Ratio Function of PLR Curve Name at Speed 4", + ", !- Rated Cooling Capacity at Speed 5", + ", !- Rated COP for Cooling at Speed 5", + ", !- Normalized Cooling Capacity Function of Temperature Curve Name at Speed 5", + ", !- Cooling Energy Input Ratio Function of Temperature Curve Name at Speed 5", + ", !- Cooling Energy Input Ratio Function of PLR Curve Name at Speed 5", + "Yes, !- Booster Mode On Cooling", + "500, !- Rated Cooling Capacity in Booster Mode", + "2.0, !- Rated Cooling COP in Booster Mode", + "CapCurveFuncTemp, !- Normalized Cooling Capacity Function of Temperature Curve Name in Booster Mode", + "EIRCurveFuncTemp, !- Cooling Energy Input Ratio Function of Temperature Curve Name in Booster Mode", + "EIRCurveFuncPLR; !- Cooling Energy Input Ratio Function of PLR Curve Name in Booster Mode", + + "HeatPump:AirToWater,", + "test_AWHP_defaults, !- Name", + ", !- Availability Schedule Name Heating", + ", !- Availability Schedule Name Cooling", + ", !- Operating Mode Control Method", + ", !- Operating Mode Control Option for Multiple Unit", + ", !- Operating Mode Control Schedule Name", + ", !- Minimum Part Load Ratio", + ", !- Rated Inlet Air Temperature in Heating Mode", + ", !- Rated Air Flow Rate in Heating Mode", + ", !- Rated Leaving Water Temperature in Heating Mode", + ", !- Rated Water Flow Rate in Heating Mode", + ", !- Minimum Outdoor Air Temperature in Heating Mode", + ", !- Maximum Outdoor Air Temperature in Heating Mode", + ", !- Minimum Leaving Water Temperature Curve Name in Heating Mode", + ", !- Maximum Leaving Water Temperature Curve Name in Heating Mode", + ", !- Sizing Factor for Heating", + ", !- Rated Inlet Air Temperature in Cooling Mode", + ", !- Rated Air Flow Rate in Cooling Mode", + ", !- Rated Leaving Water Temperature in Cooling Mode", + ", !- Rated Water Flow Rate in Cooling Mode", + ", !- Minimum Outdoor Air Temperature in Cooling Mode", + ", !- Maximum Outdoor Air Temperature in Cooling Mode", + ", !- Minimum Leaving Water Temperature Curve Name in Cooling Mode", + ", !- Maximum Leaving Water Temperature Curve Name in Cooling Mode", + ", !- Sizing Factor for Cooling", + "Outdoor Air Inlet Node , !- Air Inlet Node Name", + "Outdoor Air Outlet Node, !- Air Outlet Node Name", + "Heating Coil Load Loop Intermediate Node, !- Hot Water Inlet Node Name", + "Heating Coil Load Loop Supply Outlet Node, !- Hot Water Outlet Node Name", + "Cooling Coil Load Loop Intermediate Node , !- Chilled Water Inlet Node Name", + "Cooling Coil Load Loop Supply Outlet Node , !- Chilled Water Outlet Node Name", + ", !- Maximum Outdoor Dry Bulb Temperature For Defrost Operation", + ", !- Heat Pump Defrost Control", + ", !- Defrost Time Period Fraction", + ", !- Resistive Defrost Heater Capacity", + ", !- Defrost Energy Input Ratio Function of Temperature Curve Name", + ", !- Compressor Multiplier", + ", !- Control Type", + ", !- Crankcase Heater Capacity", + ", !- Crankcase Heater Capacity Function of Temperature Curve Name", + ", !- Maximum Ambient Temperature for Crankcase Heater Operation", + ", !- Number of Speeds for Heating", + ", !- Rated Heating Capacity at Speed 1", + ", !- Rated COP for Heating at Speed 1", + "CapCurveFuncTemp, !- Normalized Heating Capacity Function of Temperature Curve Name at Speed 1", + "EIRCurveFuncTemp, !- Heating Energy Input Ratio Function of Temperature Curve Name at Speed 1", + "EIRCurveFuncPLR, !- Heating Energy Input Ratio Function of PLR Curve Name at Speed 1", + ", !- Rated Heating Capacity at Speed 2", + ", !- Rated COP for Heating at Speed 2", + ", !- Normalized Heating Capacity Function of Temperature Curve Name at Speed 2", + ", !- Heating Energy Input Ratio Function of Temperature Curve Name at Speed 2", + ", !- Heating Energy Input Ratio Function of PLR Curve Name at Speed 2", + ", !- Rated Heating Capacity at Speed 3", + ", !- Rated COP for Heating at Speed 3", + ", !- Normalized Heating Capacity Function of Temperature Curve Name at Speed 3", + ", !- Heating Energy Input Ratio Function of Temperature Curve Name at Speed 3", + ", !- Heating Energy Input Ratio Function of PLR Curve Name at Speed 3", + ", !- Rated Heating Capacity at Speed 4", + ", !- Rated COP for Heating at Speed 4", + ", !- Normalized Heating Capacity Function of Temperature Curve Name at Speed 4", + ", !- Heating Energy Input Ratio Function of Temperature Curve Name at Speed 4", + ", !- Heating Energy Input Ratio Function of PLR Curve Name at Speed 4", + ", !- Rated Heating Capacity at Speed 5", + ", !- Rated COP for Heating at Speed 5", + ", !- Normalized Heating Capacity Function of Temperature Curve Name at Speed 5", + ", !- Heating Energy Input Ratio Function of Temperature Curve Name at Speed 5", + ", !- Heating Energy Input Ratio Function of PLR Curve Name at Speed 5", + ", !- Booster Mode On Heating", + ", !- Rated Heating Capacity in Booster Mode", + ", !- Rated Heating COP in Booster Mode", + ", !- Normalized Heating Capacity Function of Temperature Curve Name in Booster Mode", + ", !- Heating Energy Input Ratio Function of Temperature Curve Name in Booster Mode", + ", !- Heating Energy Input Ratio Function of PLR Curve Name in Booster Mode", + ", !- Number of Speeds for Cooling", + ", !- Rated Cooling Capacity at Speed 1", + ", !- Rated COP for Cooling at Speed 1", + "CapCurveFuncTemp, !- Normalized Cooling Capacity Function of Temperature Curve Name at Speed 1", + "EIRCurveFuncTemp, !- Cooling Energy Input Ratio Function of Temperature Curve Name at Speed 1", + "EIRCurveFuncPLR, !- Cooling Energy Input Ratio Function of PLR Curve Name at Speed 1", + ", !- Rated Cooling Capacity at Speed 2", + ", !- Rated COP for Cooling at Speed 2", + ", !- Normalized Cooling Capacity Function of Temperature Curve Name at Speed 2", + ", !- Cooling Energy Input Ratio Function of Temperature Curve Name at Speed 2", + ", !- Cooling Energy Input Ratio Function of PLR Curve Name at Speed 2", + ", !- Rated Cooling Capacity at Speed 3", + ", !- Rated COP for Cooling at Speed 3", + ", !- Normalized Cooling Capacity Function of Temperature Curve Name at Speed 3", + ", !- Cooling Energy Input Ratio Function of Temperature Curve Name at Speed 3", + ", !- Cooling Energy Input Ratio Function of PLR Curve Name at Speed 3", + ", !- Rated Cooling Capacity at Speed 4", + ", !- Rated COP for Cooling at Speed 4", + ", !- Normalized Cooling Capacity Function of Temperature Curve Name at Speed 4", + ", !- Cooling Energy Input Ratio Function of Temperature Curve Name at Speed 4", + ", !- Cooling Energy Input Ratio Function of PLR Curve Name at Speed 4", + ", !- Rated Cooling Capacity at Speed 5", + ", !- Rated COP for Cooling at Speed 5", + ", !- Normalized Cooling Capacity Function of Temperature Curve Name at Speed 5", + ", !- Cooling Energy Input Ratio Function of Temperature Curve Name at Speed 5", + ", !- Cooling Energy Input Ratio Function of PLR Curve Name at Speed 5", + ", !- Booster Mode On Cooling", + ", !- Rated Cooling Capacity in Booster Mode", + ", !- Rated Cooling COP in Booster Mode", + ", !- Normalized Cooling Capacity Function of Temperature Curve Name in Booster Mode", + ", !- Cooling Energy Input Ratio Function of Temperature Curve Name in Booster Mode", + "; !- Cooling Energy Input Ratio Function of PLR Curve Name in Booster Mode", + + "Curve:Quadratic,", + " MinLWTvsOAT, !- Name", + " 0.0, !- Coefficient1 Constant", + " 1.0, !- Coefficient2 x", + " 0.0, !- Coefficient3 x**2", + " -17.77778, !- Minimum Value of x", + " 35.0, !- Maximum Value of x", + " 20.0, !- Minimum Curve Output", + " 35.0, !- Maximum Curve Output", + " Temperature, !- Input Unit Type for X", + " Temperature; !- Output Unit Type", + + "Curve:Quadratic,", + " MaxLWTvsOAT, !- Name", + " 53.1666666666667, !- Coefficient1 Constant", + " 0.85, !- Coefficient2 x", + " 0.0, !- Coefficient3 x**2", + " -17.777778, !- Minimum Value of x", + " 35.0, !- Maximum Value of x", + " 20.0, !- Minimum Curve Output", + " 60.0, !- Maximum Curve Output", + " Temperature, !- Input Unit Type for X", + " Temperature; !- Output Unit Type", + + "Curve:Biquadratic,", + "CapCurveFuncTemp, !- Name", + "1.0, !- Coefficient1 Constant", + "0.0, !- Coefficient2 x", + "0.0, !- Coefficient3 x**2", + "0.0, !- Coefficient4 y", + "0.0, !- Coefficient5 y**2", + "0.0, !- Coefficient6 x*y", + "5.0, !- Minimum Value of x", + "10.0, !- Maximum Value of x", + "24.0, !- Minimum Value of y", + "35.0, !- Maximum Value of y", + ", !- Minimum Curve Output", + ", !- Maximum Curve Output", + "Temperature, !- Input Unit Type for X", + "Temperature, !- Input Unit Type for Y", + "Dimensionless; !- Output Unit Type", + + "Curve:Biquadratic,", + "EIRCurveFuncTemp, !- Name", + "1.0, !- Coefficient1 Constant", + "0.0, !- Coefficient2 x", + "0.0, !- Coefficient3 x**2", + "0.0, !- Coefficient4 y", + "0.0, !- Coefficient5 y**2", + "0.0, !- Coefficient6 x*y", + "5.0, !- Minimum Value of x", + "10.0, !- Maximum Value of x", + "24.0, !- Minimum Value of y", + "35.0, !- Maximum Value of y", + ", !- Minimum Curve Output", + ", !- Maximum Curve Output", + "Temperature, !- Input Unit Type for X", + "Temperature, !- Input Unit Type for Y", + "Dimensionless; !- Output Unit Type", + + "Curve:Quadratic,", + "EIRCurveFuncPLR, !- Name", + "1.0, !- Coefficient1 Constant", + "0.0, !- Coefficient2 x", + "0.0, !- Coefficient3 x**2", + "0.0, !- Minimum Value of x", + "1.0; !- Maximum Value of x", + }); + ASSERT_TRUE(process_idf(idf_objects)); + state->init_state(*state); + + DataPlant::PlantEquipmentType equipType = DataPlant::PlantEquipmentType::HeatPumpAirToWaterHeating; + HeatPumpAirToWater::factory(*state, equipType, "test_AWHP_defaults"); + equipType = DataPlant::PlantEquipmentType::HeatPumpAirToWaterCooling; + HeatPumpAirToWater::factory(*state, equipType, "test_AWHP_defaults"); + // cooling component in the AWHP + EXPECT_ENUM_EQ(state->dataHeatPumpAirToWater->heatPumps[0].EIRHPType, DataPlant::PlantEquipmentType::HeatPumpAirToWaterCooling); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[0].availSchedName, ""); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[0].availSched, Sched::GetScheduleAlwaysOn(*state)); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[0].operationModeControlSche, nullptr); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[0].sourceSideDesignInletTemp, 25); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[0].sourceSideDesignVolFlowRate, 0.1); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[0].ratedLeavingWaterTemperature, 22); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[0].loadSideDesignVolFlowRate, 0.05); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[0].minSourceTempLimit, 18); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[0].maxSourceTempLimit, 40); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[0].minSupplyWaterTempCurveIndex, 3); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[0].maxSupplyWaterTempCurveIndex, 4); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[0].sizingFactor, 0.9); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[0].loadSideNodes.inlet, 1); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[0].loadSideNodes.outlet, 2); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[0].sourceSideNodes.inlet, 3); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[0].sourceSideNodes.outlet, 4); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[0].heatPumpMultiplier, 1); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[0].controlType, HeatPumpAirToWater::CompressorControlType::FixedSpeed); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[0].boosterOn, true); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[0].numSpeeds, 3); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[0].referenceCapacity, 500); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[0].ratedCapacity[0], 120); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[0].ratedCOP[0], 4); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[0].capFuncTempCurveIndex[0], Curve::GetCurveIndex(*state, "CAPCURVEFUNCTEMP")); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[0].powerRatioFuncTempCurveIndex[0], Curve::GetCurveIndex(*state, "EIRCURVEFUNCTEMP")); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[0].powerRatioFuncPLRCurveIndex[0], Curve::GetCurveIndex(*state, "EIRCURVEFUNCPLR")); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[0].ratedCapacity[1], 240); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[0].ratedCOP[1], 3.5); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[0].capFuncTempCurveIndex[1], Curve::GetCurveIndex(*state, "CAPCURVEFUNCTEMP")); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[0].powerRatioFuncTempCurveIndex[1], Curve::GetCurveIndex(*state, "EIRCURVEFUNCTEMP")); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[0].powerRatioFuncPLRCurveIndex[1], Curve::GetCurveIndex(*state, "EIRCURVEFUNCPLR")); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[0].ratedCapacity[2], 500); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[0].ratedCOP[2], 2.0); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[0].capFuncTempCurveIndex[2], Curve::GetCurveIndex(*state, "CAPCURVEFUNCTEMP")); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[0].powerRatioFuncTempCurveIndex[2], Curve::GetCurveIndex(*state, "EIRCURVEFUNCTEMP")); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[0].powerRatioFuncPLRCurveIndex[2], Curve::GetCurveIndex(*state, "EIRCURVEFUNCPLR")); + EXPECT_ENUM_EQ(state->dataHeatPumpAirToWater->heatPumps[0].operatingModeControlMethod, HeatPumpAirToWater::OperatingModeControlMethod::Load); + EXPECT_ENUM_EQ(state->dataHeatPumpAirToWater->heatPumps[0].operatingModeControlOptionMultipleUnit, + HeatPumpAirToWater::OperatingModeControlOptionMultipleUnit::SingleMode); + + EXPECT_ENUM_EQ(state->dataHeatPumpAirToWater->heatPumps[1].EIRHPType, DataPlant::PlantEquipmentType::HeatPumpAirToWaterHeating); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[1].name, "TEST_AWHP"); + EXPECT_ENUM_EQ(state->dataHeatPumpAirToWater->heatPumps[1].operatingModeControlMethod, HeatPumpAirToWater::OperatingModeControlMethod::Load); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[1].availSchedName, ""); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[1].availSched, Sched::GetScheduleAlwaysOn(*state)); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[1].operationModeControlSche, nullptr); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[1].sourceSideDesignInletTemp, 20); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[1].sourceSideDesignVolFlowRate, 0.1); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[1].ratedLeavingWaterTemperature, 50); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[1].loadSideDesignVolFlowRate, 0.02); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[1].minSourceTempLimit, -20); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[1].maxSourceTempLimit, 25); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[1].minSupplyWaterTempCurveIndex, 3); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[1].maxSupplyWaterTempCurveIndex, 4); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[1].sizingFactor, 1.0); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[1].loadSideNodes.inlet, 5); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[1].loadSideNodes.outlet, 6); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[1].sourceSideNodes.inlet, 3); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[1].sourceSideNodes.outlet, 4); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[1].maxOutdoorTemperatureDefrost, 10); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[1].defrostStrategy, DefrostControl::Timed); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[1].defrostTime, 0.2); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[1].defrostResistiveHeaterCap, 150); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[1].defrostEIRFTIndex, 0); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[1].heatPumpMultiplier, 1); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[1].controlType, HeatPumpAirToWater::CompressorControlType::FixedSpeed); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[1].CrankcaseHeaterCapacity, 100); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[1].CrankcaseHeaterCapacityCurveIndex, Curve::GetCurveIndex(*state, "EIRCURVEFUNCPLR")); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[1].MaxOATCrankcaseHeater, 20); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[1].boosterOn, true); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[1].numSpeeds, 3); // with booster mode, one more speed level + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[1].referenceCapacity, 50000); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[1].ratedCapacity[0], 100); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[1].ratedCOP[0], 3); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[1].capFuncTempCurveIndex[0], Curve::GetCurveIndex(*state, "CAPCURVEFUNCTEMP")); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[1].powerRatioFuncTempCurveIndex[0], Curve::GetCurveIndex(*state, "EIRCURVEFUNCTEMP")); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[1].powerRatioFuncPLRCurveIndex[0], Curve::GetCurveIndex(*state, "EIRCURVEFUNCPLR")); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[1].ratedCapacity[1], 200); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[1].ratedCOP[1], 3.5); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[1].capFuncTempCurveIndex[1], Curve::GetCurveIndex(*state, "CAPCURVEFUNCTEMP")); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[1].powerRatioFuncTempCurveIndex[1], Curve::GetCurveIndex(*state, "EIRCURVEFUNCTEMP")); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[1].powerRatioFuncPLRCurveIndex[1], Curve::GetCurveIndex(*state, "EIRCURVEFUNCPLR")); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[1].ratedCapacity[2], 50000); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[1].ratedCOP[2], 2.5); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[1].capFuncTempCurveIndex[2], Curve::GetCurveIndex(*state, "CAPCURVEFUNCTEMP")); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[1].powerRatioFuncTempCurveIndex[2], Curve::GetCurveIndex(*state, "EIRCURVEFUNCTEMP")); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[1].powerRatioFuncPLRCurveIndex[2], Curve::GetCurveIndex(*state, "EIRCURVEFUNCPLR")); + EXPECT_ENUM_EQ(state->dataHeatPumpAirToWater->heatPumps[1].operatingModeControlMethod, HeatPumpAirToWater::OperatingModeControlMethod::Load); + EXPECT_ENUM_EQ(state->dataHeatPumpAirToWater->heatPumps[1].operatingModeControlOptionMultipleUnit, + HeatPumpAirToWater::OperatingModeControlOptionMultipleUnit::SingleMode); + + equipType = DataPlant::PlantEquipmentType::HeatPumpAirToWaterHeating; + HeatPumpAirToWater::factory(*state, equipType, "test_AWHP_defaults"); + equipType = DataPlant::PlantEquipmentType::HeatPumpAirToWaterCooling; + HeatPumpAirToWater::factory(*state, equipType, "test_AWHP_defaults"); + + // cooling component in the AWHP + EXPECT_ENUM_EQ(state->dataHeatPumpAirToWater->heatPumps[2].EIRHPType, DataPlant::PlantEquipmentType::HeatPumpAirToWaterCooling); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[2].name, "TEST_AWHP_DEFAULTS"); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[2].availSchedName, ""); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[2].availSched, Sched::GetScheduleAlwaysOn(*state)); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[2].operationModeControlSche, nullptr); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[2].sourceSideDesignInletTemp, 8); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[2].sourceSideDesignVolFlowRate, -99999); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[2].ratedLeavingWaterTemperature, 40); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[2].loadSideDesignVolFlowRate, -99999); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[2].minSourceTempLimit, -30); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[2].maxSourceTempLimit, 100); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[2].minSupplyWaterTempCurveIndex, 0); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[2].maxSupplyWaterTempCurveIndex, 0); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[2].sizingFactor, 1.0); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[2].heatPumpMultiplier, 1); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[2].controlType, HeatPumpAirToWater::CompressorControlType::VariableSpeed); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[2].boosterOn, false); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[2].boosterMultCap, 1.0); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[2].boosterMultCOP, 1.0); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[2].numSpeeds, 1); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[2].referenceCapacity, -99999); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[2].ratedCapacity[0], -99999); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[2].ratedCOP[0], 3.0); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[2].capFuncTempCurveIndex[0], Curve::GetCurveIndex(*state, "CAPCURVEFUNCTEMP")); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[2].powerRatioFuncTempCurveIndex[0], Curve::GetCurveIndex(*state, "EIRCURVEFUNCTEMP")); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[2].powerRatioFuncPLRCurveIndex[0], Curve::GetCurveIndex(*state, "EIRCURVEFUNCPLR")); + EXPECT_ENUM_EQ(state->dataHeatPumpAirToWater->heatPumps[2].operatingModeControlMethod, HeatPumpAirToWater::OperatingModeControlMethod::Load); + EXPECT_ENUM_EQ(state->dataHeatPumpAirToWater->heatPumps[2].operatingModeControlOptionMultipleUnit, + HeatPumpAirToWater::OperatingModeControlOptionMultipleUnit::SingleMode); + + EXPECT_ENUM_EQ(state->dataHeatPumpAirToWater->heatPumps[3].EIRHPType, DataPlant::PlantEquipmentType::HeatPumpAirToWaterHeating); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[3].name, "TEST_AWHP_DEFAULTS"); + EXPECT_ENUM_EQ(state->dataHeatPumpAirToWater->heatPumps[3].operatingModeControlMethod, HeatPumpAirToWater::OperatingModeControlMethod::Load); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[3].availSchedName, ""); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[3].availSched, Sched::GetScheduleAlwaysOn(*state)); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[3].operationModeControlSche, nullptr); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[3].sourceSideDesignInletTemp, 8); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[3].sourceSideDesignVolFlowRate, -99999); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[3].ratedLeavingWaterTemperature, 40); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[3].loadSideDesignVolFlowRate, -99999); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[3].minSourceTempLimit, -30); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[3].maxSourceTempLimit, 100); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[2].minSupplyWaterTempCurveIndex, 0); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[2].maxSupplyWaterTempCurveIndex, 0); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[3].sizingFactor, 1.0); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[3].maxOutdoorTemperatureDefrost, 10); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[3].defrostStrategy, DefrostControl::None); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[3].defrostTime, 0.0); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[3].defrostResistiveHeaterCap, 0.0); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[3].defrostEIRFTIndex, 0); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[3].heatPumpMultiplier, 1.0); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[3].controlType, HeatPumpAirToWater::CompressorControlType::VariableSpeed); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[3].CrankcaseHeaterCapacity, 0.0); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[3].CrankcaseHeaterCapacityCurveIndex, 0); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[3].MaxOATCrankcaseHeater, 10.0); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[3].boosterOn, false); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[3].boosterMultCap, 1.0); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[3].boosterMultCOP, 1.0); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[3].numSpeeds, 1); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[3].referenceCapacity, -99999); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[3].ratedCapacity[0], -99999); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[3].ratedCOP[0], 3.0); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[3].capFuncTempCurveIndex[0], Curve::GetCurveIndex(*state, "CAPCURVEFUNCTEMP")); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[3].powerRatioFuncTempCurveIndex[0], Curve::GetCurveIndex(*state, "EIRCURVEFUNCTEMP")); + EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[3].powerRatioFuncPLRCurveIndex[0], Curve::GetCurveIndex(*state, "EIRCURVEFUNCPLR")); + EXPECT_ENUM_EQ(state->dataHeatPumpAirToWater->heatPumps[3].operatingModeControlMethod, HeatPumpAirToWater::OperatingModeControlMethod::Load); + EXPECT_ENUM_EQ(state->dataHeatPumpAirToWater->heatPumps[3].operatingModeControlOptionMultipleUnit, + HeatPumpAirToWater::OperatingModeControlOptionMultipleUnit::SingleMode); +} + +TEST_F(EnergyPlusFixture, calcLoadSideHeatTransfer_AWHP) +{ + auto thisAWHP = HeatPumpAirToWater(); + + state->dataPlnt->PlantLoop.allocate(1); + state->dataPlnt->PlantLoop(1).FluidName = "WATER"; + state->dataPlnt->PlantLoop(1).glycol = Fluid::GetWater(*state); + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).TotalBranches = 1; + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch.allocate(1); + 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 &PLHPPlantLoadSideComp = state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp(1); + PLHPPlantLoadSideComp.Type = DataPlant::PlantEquipmentType::HeatPumpAirToWaterCooling; + state->dataLoopNodes->Node.allocate(2); + thisAWHP.EIRHPType = EnergyPlus::DataPlant::PlantEquipmentType::HeatPumpAirToWaterCooling; + thisAWHP.loadSidePlantLoc = {1, DataPlant::LoopSideLocation::Supply, 1, 1}; + PlantUtilities::SetPlantLocationLinks(*state, thisAWHP.loadSidePlantLoc); + thisAWHP.loadSideNodes.outlet = 2; + thisAWHP.loadSideNodes.inlet = 1; + thisAWHP.loadSideMassFlowRate = 2; + thisAWHP.loadSideInletTemp = 20; + state->dataLoopNodes->Node(thisAWHP.loadSideNodes.inlet).Temp = thisAWHP.loadSideInletTemp; + Real64 CpLoad = thisAWHP.loadSidePlantLoc.loop->glycol->getSpecificHeat( + *state, state->dataLoopNodes->Node(thisAWHP.loadSideNodes.inlet).Temp, "HeatPumpAirToWater::calcLoadSideHeatTransfer()"); + // if capacity is less than load, heat transfer is just capacity + Real64 capacity = 100; + Real64 load = 120; + thisAWHP.calcLoadOutletTemp = EIRPlantLoopHeatPumps::HeatPumpAirToWater::subtract; + thisAWHP.calcLoadSideHeatTransfer(*state, capacity, load); + EXPECT_EQ(thisAWHP.loadSideHeatTransfer, capacity); + EXPECT_NEAR( + CpLoad * (thisAWHP.loadSideOutletTemp - thisAWHP.loadSideInletTemp) * thisAWHP.loadSideMassFlowRate, -thisAWHP.loadSideHeatTransfer, 1e-6); + + thisAWHP.EIRHPType = EnergyPlus::DataPlant::PlantEquipmentType::HeatPumpAirToWaterHeating; + thisAWHP.calcLoadOutletTemp = EIRPlantLoopHeatPumps::HeatPumpAirToWater::add; + thisAWHP.calcLoadSideHeatTransfer(*state, capacity, load); + EXPECT_EQ(thisAWHP.loadSideHeatTransfer, capacity); + EXPECT_NEAR( + CpLoad * (thisAWHP.loadSideOutletTemp - thisAWHP.loadSideInletTemp) * thisAWHP.loadSideMassFlowRate, thisAWHP.loadSideHeatTransfer, 1e-6); + + // if capacity is less than load, heat transfer is load + capacity = 120; + load = 100; + thisAWHP.EIRHPType = EnergyPlus::DataPlant::PlantEquipmentType::HeatPumpAirToWaterCooling; + thisAWHP.calcLoadOutletTemp = EIRPlantLoopHeatPumps::HeatPumpAirToWater::subtract; + thisAWHP.calcLoadSideHeatTransfer(*state, capacity, load); + EXPECT_EQ(thisAWHP.loadSideHeatTransfer, load); + EXPECT_NEAR( + CpLoad * (thisAWHP.loadSideOutletTemp - thisAWHP.loadSideInletTemp) * thisAWHP.loadSideMassFlowRate, -thisAWHP.loadSideHeatTransfer, 1e-6); + + thisAWHP.EIRHPType = EnergyPlus::DataPlant::PlantEquipmentType::HeatPumpAirToWaterHeating; + thisAWHP.calcLoadOutletTemp = EIRPlantLoopHeatPumps::HeatPumpAirToWater::add; + thisAWHP.calcLoadSideHeatTransfer(*state, capacity, load); + EXPECT_EQ(thisAWHP.loadSideHeatTransfer, load); + EXPECT_NEAR( + CpLoad * (thisAWHP.loadSideOutletTemp - thisAWHP.loadSideInletTemp) * thisAWHP.loadSideMassFlowRate, thisAWHP.loadSideHeatTransfer, 1e-6); + + // if load is smaller than minimum load (minimumPLR * availableCapacity), heat transfer is minimum load + capacity = 120; + load = 10; + thisAWHP.minimumPLR = 0.1; + thisAWHP.referenceCapacity = capacity; + thisAWHP.EIRHPType = EnergyPlus::DataPlant::PlantEquipmentType::HeatPumpAirToWaterCooling; + thisAWHP.calcLoadOutletTemp = EIRPlantLoopHeatPumps::HeatPumpAirToWater::subtract; + thisAWHP.calcLoadSideHeatTransfer(*state, capacity, load); + EXPECT_EQ(thisAWHP.loadSideHeatTransfer, capacity * thisAWHP.minimumPLR); +} + +TEST_F(EnergyPlusFixture, calcPowerUsage_AWHP) +{ + + std::string const idf_objects = delimited_string({ + "Curve:Biquadratic,", + "CapCurveFuncTemp, !- Name", + "1.0, !- Coefficient1 Constant", + "0.0, !- Coefficient2 x", + "0.0, !- Coefficient3 x**2", + "0.0, !- Coefficient4 y", + "0.0, !- Coefficient5 y**2", + "0.0, !- Coefficient6 x*y", + "5.0, !- Minimum Value of x", + "10.0, !- Maximum Value of x", + "24.0, !- Minimum Value of y", + "35.0, !- Maximum Value of y", + ", !- Minimum Curve Output", + ", !- Maximum Curve Output", + "Temperature, !- Input Unit Type for X", + "Temperature, !- Input Unit Type for Y", + "Dimensionless; !- Output Unit Type", + + "Curve:Biquadratic,", + "EIRCurveFuncTemp, !- Name", + "1.0, !- Coefficient1 Constant", + "0.0, !- Coefficient2 x", + "0.0, !- Coefficient3 x**2", + "0.0, !- Coefficient4 y", + "0.0, !- Coefficient5 y**2", + "0.0, !- Coefficient6 x*y", + "5.0, !- Minimum Value of x", + "10.0, !- Maximum Value of x", + "24.0, !- Minimum Value of y", + "35.0, !- Maximum Value of y", + ", !- Minimum Curve Output", + ", !- Maximum Curve Output", + "Temperature, !- Input Unit Type for X", + "Temperature, !- Input Unit Type for Y", + "Dimensionless; !- Output Unit Type", + + "Curve:Quadratic,", + "EIRCurveFuncPLR, !- Name", + "1.0, !- Coefficient1 Constant", + "0.0, !- Coefficient2 x", + "0.0, !- Coefficient3 x**2", + "0.0, !- Minimum Value of x", + "1.0; !- Maximum Value of x", + }); + ASSERT_TRUE(process_idf(idf_objects)); + state->init_state(*state); + + auto thisAWHP = HeatPumpAirToWater(); + state->dataPlnt->PlantLoop.allocate(1); + state->dataPlnt->PlantLoop(1).FluidName = "WATER"; + state->dataPlnt->PlantLoop(1).glycol = Fluid::GetWater(*state); + state->dataPlnt->PlantLoop(1).LoopDemandCalcScheme = DataPlant::LoopDemandCalcScheme::SingleSetPoint; + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).TotalBranches = 1; + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch.allocate(1); + 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 &PLHPPlantLoadSideComp = state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp(1); + PLHPPlantLoadSideComp.Type = DataPlant::PlantEquipmentType::HeatPumpAirToWaterCooling; + thisAWHP.loadSidePlantLoc = {1, DataPlant::LoopSideLocation::Supply, 1, 1}; + PlantUtilities::SetPlantLocationLinks(*state, thisAWHP.loadSidePlantLoc); + thisAWHP.loadSidePlantLoc.comp->CurOpSchemeType = DataPlant::OpScheme::CompSetPtBased; + thisAWHP.EIRHPType = EnergyPlus::DataPlant::PlantEquipmentType::HeatPumpAirToWaterCooling; + + thisAWHP.loadSideNodes.inlet = 1; + thisAWHP.loadSideNodes.outlet = 2; + thisAWHP.setPointNodeNum = thisAWHP.loadSideNodes.outlet; + state->dataLoopNodes->Node.allocate(2); + state->dataLoopNodes->Node(thisAWHP.setPointNodeNum).TempSetPoint = 20; + thisAWHP.sourceSideHeatTransfer = 100; + thisAWHP.loadSideHeatTransfer = 90; + thisAWHP.numSpeeds = 2; + thisAWHP.ratedCapacity[0] = 600; + thisAWHP.ratedCOP[0] = 1; + thisAWHP.capFuncTempCurveIndex[0] = Curve::GetCurveIndex(*state, "CAPCURVEFUNCTEMP"); + thisAWHP.powerRatioFuncTempCurveIndex[0] = Curve::GetCurveIndex(*state, "EIRCURVEFUNCTEMP"); + thisAWHP.powerRatioFuncPLRCurveIndex[0] = Curve::GetCurveIndex(*state, "EIRCURVEFUNCPLR"); + thisAWHP.ratedCapacity[1] = 1200; + thisAWHP.ratedCOP[1] = 1; + thisAWHP.capFuncTempCurveIndex[1] = Curve::GetCurveIndex(*state, "CAPCURVEFUNCTEMP"); + thisAWHP.powerRatioFuncTempCurveIndex[1] = Curve::GetCurveIndex(*state, "EIRCURVEFUNCTEMP"); + thisAWHP.powerRatioFuncPLRCurveIndex[1] = Curve::GetCurveIndex(*state, "EIRCURVEFUNCPLR"); + thisAWHP.heatPumpMultiplier = 2; + thisAWHP.cyclingRatio = 1.0; + Real64 availableCapacityBeforeMultiplier = thisAWHP.ratedCapacity[1]; + // when COP = 1, power usage should equal heat transfer + thisAWHP.loadSideHeatTransfer = 500; + thisAWHP.calcPowerUsage(*state, availableCapacityBeforeMultiplier); + EXPECT_EQ(thisAWHP.speedLevel, 1); + EXPECT_EQ(thisAWHP.powerUsage, 500); + EXPECT_EQ(thisAWHP.cyclingRatio, 500.0 / 600.0); + EXPECT_EQ(thisAWHP.numUnitUsed, 1); + thisAWHP.loadSideHeatTransfer = 1000; + thisAWHP.cyclingRatio = 1.0; // reset cycling ratio back to 1 + thisAWHP.calcPowerUsage(*state, availableCapacityBeforeMultiplier); + EXPECT_EQ(thisAWHP.speedLevel, 2); + EXPECT_EQ(thisAWHP.powerUsage, 1000); + EXPECT_EQ(thisAWHP.cyclingRatio, 1.0); + EXPECT_EQ(thisAWHP.numUnitUsed, 1); + thisAWHP.loadSideHeatTransfer = 1500; + thisAWHP.cyclingRatio = 1.0; // reset cycling ratio back to 1 + thisAWHP.calcPowerUsage(*state, availableCapacityBeforeMultiplier); + EXPECT_EQ(thisAWHP.speedLevel, 1); + EXPECT_EQ(thisAWHP.powerUsage, 1500); + EXPECT_EQ(thisAWHP.numUnitUsed, 2); + EXPECT_EQ(thisAWHP.cyclingRatio, 300.0 / 600.0); + thisAWHP.loadSideHeatTransfer = 2000; + thisAWHP.cyclingRatio = 1.0; // reset cycling ratio back to 1 + thisAWHP.calcPowerUsage(*state, availableCapacityBeforeMultiplier); + EXPECT_EQ(thisAWHP.speedLevel, 2); + EXPECT_EQ(thisAWHP.powerUsage, 2000); + EXPECT_EQ(thisAWHP.numUnitUsed, 2); + EXPECT_EQ(thisAWHP.cyclingRatio, 1.0); +} + +TEST_F(EnergyPlusFixture, crankcaseHeater_AWHP) +{ + std::string const idf_objects = delimited_string({ + "Curve:Linear,", + "heaterCapCurve, !- Name", + "10.0, !- Coefficient1 Constant", + "2., !- Coefficient2 x", + "-10.0, !- Minimum Value of x", + "70; !- Maximum Value of x", + }); + ASSERT_TRUE(process_idf(idf_objects)); + state->init_state(*state); + auto thisAWHP = HeatPumpAirToWater(); + thisAWHP.CrankcaseHeaterCapacity = 100; + thisAWHP.CrankcaseHeaterCapacityCurveIndex = Curve::GetCurveIndex(*state, "HEATERCAPCURVE"); + // when outdoor temperature is higher than temperature threshold, crankcase heater is off + state->dataEnvrn->OutDryBulbTemp = 15; + thisAWHP.MaxOATCrankcaseHeater = 10; + thisAWHP.CrankcaseHeaterPower = thisAWHP.calcCrankcaseHeaterPower(*state); + ASSERT_EQ(thisAWHP.CrankcaseHeaterPower, 0.0); + state->dataEnvrn->OutDryBulbTemp = 9; + thisAWHP.CrankcaseHeaterPower = thisAWHP.calcCrankcaseHeaterPower(*state); + ASSERT_EQ(thisAWHP.CrankcaseHeaterPower, 100 * (10 + 2 * 9)); +} + +TEST_F(EnergyPlusFixture, calcOpMode_AWHP) +{ + std::string const idf_objects = delimited_string({ + "Curve:Biquadratic," + "CapCurveFuncTemp, !- Name", + "1.0, !- Coefficient1 Constant", + "0.0, !- Coefficient2 x", + "0.0, !- Coefficient3 x**2", + "0.0, !- Coefficient4 y", + "0.0, !- Coefficient5 y**2", + "0.0, !- Coefficient6 x*y", + "5.0, !- Minimum Value of x", + "10.0, !- Maximum Value of x", + "24.0, !- Minimum Value of y", + "35.0, !- Maximum Value of y", + ", !- Minimum Curve Output", + ", !- Maximum Curve Output", + "Temperature, !- Input Unit Type for X", + "Temperature, !- Input Unit Type for Y", + "Dimensionless; !- Output Unit Type", + }); + ASSERT_TRUE(process_idf(idf_objects)); + state->init_state(*state); + auto thisAWHP = HeatPumpAirToWater(); + auto companionAWHP = HeatPumpAirToWater(); + thisAWHP.companionHeatPumpCoil = &companionAWHP; + companionAWHP.companionHeatPumpCoil = &thisAWHP; + thisAWHP.heatPumpMultiplier = 6; + companionAWHP.heatPumpMultiplier = 6; + thisAWHP.referenceCapacityOneUnit = 100; + companionAWHP.referenceCapacityOneUnit = 150; + thisAWHP.EIRHPType = EnergyPlus::DataPlant::PlantEquipmentType::HeatPumpAirToWaterCooling; + companionAWHP.EIRHPType = EnergyPlus::DataPlant::PlantEquipmentType::HeatPumpAirToWaterHeating; + + state->dataPlnt->PlantLoop.allocate(2); + state->dataPlnt->PlantLoop(1).FluidName = "WATER"; + state->dataPlnt->PlantLoop(1).glycol = Fluid::GetWater(*state); + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).TotalBranches = 1; + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch.allocate(1); + 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 &PLHPPlantLoadSideComp = state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp(1); + PLHPPlantLoadSideComp.Type = DataPlant::PlantEquipmentType::HeatPumpAirToWaterCooling; + thisAWHP.loadSidePlantLoc = {1, EnergyPlus::DataPlant::LoopSideLocation::Supply, 1, 1}; + PlantUtilities::SetPlantLocationLinks(*state, thisAWHP.loadSidePlantLoc); + thisAWHP.OperationModeEMSOverrideOn = false; + + state->dataPlnt->PlantLoop(2).FluidName = "WATER"; + state->dataPlnt->PlantLoop(2).glycol = Fluid::GetWater(*state); + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Supply).TotalBranches = 1; + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Supply).Branch.allocate(1); + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).TotalComponents = 1; + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp.allocate(1); + auto &companionPLHPPlantLoadSideComp = state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp(1); + companionPLHPPlantLoadSideComp.Type = DataPlant::PlantEquipmentType::HeatPumpAirToWaterHeating; + companionAWHP.loadSidePlantLoc = {2, EnergyPlus::DataPlant::LoopSideLocation::Supply, 1, 1}; + PlantUtilities::SetPlantLocationLinks(*state, companionAWHP.loadSidePlantLoc); + companionAWHP.OperationModeEMSOverrideOn = false; + + thisAWHP.capFuncTempCurveIndex[0] = 1; + thisAWHP.loadSideOutletTemp = 65; + thisAWHP.sourceSideInletTemp = 20; + companionAWHP.capFuncTempCurveIndex[0] = 1; + companionAWHP.loadSideOutletTemp = 65; + companionAWHP.sourceSideInletTemp = 20; + + Real64 currentLoad = -300; + companionPLHPPlantLoadSideComp.MyLoad = 400; + thisAWHP.calcOpMode(*state, currentLoad, HeatPumpAirToWater::OperatingModeControlOptionMultipleUnit::SingleMode); + EXPECT_EQ(thisAWHP.operatingMode, 0); + EXPECT_EQ(companionAWHP.operatingMode, 3); + + currentLoad = -500; + companionPLHPPlantLoadSideComp.MyLoad = 300; + thisAWHP.calcOpMode(*state, currentLoad, HeatPumpAirToWater::OperatingModeControlOptionMultipleUnit::SingleMode); + EXPECT_EQ(thisAWHP.operatingMode, 5); + EXPECT_EQ(companionAWHP.operatingMode, 0); + + currentLoad = -300; + companionPLHPPlantLoadSideComp.MyLoad = 400; + thisAWHP.calcOpMode(*state, currentLoad, HeatPumpAirToWater::OperatingModeControlOptionMultipleUnit::CoolingPriority); + EXPECT_EQ(thisAWHP.operatingMode, 3); + EXPECT_EQ(companionAWHP.operatingMode, 3); + + currentLoad = -500; + companionPLHPPlantLoadSideComp.MyLoad = 300; + thisAWHP.calcOpMode(*state, currentLoad, HeatPumpAirToWater::OperatingModeControlOptionMultipleUnit::CoolingPriority); + EXPECT_EQ(thisAWHP.operatingMode, 5); + EXPECT_EQ(companionAWHP.operatingMode, 1); + + currentLoad = -250; + companionPLHPPlantLoadSideComp.MyLoad = 250; + thisAWHP.calcOpMode(*state, currentLoad, HeatPumpAirToWater::OperatingModeControlOptionMultipleUnit::CoolingPriority); + EXPECT_EQ(thisAWHP.operatingMode, 3); + EXPECT_EQ(companionAWHP.operatingMode, 2); + + currentLoad = -300; + companionPLHPPlantLoadSideComp.MyLoad = 400; + thisAWHP.calcOpMode(*state, currentLoad, HeatPumpAirToWater::OperatingModeControlOptionMultipleUnit::HeatingPriority); + EXPECT_EQ(thisAWHP.operatingMode, 3); + EXPECT_EQ(companionAWHP.operatingMode, 3); + + currentLoad = -500; + companionPLHPPlantLoadSideComp.MyLoad = 300; + thisAWHP.calcOpMode(*state, currentLoad, HeatPumpAirToWater::OperatingModeControlOptionMultipleUnit::HeatingPriority); + EXPECT_EQ(thisAWHP.operatingMode, 4); + EXPECT_EQ(companionAWHP.operatingMode, 2); + + currentLoad = -250; + companionPLHPPlantLoadSideComp.MyLoad = 250; + thisAWHP.calcOpMode(*state, currentLoad, HeatPumpAirToWater::OperatingModeControlOptionMultipleUnit::HeatingPriority); + EXPECT_EQ(thisAWHP.operatingMode, 3); + EXPECT_EQ(companionAWHP.operatingMode, 2); + + currentLoad = -200; + companionPLHPPlantLoadSideComp.MyLoad = 1000; + thisAWHP.calcOpMode(*state, currentLoad, HeatPumpAirToWater::OperatingModeControlOptionMultipleUnit::HeatingPriority); + EXPECT_EQ(thisAWHP.operatingMode, 0); + EXPECT_EQ(companionAWHP.operatingMode, 6); + + currentLoad = 0; + companionPLHPPlantLoadSideComp.MyLoad = 400; + thisAWHP.calcOpMode(*state, currentLoad, HeatPumpAirToWater::OperatingModeControlOptionMultipleUnit::Balanced); + EXPECT_EQ(thisAWHP.operatingMode, 0); + EXPECT_EQ(companionAWHP.operatingMode, 3); + + currentLoad = 0; + companionPLHPPlantLoadSideComp.MyLoad = 4000; + thisAWHP.calcOpMode(*state, currentLoad, HeatPumpAirToWater::OperatingModeControlOptionMultipleUnit::Balanced); + EXPECT_EQ(thisAWHP.operatingMode, 0); + EXPECT_EQ(companionAWHP.operatingMode, 6); + + currentLoad = -400; + companionPLHPPlantLoadSideComp.MyLoad = 0; + thisAWHP.calcOpMode(*state, currentLoad, HeatPumpAirToWater::OperatingModeControlOptionMultipleUnit::Balanced); + EXPECT_EQ(thisAWHP.operatingMode, 4); + EXPECT_EQ(companionAWHP.operatingMode, 0); + + currentLoad = -4000; + companionPLHPPlantLoadSideComp.MyLoad = 0; + thisAWHP.calcOpMode(*state, currentLoad, HeatPumpAirToWater::OperatingModeControlOptionMultipleUnit::Balanced); + EXPECT_EQ(thisAWHP.operatingMode, 6); + EXPECT_EQ(companionAWHP.operatingMode, 0); + + currentLoad = -300; + companionPLHPPlantLoadSideComp.MyLoad = 400; + thisAWHP.calcOpMode(*state, currentLoad, HeatPumpAirToWater::OperatingModeControlOptionMultipleUnit::Balanced); + EXPECT_EQ(thisAWHP.operatingMode, 3); + EXPECT_EQ(companionAWHP.operatingMode, 3); + + currentLoad = -500; + companionPLHPPlantLoadSideComp.MyLoad = 300; + thisAWHP.calcOpMode(*state, currentLoad, HeatPumpAirToWater::OperatingModeControlOptionMultipleUnit::Balanced); + EXPECT_EQ(thisAWHP.operatingMode, 4); + EXPECT_EQ(companionAWHP.operatingMode, 2); + + currentLoad = -1000; + companionPLHPPlantLoadSideComp.MyLoad = 1500; + thisAWHP.calcOpMode(*state, currentLoad, HeatPumpAirToWater::OperatingModeControlOptionMultipleUnit::Balanced); + EXPECT_EQ(thisAWHP.operatingMode, 3); + EXPECT_EQ(companionAWHP.operatingMode, 3); +} + +TEST_F(EnergyPlusFixture, processInputForEIRPLHP_TestAirSourceDuplicateNodes) +{ + std::string const idf_objects = delimited_string({"HeatPump:PlantLoop:EIR:Cooling,", + " hp cooling side,", + " node 1,", + " node 2,", + " AirSource,", + " node 3,", + " node 3,", + " ,", + " ,", + " ,", + " 0.001,", + " 0.001,", + " ,", + " 1000,", + " 3.14,", + " ,", + " dummyCurve,", + " dummyCurve,", + " dummyCurve;", + "Curve:Linear,", + " dummyCurve,", + " 1,", + " 0,", + " 1,", + " 1;"}); + ASSERT_TRUE(process_idf(idf_objects)); + state->init_state(*state); + + // set up the plant loops + // first the load side + state->dataPlnt->TotNumLoops = 2; + state->dataPlnt->PlantLoop.allocate(2); + + state->dataPlnt->PlantLoop(1).FluidName = "WATER"; + state->dataPlnt->PlantLoop(1).glycol = Fluid::GetWater(*state); + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).TotalBranches = 1; + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch.allocate(1); + 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 &PLHPPlantLoadSideComp = state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp(1); + PLHPPlantLoadSideComp.Type = DataPlant::PlantEquipmentType::HeatPumpEIRCooling; + // then the source side + + state->dataPlnt->PlantLoop(2).FluidName = "WATER"; + state->dataPlnt->PlantLoop(2).glycol = Fluid::GetWater(*state); + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).TotalBranches = 1; + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch.allocate(1); + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).TotalComponents = 1; + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp.allocate(1); + auto &PLHPPlantLoadSourceComp = state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp(1); + PLHPPlantLoadSourceComp.Type = DataPlant::PlantEquipmentType::HeatPumpEIRCooling; + + // the init call expects a "from" calling point + PlantLocation myLocation = PlantLocation(1, DataPlant::LoopSideLocation::Supply, 1, 1); + + // call the factory with a valid name to trigger reading inputs + // expects fatal error due to same node names + EXPECT_THROW(EIRPlantLoopHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpEIRCooling, "HP COOLING SIDE");, std::runtime_error); + // expect error related to same node names + std::string error_msg = delimited_string({ + " ** Severe ** PlantLoopHeatPump hp cooling side has the same inlet and outlet node.", + " ** ~~~ ** Node Name: NODE 3", + " ** Fatal ** Previous EIR PLHP errors cause program termination", + " ...Summary of Errors that led to program termination:", + " ..... Reference severe error count=1", + " ..... Last severe error=PlantLoopHeatPump hp cooling side has the same inlet and outlet node.", + }); + EXPECT_TRUE(compare_err_stream(error_msg)); +} + +TEST_F(EnergyPlusFixture, processInputForEIRPLHP_TestAirSourceOANode) +{ + std::string const idf_objects = delimited_string({"HeatPump:PlantLoop:EIR:Cooling,", + " hp cooling side,", + " node 1,", + " node 2,", + " AirSource,", + " node 3,", + " node 4,", + " ,", + " ,", + " ,", + " Autosize,", + " Autosize,", + " ,", + " Autosize,", + " 1.0,", + " 1,", + " dummyCurve,", + " dummyCurve,", + " dummyCurve;", + "Curve:Linear,", + " dummyCurve,", + " 1,", + " 0,", + " 1,", + " 1;", + "OutdoorAir:NodeList,", + " node 3;"}); + ASSERT_TRUE(process_idf(idf_objects)); + state->init_state(*state); + + // set up the plant loops + // first the load side + state->dataPlnt->TotNumLoops = 2; + state->dataPlnt->PlantLoop.allocate(2); + + state->dataPlnt->PlantLoop(1).FluidName = "WATER"; + state->dataPlnt->PlantLoop(1).glycol = Fluid::GetWater(*state); + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).TotalBranches = 1; + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch.allocate(1); + 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 &PLHPPlantLoadSideComp = state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp(1); + PLHPPlantLoadSideComp.Type = DataPlant::PlantEquipmentType::HeatPumpEIRCooling; + // then the source side + + state->dataPlnt->PlantLoop(2).FluidName = "WATER"; + state->dataPlnt->PlantLoop(2).glycol = Fluid::GetWater(*state); + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).TotalBranches = 1; + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch.allocate(1); + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).TotalComponents = 1; + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp.allocate(1); + auto &PLHPPlantLoadSourceComp = state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp(1); + PLHPPlantLoadSourceComp.Type = DataPlant::PlantEquipmentType::HeatPumpEIRCooling; + + // the init call expects a "from" calling point + PlantLocation myLocation = PlantLocation(1, DataPlant::LoopSideLocation::Supply, 1, 1); + // setup the outdoor air nodes + OutAirNodeManager::SetOutAirNodes(*state); + // call the factory with a valid name to trigger reading inputs + // expects fatal error due to same node names + EIRPlantLoopHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpEIRCooling, "HP COOLING SIDE"); + EXPECT_TRUE(compare_err_stream("")); +} + +TEST_F(EnergyPlusFixture, processInputForEIRPLHP_TestAirSourceNoOANode) +{ + std::string const idf_objects = delimited_string({"HeatPump:PlantLoop:EIR:Cooling,", + " hp cooling side,", + " node 1,", + " node 2,", + " AirSource,", + " node 3,", + " node 4,", + " ,", + " ,", + " ,", + " Autosize,", + " Autosize,", + " ,", + " Autosize,", + " 1.0,", + " 1,", + " dummyCurve,", + " dummyCurve,", + " dummyCurve;", + "Curve:Linear,", + " dummyCurve,", + " 1,", + " 0,", + " 1,", + " 1;"}); + ASSERT_TRUE(process_idf(idf_objects)); + state->init_state(*state); + + // set up the plant loops + // first the load side + state->dataPlnt->TotNumLoops = 2; + state->dataPlnt->PlantLoop.allocate(2); + + state->dataPlnt->PlantLoop(1).FluidName = "WATER"; + state->dataPlnt->PlantLoop(1).glycol = Fluid::GetWater(*state); + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).TotalBranches = 1; + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch.allocate(1); + 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 &PLHPPlantLoadSideComp = state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp(1); + PLHPPlantLoadSideComp.Type = DataPlant::PlantEquipmentType::HeatPumpEIRCooling; + // then the source side + + state->dataPlnt->PlantLoop(2).FluidName = "WATER"; + state->dataPlnt->PlantLoop(2).glycol = Fluid::GetWater(*state); + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).TotalBranches = 1; + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch.allocate(1); + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).TotalComponents = 1; + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp.allocate(1); + auto &PLHPPlantLoadSourceComp = state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp(1); + PLHPPlantLoadSourceComp.Type = DataPlant::PlantEquipmentType::HeatPumpEIRCooling; + + // the init call expects a "from" calling point + PlantLocation myLocation = PlantLocation(1, DataPlant::LoopSideLocation::Supply, 1, 1); + + // call the factory with a valid name to trigger reading inputs + // expects fatal error due to same node names + EIRPlantLoopHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpEIRCooling, "HP COOLING SIDE"); + bool ErrFound = false; + Node::CheckNodeConnections(*state, ErrFound); + // expect error related to OA node not being an OutdoorAirNode + std::string error_msg = delimited_string({ + " ** Severe ** Node Connection Error, Node=\"NODE 1\", Inlet node did not find an appropriate matching \"outlet\" node.", + " ** ~~~ ** If this is an outdoor air inlet node, it must be listed in an OutdoorAir:Node or OutdoorAir:NodeList object.", + " ** ~~~ ** Reference Object=HeatPump:PlantLoop:EIR:Cooling, Name=HP COOLING SIDE", + " ** Severe ** Node Connection Error, Node=\"NODE 3\", Inlet node did not find an appropriate matching \"outlet\" node.", + " ** ~~~ ** If this is an outdoor air inlet node, it must be listed in an OutdoorAir:Node or OutdoorAir:NodeList object.", + " ** ~~~ ** Reference Object=HeatPump:PlantLoop:EIR:Cooling, Name=HP COOLING SIDE", + + }); + EXPECT_TRUE(compare_err_stream(error_msg)); +} + +TEST_F(EnergyPlusFixture, Initialization) +{ + std::string const idf_objects = delimited_string({"HeatPump:PlantLoop:EIR:Cooling,", + " hp cooling side,", + " node 1,", + " node 2,", + " WaterSource,", + " node 3,", + " node 4,", + " ,", + " ,", + " ,", + " 0.001,", + " 0.001,", + " ,", + " 1000,", + " 3.14,", + " ,", + " dummyCurve,", + " dummyCurve,", + " dummyCurve;", + "Curve:Linear,", + " dummyCurve,", + " 1,", + " 0,", + " 1,", + " 1;"}); + ASSERT_TRUE(process_idf(idf_objects)); + state->init_state(*state); + + // set up the plant loops + // first the load side + state->dataPlnt->TotNumLoops = 2; + state->dataPlnt->PlantLoop.allocate(2); + + state->dataPlnt->PlantLoop(1).FluidName = "WATER"; + state->dataPlnt->PlantLoop(1).glycol = Fluid::GetWater(*state); + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).TotalBranches = 1; + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch.allocate(1); + 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 &PLHPPlantLoadSideComp = state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp(1); + PLHPPlantLoadSideComp.Type = DataPlant::PlantEquipmentType::HeatPumpEIRCooling; + // then the source side + + state->dataPlnt->PlantLoop(2).FluidName = "WATER"; + state->dataPlnt->PlantLoop(2).glycol = Fluid::GetWater(*state); + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).TotalBranches = 1; + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch.allocate(1); + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).TotalComponents = 1; + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp.allocate(1); + auto &PLHPPlantLoadSourceComp = state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp(1); + PLHPPlantLoadSourceComp.Type = DataPlant::PlantEquipmentType::HeatPumpEIRCooling; + + // the init call expects a "from" calling point + PlantLocation myLocation = PlantLocation(1, DataPlant::LoopSideLocation::Supply, 1, 1); + + // call the factory with a valid name to trigger reading inputs + EIRPlantLoopHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpEIRCooling, "HP COOLING SIDE"); + + // verify the size of the vector and the processed condition + EXPECT_EQ(1u, state->dataEIRPlantLoopHeatPump->heatPumps.size()); + + // for now we know the order is maintained, so get each heat pump object + EIRPlantLoopHeatPump *thisCoolingPLHP = &state->dataEIRPlantLoopHeatPump->heatPumps[0]; + + // do a bit of extra wiring up to the plant + PLHPPlantLoadSideComp.Name = thisCoolingPLHP->name; + PLHPPlantLoadSideComp.NodeNumIn = thisCoolingPLHP->loadSideNodes.inlet; + PLHPPlantLoadSourceComp.Name = thisCoolingPLHP->name; + PLHPPlantLoadSourceComp.NodeNumIn = thisCoolingPLHP->sourceSideNodes.inlet; + + // call for initialization, oneTimeInit only first + state->dataGlobal->BeginEnvrnFlag = false; + thisCoolingPLHP->onInitLoopEquip(*state, myLocation); + + // validate that location work got done correctly + EXPECT_EQ(1, thisCoolingPLHP->loadSidePlantLoc.loopNum); + EXPECT_ENUM_EQ(DataPlant::LoopSideLocation::Supply, thisCoolingPLHP->loadSidePlantLoc.loopSideNum); + EXPECT_EQ(1, thisCoolingPLHP->loadSidePlantLoc.branchNum); + EXPECT_EQ(1, thisCoolingPLHP->loadSidePlantLoc.compNum); + EXPECT_EQ(2, thisCoolingPLHP->sourceSidePlantLoc.loopNum); + EXPECT_ENUM_EQ(DataPlant::LoopSideLocation::Demand, thisCoolingPLHP->sourceSidePlantLoc.loopSideNum); + EXPECT_EQ(1, thisCoolingPLHP->sourceSidePlantLoc.branchNum); + EXPECT_EQ(1, thisCoolingPLHP->sourceSidePlantLoc.compNum); + + // now call for initialization again, for begin environment + state->dataGlobal->BeginEnvrnFlag = true; + state->dataPlnt->PlantFirstSizesOkayToFinalize = true; + thisCoolingPLHP->onInitLoopEquip(*state, myLocation); + + // validate that plant sizing went ok + Real64 constexpr flowTol = 0.001; + Real64 constexpr rho = 999.89; // easy to edit here if the expected density gets adjusted in E+ + Real64 const expectedLoadSideMassFlow = rho * thisCoolingPLHP->loadSideDesignVolFlowRate; + Real64 const expectedSourceSideMassFlow = rho * thisCoolingPLHP->sourceSideDesignVolFlowRate; + EXPECT_NEAR(expectedLoadSideMassFlow, thisCoolingPLHP->loadSideDesignMassFlowRate, flowTol); + EXPECT_NEAR(expectedSourceSideMassFlow, thisCoolingPLHP->sourceSideDesignMassFlowRate, flowTol); + EXPECT_NEAR(0.0, state->dataLoopNodes->Node(thisCoolingPLHP->loadSideNodes.inlet).MassFlowRateMin, flowTol); + EXPECT_NEAR(0.0, state->dataLoopNodes->Node(thisCoolingPLHP->loadSideNodes.inlet).MassFlowRateMinAvail, flowTol); + EXPECT_NEAR(expectedLoadSideMassFlow, state->dataLoopNodes->Node(thisCoolingPLHP->loadSideNodes.inlet).MassFlowRateMax, flowTol); + EXPECT_NEAR(expectedLoadSideMassFlow, state->dataLoopNodes->Node(thisCoolingPLHP->loadSideNodes.inlet).MassFlowRateMaxAvail, flowTol); + EXPECT_NEAR(0.0, state->dataLoopNodes->Node(thisCoolingPLHP->sourceSideNodes.inlet).MassFlowRateMin, flowTol); + EXPECT_NEAR(0.0, state->dataLoopNodes->Node(thisCoolingPLHP->sourceSideNodes.inlet).MassFlowRateMinAvail, flowTol); + EXPECT_NEAR(expectedSourceSideMassFlow, state->dataLoopNodes->Node(thisCoolingPLHP->sourceSideNodes.inlet).MassFlowRateMax, flowTol); + EXPECT_NEAR(expectedSourceSideMassFlow, state->dataLoopNodes->Node(thisCoolingPLHP->sourceSideNodes.inlet).MassFlowRateMaxAvail, flowTol); +} + +TEST_F(EnergyPlusFixture, EIRPLHP_Initialization_SetpointMissing) +{ + std::string const idf_objects = delimited_string({ + "HeatPump:PlantLoop:EIR:Heating,", + " Heating Coil, !- Name", + " Heating Coil Load Loop Intermediate Node, !- Load Side Inlet Node Name", + " Heating Coil Load Loop Supply Side Heating Coil Outlet Node, !- Load Side Outlet Node Name", + " AirSource, !- Condenser Type", + " Outdoor Air Inlet Node, !- Source Side Inlet Node Name", + " Outdoor Air Outlet Node, !- Source Side Outlet Node Name", + " , !- Heat Recovery Inlet Node Name", + " , !- Heat Recovery Outlet Node Name", + " , !- Companion Heat Pump Name", + " 0.005, !- Load Side Reference Flow Rate {m3/s}", + " 2, !- Source Side Reference Flow Rate {m3/s}", + " autosize, !- Heat Recovery Reference Flow Rate {m3/s}", + " 80000, !- Reference Capacity {W}", + " 3.5, !- Reference Coefficient of Performance {W/W}", + " 1.0, !- Sizing Factor", + " CapCurveFuncTemp, !- Capacity Modifier Function of Temperature Curve Name", + " EIRCurveFuncTemp, !- Electric Input to Output Ratio Modifier Function of Temperature Curve Name", + " EIRCurveFuncPLR, !- Electric Input to Output Ratio Modifier Function of Part Load Ratio Curve Name", + " 1.0, !- Heating To Cooling Capacity Sizing Ratio", + " CoolingCapacity, !- Heat Pump Sizing Method", + " Setpoint, !- Control Type", + " ConstantFlow, !- Flow Mode", + " 0.0, !- Minimum Part Load Ratio", + " -100.0, !- Minimum Source Inlet Temperature {C}", + " 100.0, !- Maximum Source Inlet Temperature {C}", + " , !- Minimum Supply Water Temperature Curve Name", + " , !- Maximum Supply Water Temperature Curve Name", + " , !- Dry Outdoor Correction Factor Curve Name", + " 10.0, !- Maximum Outdoor Dry Bulb Temperature For Defrost Operation", + " , !- Heat Pump Defrost Control", + " 0.058333, !- Heat Pump Defrost Time Period Fraction", + " , !- Defrost Energy Input Ratio Function of Temperature Curve Name", + " , !- Timed Empirical Defrost Frequency Curve Name", + " , !- Timed Empirical Defrost Heat Load Penalty Curve Name", + " , !- Timed Empirical Defrost Heat Input Energy Fraction Curve Name", + " 4.5, !- Minimum Heat Recovery Outlet Temperature {C}", + " , !- Heat Recovery Capacity Modifier Function of Temperature Curve Name", + " ; !- Heat Recovery Electric Input to Output Ratio Modifier Function of Temperature Curve Name", + + "Curve:Biquadratic,", + " CapCurveFuncTemp, !- Name", + " 1.0, !- Coefficient1 Constant", + " 0.0, !- Coefficient2 x", + " 0.0, !- Coefficient3 x**2", + " 0.0, !- Coefficient4 y", + " 0.0, !- Coefficient5 y**2", + " 0.0, !- Coefficient6 x*y", + " 5.0, !- Minimum Value of x", + " 10.0, !- Maximum Value of x", + " 24.0, !- Minimum Value of y", + " 35.0, !- Maximum Value of y", + " , !- Minimum Curve Output", + " , !- Maximum Curve Output", + " Temperature, !- Input Unit Type for X", + " Temperature, !- Input Unit Type for Y", + " Dimensionless; !- Output Unit Type", + + "Curve:Biquadratic,", + " EIRCurveFuncTemp, !- Name", + " 1.0, !- Coefficient1 Constant", + " 0.0, !- Coefficient2 x", + " 0.0, !- Coefficient3 x**2", + " 0.0, !- Coefficient4 y", + " 0.0, !- Coefficient5 y**2", + " 0.0, !- Coefficient6 x*y", + " 5.0, !- Minimum Value of x", + " 10.0, !- Maximum Value of x", + " 24.0, !- Minimum Value of y", + " 35.0, !- Maximum Value of y", + " , !- Minimum Curve Output", + " , !- Maximum Curve Output", + " Temperature, !- Input Unit Type for X", + " Temperature, !- Input Unit Type for Y", + " Dimensionless; !- Output Unit Type", + + "Curve:Quadratic,", + " EIRCurveFuncPLR, !- Name", + " 1.0, !- Coefficient1 Constant", + " 0.0, !- Coefficient2 x", + " 0.0, !- Coefficient3 x**2", + " 0.0, !- Minimum Value of x", + " 1.0; !- Maximum Value of x", + }); + + ASSERT_TRUE(process_idf(idf_objects)); + state->init_state(*state); + + // call the factory with a valid name to trigger reading inputs + EIRPlantLoopHeatPump *thisHeatingPLHP = + static_cast(EIRPlantLoopHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpEIRHeating, "HEATING COIL")); + + // the factory would've called GetOnlySingleNode for the in/out pairs on the PLHP, add another one for the loop + // outlet setpoint node + EXPECT_EQ(4, state->dataLoopNodes->NumOfNodes); + EXPECT_EQ(4, state->dataLoopNodes->Node.size()); + state->dataLoopNodes->Node.redimension(5); + state->dataLoopNodes->NodeID.redimension(5); + int constexpr loadSidePlantOutletNodeIndex = 5; + state->dataLoopNodes->NodeID(loadSidePlantOutletNodeIndex) = "HEATING LOOP SUPPLY OUTLET NODE"; + + // set up the plant loops + // first the load side + state->dataPlnt->TotNumLoops = 1; + state->dataPlnt->PlantLoop.allocate(1); + + int constexpr loadSidePlantIndex = 1; + + auto &loadSideLoop = state->dataPlnt->PlantLoop(loadSidePlantIndex); + loadSideLoop.FluidName = "WATER"; + loadSideLoop.glycol = Fluid::GetWater(*state); + loadSideLoop.LoopSide(DataPlant::LoopSideLocation::Supply).TotalBranches = 1; + loadSideLoop.LoopSide(DataPlant::LoopSideLocation::Supply).Branch.allocate(1); + loadSideLoop.LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).TotalComponents = 1; + loadSideLoop.LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp.allocate(1); + loadSideLoop.TempSetPointNodeNum = loadSidePlantOutletNodeIndex; + + auto &loadSideLoopComp = loadSideLoop.LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp(1); + loadSideLoopComp.Name = thisHeatingPLHP->name; + loadSideLoopComp.NodeNumIn = thisHeatingPLHP->loadSideNodes.inlet; + loadSideLoopComp.Type = DataPlant::PlantEquipmentType::HeatPumpEIRHeating; + loadSideLoopComp.CurOpSchemeType = DataPlant::OpScheme::CompSetPtBased; + + // do a little setup here + thisHeatingPLHP->loadSidePlantLoc.loopNum = loadSidePlantIndex; + thisHeatingPLHP->loadSidePlantLoc.loopSideNum = DataPlant::LoopSideLocation::Supply; + thisHeatingPLHP->loadSidePlantLoc.branchNum = 1; + thisHeatingPLHP->loadSidePlantLoc.compNum = 1; + + // This sets the loop, side,branch and comp on the PlantLoc + PlantUtilities::SetPlantLocationLinks(*state, thisHeatingPLHP->loadSidePlantLoc); + + // Test SingleSetPoint operation first + loadSideLoop.LoopDemandCalcScheme = DataPlant::LoopDemandCalcScheme::SingleSetPoint; + state->dataLoopNodes->Node(loadSidePlantOutletNodeIndex).TempSetPoint = 30.0; + state->dataLoopNodes->Node(loadSidePlantOutletNodeIndex).TempSetPointHi = Node::SensedNodeFlagValue; + state->dataLoopNodes->Node(loadSidePlantOutletNodeIndex).TempSetPointLo = Node::SensedNodeFlagValue; + // This is already the case, but I'm being explicit + state->dataLoopNodes->Node(thisHeatingPLHP->loadSideNodes.outlet).TempSetPoint = Node::SensedNodeFlagValue; + state->dataLoopNodes->Node(thisHeatingPLHP->loadSideNodes.outlet).TempSetPointHi = Node::SensedNodeFlagValue; + state->dataLoopNodes->Node(thisHeatingPLHP->loadSideNodes.outlet).TempSetPointLo = Node::SensedNodeFlagValue; + + // the init call expects a "from" calling point + PlantLocation myLocation = PlantLocation(loadSidePlantIndex, DataPlant::LoopSideLocation::Supply, 1, 1); + + // call for initialization, oneTimeInit only first + state->dataGlobal->BeginEnvrnFlag = false; + thisHeatingPLHP->onInitLoopEquip(*state, myLocation); + + compare_err_stream(delimited_string({ + " ** Warning ** EIRPlantLoopHeatPump : oneTimeInit: Missing temperature setpoint for Setpoint Controlled HeatPump:PlantLoop:EIR:Heating " + "name = \"HEATING COIL\"", + " ** ~~~ ** A temperature setpoint is needed at the load side outlet node, use a SetpointManager", + " ** ~~~ ** The overall loop setpoint will be assumed for the Heat Pump. The simulation continues ... ", + })); + EXPECT_TRUE(thisHeatingPLHP->SetpointSetToLoopErrDone); + EXPECT_EQ(loadSidePlantOutletNodeIndex, thisHeatingPLHP->setPointNodeNum); + + EXPECT_NEAR(30.0, thisHeatingPLHP->getLoadSideOutletSetPointTemp(*state), 0.001); + EXPECT_NEAR(30.0, state->dataLoopNodes->Node(thisHeatingPLHP->setPointNodeNum).TempSetPoint, 0.001); + EXPECT_NEAR(Node::SensedNodeFlagValue, state->dataLoopNodes->Node(thisHeatingPLHP->setPointNodeNum).TempSetPointHi, 0.001); + EXPECT_NEAR(Node::SensedNodeFlagValue, state->dataLoopNodes->Node(thisHeatingPLHP->setPointNodeNum).TempSetPointLo, 0.001); + + // test for dual setpoint operation + loadSideLoop.LoopDemandCalcScheme = DataPlant::LoopDemandCalcScheme::DualSetPointDeadBand; + state->dataLoopNodes->Node(loadSidePlantOutletNodeIndex).TempSetPoint = Node::SensedNodeFlagValue; + state->dataLoopNodes->Node(loadSidePlantOutletNodeIndex).TempSetPointHi = 30.0; + state->dataLoopNodes->Node(loadSidePlantOutletNodeIndex).TempSetPointLo = 10.0; + state->dataLoopNodes->Node(thisHeatingPLHP->loadSideNodes.outlet).TempSetPoint = Node::SensedNodeFlagValue; + state->dataLoopNodes->Node(thisHeatingPLHP->loadSideNodes.outlet).TempSetPointHi = Node::SensedNodeFlagValue; + state->dataLoopNodes->Node(thisHeatingPLHP->loadSideNodes.outlet).TempSetPointLo = Node::SensedNodeFlagValue; + + // reset the flag to force re-running oneTimeInit + thisHeatingPLHP->oneTimeInitFlag = true; + thisHeatingPLHP->SetpointSetToLoopErrDone = false; + thisHeatingPLHP->onInitLoopEquip(*state, myLocation); + + compare_err_stream(delimited_string({ + " ** Warning ** EIRPlantLoopHeatPump : oneTimeInit: Missing temperature setpoint for Setpoint Controlled HeatPump:PlantLoop:EIR:Heating " + "name = \"HEATING COIL\"", + " ** ~~~ ** A temperature setpoint is needed at the load side outlet node, use a SetpointManager", + " ** ~~~ ** The overall loop setpoint will be assumed for the Heat Pump. The simulation continues ... ", + })); + EXPECT_TRUE(thisHeatingPLHP->SetpointSetToLoopErrDone); + EXPECT_EQ(loadSidePlantOutletNodeIndex, thisHeatingPLHP->setPointNodeNum); + + EXPECT_NEAR(10.0, thisHeatingPLHP->getLoadSideOutletSetPointTemp(*state), 0.001); + EXPECT_NEAR(Node::SensedNodeFlagValue, state->dataLoopNodes->Node(thisHeatingPLHP->setPointNodeNum).TempSetPoint, 0.001); + EXPECT_NEAR(30.0, state->dataLoopNodes->Node(thisHeatingPLHP->setPointNodeNum).TempSetPointHi, 0.001); + EXPECT_NEAR(10.0, state->dataLoopNodes->Node(thisHeatingPLHP->setPointNodeNum).TempSetPointLo, 0.001); + + // validate that location work got done correctly + EXPECT_EQ(1, thisHeatingPLHP->loadSidePlantLoc.loopNum); + EXPECT_ENUM_EQ(DataPlant::LoopSideLocation::Supply, thisHeatingPLHP->loadSidePlantLoc.loopSideNum); + EXPECT_EQ(1, thisHeatingPLHP->loadSidePlantLoc.branchNum); + EXPECT_EQ(1, thisHeatingPLHP->loadSidePlantLoc.compNum); + + // now call for initialization again, for begin environment + state->dataGlobal->BeginEnvrnFlag = true; + state->dataPlnt->PlantFirstSizesOkayToFinalize = true; + thisHeatingPLHP->onInitLoopEquip(*state, myLocation); +} + +TEST_F(EnergyPlusFixture, TestSizing_FullyAutosizedCoolingWithCompanion_WaterSource) +{ + std::string const idf_objects = delimited_string({"HeatPump:PlantLoop:EIR:Cooling,", + " hp cooling side,", + " node 1,", + " node 2,", + " WaterSource,", + " node 3,", + " node 4,", + " ,", + " ,", + " hp heating side,", + " Autosize,", + " Autosize,", + ",", + " Autosize,", + " 1.0,", + " 1,", + " dummyCurve,", + " dummyCurve,", + " dummyCurve;", + "HeatPump:PlantLoop:EIR:Heating,", + " hp heating side,", + " node 5,", + " node 6,", + " WaterSource,", + " node 7,", + " node 8,", + " ,", + " ,", + " hp cooling side,", + " Autosize,", + " Autosize,", + ",", + " Autosize,", + " 1.0,", + " 1,", + " dummyCurve,", + " dummyCurve,", + " dummyCurve;", + "Curve:Linear,", + " dummyCurve,", + " 1,", + " 0,", + " 1,", + " 1;"}); + ASSERT_TRUE(process_idf(idf_objects)); + state->init_state(*state); + + // call the factory with a valid name to trigger reading inputs + EIRPlantLoopHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpEIRCooling, "HP COOLING SIDE"); + + // verify the size of the vector and the processed condition + EXPECT_EQ(2u, state->dataEIRPlantLoopHeatPump->heatPumps.size()); + + // for now we know the order is maintained, so get each heat pump object + EIRPlantLoopHeatPump *thisCoolingPLHP = &state->dataEIRPlantLoopHeatPump->heatPumps[0]; + EIRPlantLoopHeatPump *thisHeatingPLHP = &state->dataEIRPlantLoopHeatPump->heatPumps[1]; + + // validate that we have the right ones + EXPECT_EQ("HP COOLING SIDE", thisCoolingPLHP->name); + EXPECT_EQ("HP HEATING SIDE", thisHeatingPLHP->name); + + // We'll set up two plant loops: a load and a source loop + state->dataPlnt->TotNumLoops = 2; + state->dataPlnt->PlantLoop.allocate(state->dataPlnt->TotNumLoops); + + state->dataPlnt->PlantLoop(1).FluidName = "WATER"; + state->dataPlnt->PlantLoop(1).glycol = Fluid::GetWater(*state); + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Demand).TotalBranches = 1; + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Demand).Branch.allocate(1); + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).TotalComponents = 1; + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp.allocate(1); + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).TotalBranches = 1; + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch.allocate(1); + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).TotalComponents = 2; + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp.allocate(2); + + state->dataPlnt->PlantLoop(2).FluidName = "WATER"; + state->dataPlnt->PlantLoop(2).glycol = Fluid::GetWater(*state); + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).TotalBranches = 1; + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch.allocate(1); + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).TotalComponents = 2; + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp.allocate(2); + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Supply).TotalBranches = 1; + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Supply).Branch.allocate(1); + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).TotalComponents = 1; + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp.allocate(1); + + Real64 constexpr plantSizingLoadVolFlow = 0.01; + Real64 constexpr plantSizingLoadDeltaT = 1.0; + + state->dataSize->PlantSizData.allocate(2); + state->dataSize->PlantSizData(1).DesVolFlowRate = 0.010; + state->dataSize->PlantSizData(1).DeltaT = 1.0; + state->dataSize->PlantSizData(2).DesVolFlowRate = 0.030; + state->dataSize->PlantSizData(2).DeltaT = 1.0; + + auto &loop1supplyComponent1 = state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp(1); + auto &loop2demandComponent1 = state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp(1); + auto &loop1supplyComponent2 = state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp(2); + auto &loop2demandComponent2 = state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp(2); + + loop1supplyComponent1.Type = DataPlant::PlantEquipmentType::HeatPumpEIRHeating; + loop2demandComponent1.Type = DataPlant::PlantEquipmentType::HeatPumpEIRHeating; + loop1supplyComponent2.Type = DataPlant::PlantEquipmentType::HeatPumpEIRCooling; + loop2demandComponent2.Type = DataPlant::PlantEquipmentType::HeatPumpEIRCooling; + + loop1supplyComponent1.Name = thisHeatingPLHP->name; + loop2demandComponent1.Name = thisHeatingPLHP->name; + loop1supplyComponent2.Name = thisCoolingPLHP->name; + loop2demandComponent2.Name = thisCoolingPLHP->name; + + loop1supplyComponent1.NodeNumIn = thisHeatingPLHP->loadSideNodes.inlet; + loop2demandComponent1.NodeNumIn = thisHeatingPLHP->sourceSideNodes.inlet; + loop1supplyComponent2.NodeNumIn = thisCoolingPLHP->loadSideNodes.inlet; + loop2demandComponent2.NodeNumIn = thisCoolingPLHP->sourceSideNodes.inlet; + + // the init call expects a "from" calling point + PlantLocation myCoolingLoadLocation = PlantLocation(1, DataPlant::LoopSideLocation::Supply, 1, 1); + PlantLocation myCoolingSourceLocation = PlantLocation(2, DataPlant::LoopSideLocation::Demand, 1, 1); + PlantLocation myHeatingLoadLocation = PlantLocation(1, DataPlant::LoopSideLocation::Supply, 1, 2); + + // set a couple global flags + state->dataGlobal->BeginEnvrnFlag = true; + + // initialize so the components can find themselves on the plant + thisCoolingPLHP->onInitLoopEquip(*state, myCoolingLoadLocation); + thisHeatingPLHP->onInitLoopEquip(*state, myHeatingLoadLocation); + + state->dataPlnt->PlantFinalSizesOkayToReport = true; + state->dataPlnt->PlantFirstSizesOkayToReport = true; + state->dataPlnt->PlantFirstSizesOkayToFinalize = true; + + // assign the plant sizing data + state->dataPlnt->PlantLoop(1).PlantSizNum = 1; + state->dataPlnt->PlantLoop(2).PlantSizNum = 2; + + // The load side should be what is imposed by the plant sizing data for the design flow rate. + // The source side should be calculated based on the COP, which was set at 1.0 for convenience. + // This works out to a multiplier of 2 on the source flow rate. With the same deltaT on the design of the source + // loop, the flow rate must be twice as high. + // The source side has a slightly different set of thermal properties so the expected flow is scaled by those. + Real64 constexpr expectedLoadCp = 4197.93; + Real64 constexpr expectedLoadRho = 999.898; + Real64 constexpr expectedSourceCp = 4185.0; + Real64 constexpr expectedSourceRho = 983.2; + Real64 const expectedLoadFlow = plantSizingLoadVolFlow; + Real64 expectedCapacity = expectedLoadRho * expectedLoadFlow * expectedLoadCp * plantSizingLoadDeltaT; + Real64 expectedSourceFlow = plantSizingLoadVolFlow * 2.0; + thisCoolingPLHP->sizeLoadSide(*state); + thisCoolingPLHP->sizeSrcSideWSHP(*state); + EXPECT_NEAR(expectedLoadFlow, thisCoolingPLHP->loadSideDesignVolFlowRate, 0.0001); + EXPECT_NEAR(expectedSourceFlow, thisCoolingPLHP->sourceSideDesignVolFlowRate, 0.0001); + EXPECT_NEAR(expectedCapacity, thisCoolingPLHP->referenceCapacity, 0.0001); + + // with a sizing run complete, we can also go ahead and get the design capacities... + // they should be nonzero for the load side of things + Real64 tmpMin = -1.0, tmpMax = -1.0, tmpOpt = -1.0; + thisCoolingPLHP->getDesignCapacities(*state, myCoolingLoadLocation, tmpMax, tmpMin, tmpOpt); + EXPECT_NEAR(0.0, tmpMin, 0.001); + EXPECT_NEAR(expectedCapacity, tmpMax, 0.001); + EXPECT_NEAR(expectedCapacity, tmpOpt, 0.001); + // but always zero for the source side of things + tmpMin = -1.0, tmpMax = -1.0, tmpOpt = -1.0; + thisCoolingPLHP->getDesignCapacities(*state, myCoolingSourceLocation, tmpMax, tmpMin, tmpOpt); + EXPECT_NEAR(0.0, tmpMin, 0.001); + EXPECT_NEAR(0.0, tmpMax, 0.001); + EXPECT_NEAR(0.0, tmpOpt, 0.001); + + // we can reset things and do a few more corner cases here + + // lets just try it again but with the plant sizing data set to zero flow, it should try to use the companion + // but the companion isn't sized yet, so it should get zero conditions + thisCoolingPLHP->loadSideDesignVolFlowRate = DataSizing::AutoSize; + thisCoolingPLHP->sourceSideDesignVolFlowRate = DataSizing::AutoSize; + thisCoolingPLHP->referenceCapacity = DataSizing::AutoSize; + state->dataSize->PlantSizData(1).DesVolFlowRate = 0.0; + thisCoolingPLHP->sizeLoadSide(*state); + thisCoolingPLHP->sizeSrcSideWSHP(*state); + EXPECT_NEAR(0.0, thisCoolingPLHP->loadSideDesignVolFlowRate, 0.0001); + EXPECT_NEAR(0.0, thisCoolingPLHP->sourceSideDesignVolFlowRate, 0.0001); + EXPECT_NEAR(0.0, thisCoolingPLHP->referenceCapacity, 0.0001); + + // but now let's try to size the heating coil, which will try to use the cooling coil's sized data + thisCoolingPLHP->loadSideDesignVolFlowRate = expectedLoadFlow; + thisCoolingPLHP->sourceSideDesignVolFlowRate = expectedSourceFlow; + thisCoolingPLHP->referenceCapacity = expectedCapacity; + Real64 const designSourceSideHeatTransfer = expectedCapacity * (1 - 1 / thisHeatingPLHP->referenceCOP); + expectedSourceFlow = designSourceSideHeatTransfer / (state->dataSize->PlantSizData(2).DeltaT * expectedSourceCp * expectedSourceRho); + expectedCapacity = expectedLoadRho * expectedLoadFlow * expectedLoadCp * plantSizingLoadDeltaT; + thisHeatingPLHP->sizeLoadSide(*state); + thisHeatingPLHP->sizeSrcSideWSHP(*state); + EXPECT_NEAR(expectedLoadFlow, thisHeatingPLHP->loadSideDesignVolFlowRate, 0.0001); + EXPECT_NEAR(expectedSourceFlow, thisHeatingPLHP->sourceSideDesignVolFlowRate, 0.0001); + EXPECT_NEAR(expectedCapacity, thisHeatingPLHP->referenceCapacity, 0.0001); +} + +TEST_F(EnergyPlusFixture, TestSizing_FullyHardsizedHeatingWithCompanion) +{ + std::string const idf_objects = delimited_string({"HeatPump:PlantLoop:EIR:Cooling,", + " hp cooling side,", + " node 1,", + " node 2,", + " WaterSource,", + " node 3,", + " node 4,", + " ,", + " ,", + " hp heating side,", + " 0.01,", + " 0.02,", + " ,", + " 1200,", + " 1.0,", + " 1,", + " dummyCurve,", + " dummyCurve,", + " dummyCurve;", + "HeatPump:PlantLoop:EIR:Heating,", + " hp heating side,", + " node 5,", + " node 6,", + " WaterSource,", + " node 7,", + " node 8,", + " ,", + " ,", + " hp cooling side,", + " 0.01,", + " 0.02,", + " ,", + " 1200,", + " 1.0,", + " 1,", + " dummyCurve,", + " dummyCurve,", + " dummyCurve;", + "Curve:Linear,", + " dummyCurve,", + " 1,", + " 0,", + " 1,", + " 1;"}); + ASSERT_TRUE(process_idf(idf_objects)); + state->init_state(*state); + + // call the factory with a valid name to trigger reading inputs + EIRPlantLoopHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpEIRCooling, "HP COOLING SIDE"); + + // verify the size of the vector and the processed condition + EXPECT_EQ(2u, state->dataEIRPlantLoopHeatPump->heatPumps.size()); + + // for now we know the order is maintained, so get each heat pump object + EIRPlantLoopHeatPump *thisCoolingPLHP = &state->dataEIRPlantLoopHeatPump->heatPumps[0]; + EIRPlantLoopHeatPump *thisHeatingPLHP = &state->dataEIRPlantLoopHeatPump->heatPumps[1]; + + // validate that we have the right ones + EXPECT_EQ("HP COOLING SIDE", thisCoolingPLHP->name); + EXPECT_EQ("HP HEATING SIDE", thisHeatingPLHP->name); + + // We'll set up two plant loops: a load and a source loop + state->dataPlnt->TotNumLoops = 2; + state->dataPlnt->PlantLoop.allocate(state->dataPlnt->TotNumLoops); + + state->dataPlnt->PlantLoop(1).FluidName = "WATER"; + state->dataPlnt->PlantLoop(1).glycol = Fluid::GetWater(*state); + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Demand).TotalBranches = 1; + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Demand).Branch.allocate(1); + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).TotalComponents = 1; + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp.allocate(1); + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).TotalBranches = 1; + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch.allocate(1); + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).TotalComponents = 2; + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp.allocate(2); + state->dataPlnt->PlantLoop(1).PlantSizNum = 1; + + state->dataPlnt->PlantLoop(2).FluidName = "WATER"; + state->dataPlnt->PlantLoop(2).glycol = Fluid::GetWater(*state); + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).TotalBranches = 1; + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch.allocate(1); + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).TotalComponents = 2; + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp.allocate(2); + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Supply).TotalBranches = 1; + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Supply).Branch.allocate(1); + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).TotalComponents = 1; + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp.allocate(1); + state->dataPlnt->PlantLoop(2).PlantSizNum = 2; + + state->dataSize->PlantSizData.allocate(2); + state->dataSize->PlantSizData(1).DesVolFlowRate = 0.020; + state->dataSize->PlantSizData(1).DeltaT = 1.0; + state->dataSize->PlantSizData(2).DesVolFlowRate = 0.030; + state->dataSize->PlantSizData(2).DeltaT = 1.0; + + auto &loop1supplyComponent1 = state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp(1); + auto &loop2demandComponent1 = state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp(1); + auto &loop1supplyComponent2 = state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp(2); + auto &loop2demandComponent2 = state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp(2); + + loop1supplyComponent1.Type = DataPlant::PlantEquipmentType::HeatPumpEIRHeating; + loop2demandComponent1.Type = DataPlant::PlantEquipmentType::HeatPumpEIRHeating; + loop1supplyComponent2.Type = DataPlant::PlantEquipmentType::HeatPumpEIRCooling; + loop2demandComponent2.Type = DataPlant::PlantEquipmentType::HeatPumpEIRCooling; + + loop1supplyComponent1.Name = thisHeatingPLHP->name; + loop2demandComponent1.Name = thisHeatingPLHP->name; + loop1supplyComponent2.Name = thisCoolingPLHP->name; + loop2demandComponent2.Name = thisCoolingPLHP->name; + + loop1supplyComponent1.NodeNumIn = thisHeatingPLHP->loadSideNodes.inlet; + loop2demandComponent1.NodeNumIn = thisHeatingPLHP->sourceSideNodes.inlet; + loop1supplyComponent2.NodeNumIn = thisCoolingPLHP->loadSideNodes.inlet; + loop2demandComponent2.NodeNumIn = thisCoolingPLHP->sourceSideNodes.inlet; + + // the init call expects a "from" calling point + PlantLocation myLoadLocation = PlantLocation(1, DataPlant::LoopSideLocation::Supply, 1, 1); + + // set a couple global flags + state->dataGlobal->BeginEnvrnFlag = true; + state->dataGlobal->DisplayExtraWarnings = true; + + // initialize so the components can find themselves on the plant + thisHeatingPLHP->onInitLoopEquip(*state, myLoadLocation); + + state->dataPlnt->PlantFinalSizesOkayToReport = true; + state->dataPlnt->PlantFirstSizesOkayToReport = true; + state->dataPlnt->PlantFirstSizesOkayToFinalize = true; + + // The values really should just come out all as the hard-sized values, this just makes sure that function didn't + // botch something up. + thisHeatingPLHP->sizeLoadSide(*state); + thisHeatingPLHP->sizeSrcSideWSHP(*state); + EXPECT_NEAR(0.01, thisHeatingPLHP->loadSideDesignVolFlowRate, 0.0001); + EXPECT_NEAR(0.02, thisHeatingPLHP->sourceSideDesignVolFlowRate, 0.0001); + EXPECT_NEAR(1200, thisHeatingPLHP->referenceCapacity, 0.0001); + + // Call it again, but this time with PlantSizing on, it should come out the same again + state->dataGlobal->DoPlantSizing = true; + thisHeatingPLHP->sizeLoadSide(*state); + thisHeatingPLHP->sizeSrcSideWSHP(*state); + EXPECT_NEAR(0.01, thisHeatingPLHP->loadSideDesignVolFlowRate, 0.0001); + EXPECT_NEAR(0.02, thisHeatingPLHP->sourceSideDesignVolFlowRate, 0.0001); + EXPECT_NEAR(1200, thisHeatingPLHP->referenceCapacity, 0.0001); +} + +TEST_F(EnergyPlusFixture, TestSizing_WithCompanionNoPlantSizing) +{ + std::string const idf_objects = delimited_string({"HeatPump:PlantLoop:EIR:Cooling,", + " hp cooling side,", + " node 1,", + " node 2,", + " WaterSource,", + " node 3,", + " node 4,", + " ,", + " ,", + " hp heating side,", + " Autosize,", + " Autosize,", + " ,", + " Autosize,", + " 1.0,", + " 1,", + " dummyCurve,", + " dummyCurve,", + " dummyCurve;", + "HeatPump:PlantLoop:EIR:Heating,", + " hp heating side,", + " node 5,", + " node 6,", + " WaterSource,", + " node 7,", + " node 8,", + " ,", + " ,", + " hp cooling side,", + " Autosize,", + " Autosize,", + " ,", + " Autosize,", + " 1.0,", + " 1,", + " dummyCurve,", + " dummyCurve,", + " dummyCurve;", + "Curve:Linear,", + " dummyCurve,", + " 1,", + " 0,", + " 1,", + " 1;"}); + ASSERT_TRUE(process_idf(idf_objects)); + state->init_state(*state); + + // call the factory with a valid name to trigger reading inputs + EIRPlantLoopHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpEIRCooling, "HP COOLING SIDE"); + + // verify the size of the vector and the processed condition + EXPECT_EQ(2u, state->dataEIRPlantLoopHeatPump->heatPumps.size()); + + // for now we know the order is maintained, so get each heat pump object + EIRPlantLoopHeatPump *thisCoolingPLHP = &state->dataEIRPlantLoopHeatPump->heatPumps[0]; + EIRPlantLoopHeatPump *thisHeatingPLHP = &state->dataEIRPlantLoopHeatPump->heatPumps[1]; + + // validate that we have the right ones + EXPECT_EQ("HP COOLING SIDE", thisCoolingPLHP->name); + EXPECT_EQ("HP HEATING SIDE", thisHeatingPLHP->name); + + // We'll set up two plant loops: a load and a source loop + state->dataPlnt->TotNumLoops = 2; + state->dataPlnt->PlantLoop.allocate(state->dataPlnt->TotNumLoops); + + state->dataPlnt->PlantLoop(1).FluidName = "WATER"; + state->dataPlnt->PlantLoop(1).glycol = Fluid::GetWater(*state); + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Demand).TotalBranches = 1; + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Demand).Branch.allocate(1); + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).TotalComponents = 1; + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp.allocate(1); + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).TotalBranches = 1; + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch.allocate(1); + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).TotalComponents = 2; + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp.allocate(2); + + state->dataPlnt->PlantLoop(2).FluidName = "WATER"; + state->dataPlnt->PlantLoop(2).glycol = Fluid::GetWater(*state); + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).TotalBranches = 1; + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch.allocate(1); + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).TotalComponents = 2; + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp.allocate(2); + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Supply).TotalBranches = 1; + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Supply).Branch.allocate(1); + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).TotalComponents = 1; + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp.allocate(1); + + auto &loop1supplyComponent1 = state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp(1); + auto &loop2demandComponent1 = state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp(1); + auto &loop1supplyComponent2 = state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp(2); + auto &loop2demandComponent2 = state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp(2); + + loop1supplyComponent1.Type = DataPlant::PlantEquipmentType::HeatPumpEIRHeating; + loop2demandComponent1.Type = DataPlant::PlantEquipmentType::HeatPumpEIRHeating; + loop1supplyComponent2.Type = DataPlant::PlantEquipmentType::HeatPumpEIRCooling; + loop2demandComponent2.Type = DataPlant::PlantEquipmentType::HeatPumpEIRCooling; + + loop1supplyComponent1.Name = thisHeatingPLHP->name; + loop2demandComponent1.Name = thisHeatingPLHP->name; + loop1supplyComponent2.Name = thisCoolingPLHP->name; + loop2demandComponent2.Name = thisCoolingPLHP->name; + + loop1supplyComponent1.NodeNumIn = thisHeatingPLHP->loadSideNodes.inlet; + loop2demandComponent1.NodeNumIn = thisHeatingPLHP->sourceSideNodes.inlet; + loop1supplyComponent2.NodeNumIn = thisCoolingPLHP->loadSideNodes.inlet; + loop2demandComponent2.NodeNumIn = thisCoolingPLHP->sourceSideNodes.inlet; + + // the init call expects a "from" calling point + PlantLocation myCoolingLoadLocation = PlantLocation(1, DataPlant::LoopSideLocation::Supply, 1, 1); + PlantLocation myHeatingLoadLocation = PlantLocation(1, DataPlant::LoopSideLocation::Supply, 1, 2); + + // set a couple global flags + state->dataGlobal->BeginEnvrnFlag = true; + + // initialize so the components can find themselves on the plant + thisCoolingPLHP->onInitLoopEquip(*state, myCoolingLoadLocation); + thisHeatingPLHP->onInitLoopEquip(*state, myHeatingLoadLocation); + + state->dataPlnt->PlantFinalSizesOkayToReport = true; + state->dataPlnt->PlantFirstSizesOkayToReport = true; + state->dataPlnt->PlantFirstSizesOkayToFinalize = true; + + // let's just fake that the companion coil already got autosized properly + thisHeatingPLHP->loadSideDesignVolFlowRate = 0.1; + thisHeatingPLHP->sourceSideDesignVolFlowRate = 0.2; + thisHeatingPLHP->referenceCapacity = 1000.0; + + // With no plant sizing, it will try to use the autosized companion instead + // the load flow should be the companion load flow + // with no source plant sizing, the source flow will actually work out to be the same as the load flow (not the source flow) + // the capacity will be the companion capacity + thisCoolingPLHP->sizeLoadSide(*state); + thisCoolingPLHP->sizeSrcSideWSHP(*state); + EXPECT_NEAR(0.1, thisCoolingPLHP->loadSideDesignVolFlowRate, 0.0001); + EXPECT_NEAR(0.1, thisCoolingPLHP->sourceSideDesignVolFlowRate, 0.0001); + EXPECT_NEAR(1000.0, thisCoolingPLHP->referenceCapacity, 0.0001); +} + +TEST_F(EnergyPlusFixture, TestSizing_NoCompanionNoPlantSizingError) +{ + std::string const idf_objects = delimited_string({"HeatPump:PlantLoop:EIR:Heating,", + " hp heating side,", + " node 5,", + " node 6,", + " WaterSource,", + " node 7,", + " node 8,", + " ,", + " ,", + " ,", + " Autosize,", + " Autosize,", + " ,", + " Autosize,", + " 1.0,", + " 1,", + " dummyCurve,", + " dummyCurve,", + " dummyCurve;", + "Curve:Linear,", + " dummyCurve,", + " 1,", + " 0,", + " 1,", + " 1;"}); + ASSERT_TRUE(process_idf(idf_objects)); + state->init_state(*state); + + // call the factory with a valid name to trigger reading inputs + EIRPlantLoopHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpEIRHeating, "HP HEATING SIDE"); + + // verify the size of the vector and the processed condition + EXPECT_EQ(1u, state->dataEIRPlantLoopHeatPump->heatPumps.size()); + + // for now we know the order is maintained, so get each heat pump object + EIRPlantLoopHeatPump *thisHeatingPLHP = &state->dataEIRPlantLoopHeatPump->heatPumps[0]; + + // validate that we have the right ones + EXPECT_EQ("HP HEATING SIDE", thisHeatingPLHP->name); + + // We'll set up two plant loops: a load and a source loop + state->dataPlnt->TotNumLoops = 2; + state->dataPlnt->PlantLoop.allocate(state->dataPlnt->TotNumLoops); + + state->dataPlnt->PlantLoop(1).FluidName = "WATER"; + state->dataPlnt->PlantLoop(1).glycol = Fluid::GetWater(*state); + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Demand).TotalBranches = 1; + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Demand).Branch.allocate(1); + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).TotalComponents = 1; + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp.allocate(1); + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).TotalBranches = 1; + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch.allocate(1); + 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); + + state->dataPlnt->PlantLoop(2).FluidName = "WATER"; + state->dataPlnt->PlantLoop(2).glycol = Fluid::GetWater(*state); + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).TotalBranches = 1; + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch.allocate(1); + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).TotalComponents = 1; + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp.allocate(1); + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Supply).TotalBranches = 1; + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Supply).Branch.allocate(1); + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).TotalComponents = 1; + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp.allocate(1); + + auto &loop1supplyComponent1 = state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp(1); + auto &loop2demandComponent1 = state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp(1); + + loop1supplyComponent1.Type = DataPlant::PlantEquipmentType::HeatPumpEIRHeating; + loop2demandComponent1.Type = DataPlant::PlantEquipmentType::HeatPumpEIRHeating; + + loop1supplyComponent1.Name = thisHeatingPLHP->name; + loop2demandComponent1.Name = thisHeatingPLHP->name; + + loop1supplyComponent1.NodeNumIn = thisHeatingPLHP->loadSideNodes.inlet; + loop2demandComponent1.NodeNumIn = thisHeatingPLHP->sourceSideNodes.inlet; + + // the init call expects a "from" calling point + PlantLocation myHeatingLoadLocation = PlantLocation(1, DataPlant::LoopSideLocation::Supply, 1, 1); + + // set a couple global flags + state->dataGlobal->BeginEnvrnFlag = true; + + // initialize so the components can find themselves on the plant + thisHeatingPLHP->onInitLoopEquip(*state, myHeatingLoadLocation); + + state->dataPlnt->PlantFinalSizesOkayToReport = true; + state->dataPlnt->PlantFirstSizesOkayToReport = true; + state->dataPlnt->PlantFirstSizesOkayToFinalize = true; + + // with no plant sizing available and no companion coil to size from, it should throw a fatal + EXPECT_THROW(thisHeatingPLHP->sizeLoadSide(*state), std::runtime_error); +} + +TEST_F(EnergyPlusFixture, TestSizing_NoCompanionNoPlantSizingHardSized) +{ + std::string const idf_objects = delimited_string({"HeatPump:PlantLoop:EIR:Heating,", + " hp heating side,", + " node 5,", + " node 6,", + " WaterSource,", + " node 7,", + " node 8,", + " ,", + " ,", + " ,", + " 0.1,", + " 0.1,", + " ,", + " 1000,", + " 1.0,", + " 1,", + " dummyCurve,", + " dummyCurve,", + " dummyCurve;", + "Curve:Linear,", + " dummyCurve,", + " 1,", + " 0,", + " 1,", + " 1;"}); + ASSERT_TRUE(process_idf(idf_objects)); + state->init_state(*state); + + // call the factory with a valid name to trigger reading inputs + EIRPlantLoopHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpEIRHeating, "HP HEATING SIDE"); + + // verify the size of the vector and the processed condition + EXPECT_EQ(1u, state->dataEIRPlantLoopHeatPump->heatPumps.size()); + + // for now we know the order is maintained, so get each heat pump object + EIRPlantLoopHeatPump *thisHeatingPLHP = &state->dataEIRPlantLoopHeatPump->heatPumps[0]; + + // validate that we have the right ones + EXPECT_EQ("HP HEATING SIDE", thisHeatingPLHP->name); + + // We'll set up two plant loops: a load and a source loop + state->dataPlnt->TotNumLoops = 2; + state->dataPlnt->PlantLoop.allocate(state->dataPlnt->TotNumLoops); + + state->dataPlnt->PlantLoop(1).FluidName = "WATER"; + state->dataPlnt->PlantLoop(1).glycol = Fluid::GetWater(*state); + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Demand).TotalBranches = 1; + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Demand).Branch.allocate(1); + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).TotalComponents = 1; + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp.allocate(1); + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).TotalBranches = 1; + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch.allocate(1); + 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); + + state->dataPlnt->PlantLoop(2).FluidName = "WATER"; + state->dataPlnt->PlantLoop(2).glycol = Fluid::GetWater(*state); + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).TotalBranches = 1; + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch.allocate(1); + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).TotalComponents = 1; + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp.allocate(1); + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Supply).TotalBranches = 1; + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Supply).Branch.allocate(1); + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).TotalComponents = 1; + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp.allocate(1); + + auto &loop1supplyComponent1 = state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp(1); + auto &loop2demandComponent1 = state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp(1); + + loop1supplyComponent1.Type = DataPlant::PlantEquipmentType::HeatPumpEIRHeating; + loop2demandComponent1.Type = DataPlant::PlantEquipmentType::HeatPumpEIRHeating; + + loop1supplyComponent1.Name = thisHeatingPLHP->name; + loop2demandComponent1.Name = thisHeatingPLHP->name; + + loop1supplyComponent1.NodeNumIn = thisHeatingPLHP->loadSideNodes.inlet; + loop2demandComponent1.NodeNumIn = thisHeatingPLHP->sourceSideNodes.inlet; + + // the init call expects a "from" calling point + PlantLocation myHeatingLoadLocation = PlantLocation(1, DataPlant::LoopSideLocation::Supply, 1, 1); + + // set a couple global flags + state->dataGlobal->BeginEnvrnFlag = true; + + // initialize so the components can find themselves on the plant + thisHeatingPLHP->onInitLoopEquip(*state, myHeatingLoadLocation); + + state->dataPlnt->PlantFinalSizesOkayToReport = true; + state->dataPlnt->PlantFirstSizesOkayToReport = true; + state->dataPlnt->PlantFirstSizesOkayToFinalize = true; + + // this should report out to the sizing output, but just the user defined stuff + thisHeatingPLHP->sizeLoadSide(*state); + thisHeatingPLHP->sizeSrcSideWSHP(*state); + EXPECT_NEAR(0.1, thisHeatingPLHP->loadSideDesignVolFlowRate, 0.0001); + EXPECT_NEAR(0.1, thisHeatingPLHP->sourceSideDesignVolFlowRate, 0.0001); + EXPECT_NEAR(1000, thisHeatingPLHP->referenceCapacity, 0.0001); +} + +TEST_F(EnergyPlusFixture, CoolingOutletSetpointWorker) +{ + std::string const idf_objects = delimited_string({"HeatPump:PlantLoop:EIR:Cooling,", + " hp cooling side,", + " node 1,", + " node 2,", + " WaterSource,", + " node 3,", + " node 4,", + " ,", + " ,", + " ,", + " 0.001,", + " 0.001,", + " ,", + " 1000,", + " 3.14,", + " ,", + " dummyCurve,", + " dummyCurve,", + " dummyCurve;", + "Curve:Linear,", + " dummyCurve,", + " 1,", + " 0,", + " 1,", + " 1;"}); + ASSERT_TRUE(process_idf(idf_objects)); + state->init_state(*state); + + // set up the plant loops + // first the load side + state->dataPlnt->TotNumLoops = 1; + state->dataPlnt->PlantLoop.allocate(1); + auto &PLHPPlantLoadSideLoop = state->dataPlnt->PlantLoop(1); + + state->dataPlnt->PlantLoop(1).FluidName = "WATER"; + state->dataPlnt->PlantLoop(1).glycol = Fluid::GetWater(*state); + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).TotalBranches = 1; + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch.allocate(1); + 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 &PLHPPlantLoadSideComp = state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp(1); + PLHPPlantLoadSideComp.Type = DataPlant::PlantEquipmentType::HeatPumpEIRCooling; + + // call the factory with a valid name to trigger reading inputs + EIRPlantLoopHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpEIRCooling, "HP COOLING SIDE"); + + // verify the size of the vector and the processed condition + EXPECT_EQ(1u, state->dataEIRPlantLoopHeatPump->heatPumps.size()); + + // for now we know the order is maintained, so get each heat pump object + EIRPlantLoopHeatPump *thisCoolingPLHP = &state->dataEIRPlantLoopHeatPump->heatPumps[0]; + + // do a little setup here + thisCoolingPLHP->loadSidePlantLoc.loopNum = 1; + thisCoolingPLHP->loadSidePlantLoc.loopSideNum = DataPlant::LoopSideLocation::Supply; + thisCoolingPLHP->loadSidePlantLoc.branchNum = 1; + thisCoolingPLHP->loadSidePlantLoc.compNum = 1; + PlantUtilities::SetPlantLocationLinks(*state, thisCoolingPLHP->loadSidePlantLoc); + thisCoolingPLHP->loadSideNodes.outlet = 1; + + // the factory would've called GetOnlySingleNode for the in/out pairs on the PLHP, add another one for the loop + // outlet setpoint node + state->dataLoopNodes->Node.allocate(5); + PLHPPlantLoadSideLoop.TempSetPointNodeNum = 5; + + // set up the plant setpoint conditions and test for single setpoint operation + PLHPPlantLoadSideLoop.LoopDemandCalcScheme = DataPlant::LoopDemandCalcScheme::SingleSetPoint; + PLHPPlantLoadSideComp.CurOpSchemeType = DataPlant::OpScheme::CompSetPtBased; + // Normally done by oneTimeInit + thisCoolingPLHP->setPointNodeNum = thisCoolingPLHP->loadSideNodes.outlet; + state->dataLoopNodes->Node(thisCoolingPLHP->setPointNodeNum).TempSetPoint = 3.141; + state->dataLoopNodes->Node(5).TempSetPoint = 2.718; + EXPECT_NEAR(3.141, thisCoolingPLHP->getLoadSideOutletSetPointTemp(*state), 0.001); + PLHPPlantLoadSideComp.CurOpSchemeType = DataPlant::OpScheme::CoolingRB; + EXPECT_NEAR(2.718, thisCoolingPLHP->getLoadSideOutletSetPointTemp(*state), 0.001); + + // test for dual setpoint operation + PLHPPlantLoadSideLoop.LoopDemandCalcScheme = DataPlant::LoopDemandCalcScheme::DualSetPointDeadBand; + PLHPPlantLoadSideComp.CurOpSchemeType = DataPlant::OpScheme::CompSetPtBased; + state->dataLoopNodes->Node(thisCoolingPLHP->loadSideNodes.outlet).TempSetPointHi = 6.282; + state->dataLoopNodes->Node(5).TempSetPointHi = 5.436; + EXPECT_NEAR(6.282, thisCoolingPLHP->getLoadSideOutletSetPointTemp(*state), 0.001); + PLHPPlantLoadSideComp.CurOpSchemeType = DataPlant::OpScheme::CoolingRB; + EXPECT_NEAR(5.436, thisCoolingPLHP->getLoadSideOutletSetPointTemp(*state), 0.001); +} + +TEST_F(EnergyPlusFixture, HeatingOutletSetpointWorker) +{ + std::string const idf_objects = delimited_string({"HeatPump:PlantLoop:EIR:Heating,", + " hp heating side,", + " node 1,", + " node 2,", + " WaterSource,", + " node 3,", + " node 3,", + " ,", + " ,", + " ,", + " 0.001,", + " 0.001,", + " ,", + " 1000,", + " 3.14,", + " ,", + " dummyCurve,", + " dummyCurve,", + " dummyCurve;", + "Curve:Linear,", + " dummyCurve,", + " 1,", + " 0,", + " 1,", + " 1;"}); + ASSERT_TRUE(process_idf(idf_objects)); + state->init_state(*state); + + // set up the plant loops + // first the load side + state->dataPlnt->TotNumLoops = 1; + state->dataPlnt->PlantLoop.allocate(1); + auto &PLHPPlantLoadSideLoop = state->dataPlnt->PlantLoop(1); + + state->dataPlnt->PlantLoop(1).FluidName = "WATER"; + state->dataPlnt->PlantLoop(1).glycol = Fluid::GetWater(*state); + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).TotalBranches = 1; + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch.allocate(1); + 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 &PLHPPlantLoadSideComp = state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp(1); + PLHPPlantLoadSideComp.Type = DataPlant::PlantEquipmentType::HeatPumpEIRHeating; + + // call the factory with a valid name to trigger reading inputs + EIRPlantLoopHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpEIRHeating, "HP HEATING SIDE"); + + // for now we know the order is maintained, so get each heat pump object + EIRPlantLoopHeatPump *thisHeatingPLHP = &state->dataEIRPlantLoopHeatPump->heatPumps[0]; + + // do a little setup here + thisHeatingPLHP->loadSidePlantLoc.loopNum = 1; + thisHeatingPLHP->loadSidePlantLoc.loopSideNum = DataPlant::LoopSideLocation::Supply; + thisHeatingPLHP->loadSidePlantLoc.branchNum = 1; + thisHeatingPLHP->loadSidePlantLoc.compNum = 1; + PlantUtilities::SetPlantLocationLinks(*state, thisHeatingPLHP->loadSidePlantLoc); + + thisHeatingPLHP->loadSideNodes.outlet = 1; + + // the factory would've called GetOnlySingleNode for the in/out pairs on the PLHP, add another one for the loop + // outlet setpoint node + state->dataLoopNodes->Node.allocate(5); + PLHPPlantLoadSideLoop.TempSetPointNodeNum = 5; + + // test for dual setpoint operation + PLHPPlantLoadSideLoop.LoopDemandCalcScheme = DataPlant::LoopDemandCalcScheme::DualSetPointDeadBand; + PLHPPlantLoadSideComp.CurOpSchemeType = DataPlant::OpScheme::CompSetPtBased; + // Normally done by oneTimeInit + thisHeatingPLHP->setPointNodeNum = thisHeatingPLHP->loadSideNodes.outlet; + state->dataLoopNodes->Node(thisHeatingPLHP->setPointNodeNum).TempSetPointHi = 30.0; + state->dataLoopNodes->Node(thisHeatingPLHP->setPointNodeNum).TempSetPointLo = 10.0; + state->dataLoopNodes->Node(5).TempSetPointHi = 30.0; + state->dataLoopNodes->Node(5).TempSetPointLo = 12.0; + EXPECT_NEAR(10.0, thisHeatingPLHP->getLoadSideOutletSetPointTemp(*state), 0.001); + PLHPPlantLoadSideComp.CurOpSchemeType = DataPlant::OpScheme::HeatingRB; + EXPECT_NEAR(12.0, thisHeatingPLHP->getLoadSideOutletSetPointTemp(*state), 0.001); +} + +TEST_F(EnergyPlusFixture, Initialization2_WaterSource) +{ + std::string const idf_objects = delimited_string({"HeatPump:PlantLoop:EIR:Cooling,", + " hp cooling side,", + " node 1,", + " node 2,", + " WaterSource,", + " node 3,", + " node 4,", + " ,", + " ,", + " ,", + " 0.001,", + " 0.001,", + " ,", + " 1000,", + " 3.14,", + " ,", + " dummyCurve,", + " dummyCurve,", + " dummyCurve;", + "Curve:Linear,", + " dummyCurve,", + " 1,", + " 0,", + " 1,", + " 1;"}); + ASSERT_TRUE(process_idf(idf_objects)); + state->init_state(*state); + + bool firstHVACIteration = true; + // set up the plant loops + // first the load side + state->dataPlnt->TotNumLoops = 2; + state->dataPlnt->PlantLoop.allocate(2); + + state->dataPlnt->PlantLoop(1).FluidName = "WATER"; + state->dataPlnt->PlantLoop(1).glycol = Fluid::GetWater(*state); + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).TotalBranches = 1; + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch.allocate(1); + 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 &PLHPPlantLoadSideComp = state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp(1); + PLHPPlantLoadSideComp.Type = DataPlant::PlantEquipmentType::HeatPumpEIRCooling; + // then the source side + + state->dataPlnt->PlantLoop(2).FluidName = "WATER"; + state->dataPlnt->PlantLoop(2).glycol = Fluid::GetWater(*state); + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).TotalBranches = 1; + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch.allocate(1); + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).TotalComponents = 1; + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp.allocate(1); + auto &PLHPPlantLoadSourceComp = state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp(1); + PLHPPlantLoadSourceComp.Type = DataPlant::PlantEquipmentType::HeatPumpEIRCooling; + + // the init call expects a "from" calling point + PlantLocation myLocation = PlantLocation(1, DataPlant::LoopSideLocation::Supply, 1, 1); + + // call the factory with a valid name to trigger reading inputs + EIRPlantLoopHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpEIRCooling, "HP COOLING SIDE"); + + // verify the size of the vector and the processed condition + EXPECT_EQ(1u, state->dataEIRPlantLoopHeatPump->heatPumps.size()); + + // for now we know the order is maintained, so get each heat pump object + EIRPlantLoopHeatPump *thisCoolingPLHP = &state->dataEIRPlantLoopHeatPump->heatPumps[0]; + + // do a bit of extra wiring up to the plant + PLHPPlantLoadSideComp.Name = thisCoolingPLHP->name; + PLHPPlantLoadSideComp.NodeNumIn = thisCoolingPLHP->loadSideNodes.inlet; + PLHPPlantLoadSourceComp.Name = thisCoolingPLHP->name; + PLHPPlantLoadSourceComp.NodeNumIn = thisCoolingPLHP->sourceSideNodes.inlet; + + // call for all initialization + state->dataGlobal->BeginEnvrnFlag = true; + state->dataPlnt->PlantFirstSizesOkayToFinalize = true; + thisCoolingPLHP->onInitLoopEquip(*state, myLocation); + + // call with run flag off, loose limits on node min/max + thisCoolingPLHP->running = false; + thisCoolingPLHP->setOperatingFlowRatesWSHP(*state, firstHVACIteration); + EXPECT_NEAR(0.0, thisCoolingPLHP->loadSideMassFlowRate, 0.001); + EXPECT_NEAR(0.0, thisCoolingPLHP->sourceSideMassFlowRate, 0.001); + + // call with run flag off, nonzero minimums + state->dataLoopNodes->Node(thisCoolingPLHP->loadSideNodes.inlet).MassFlowRateMinAvail = 0.1; + state->dataLoopNodes->Node(thisCoolingPLHP->sourceSideNodes.inlet).MassFlowRateMinAvail = 0.2; + thisCoolingPLHP->running = false; + thisCoolingPLHP->setOperatingFlowRatesWSHP(*state, firstHVACIteration); + EXPECT_NEAR(0.1, thisCoolingPLHP->loadSideMassFlowRate, 0.001); + EXPECT_NEAR(0.2, thisCoolingPLHP->sourceSideMassFlowRate, 0.001); + + // call with run flag off, load side flow locked + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).FlowLock = DataPlant::FlowLock::Locked; + state->dataLoopNodes->Node(thisCoolingPLHP->loadSideNodes.inlet).MassFlowRate = 0.24; + state->dataLoopNodes->Node(thisCoolingPLHP->sourceSideNodes.inlet).MassFlowRateMinAvail = 0.0; + thisCoolingPLHP->running = false; + thisCoolingPLHP->setOperatingFlowRatesWSHP(*state, firstHVACIteration); + EXPECT_NEAR(0.24, thisCoolingPLHP->loadSideMassFlowRate, 0.001); + EXPECT_NEAR(0.0, thisCoolingPLHP->sourceSideMassFlowRate, 0.001); + + // call with run flag ON, flow locked at zero on load side + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).FlowLock = DataPlant::FlowLock::Locked; + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).FlowLock = DataPlant::FlowLock::Locked; + state->dataLoopNodes->Node(thisCoolingPLHP->loadSideNodes.inlet).MassFlowRate = 0.0; + state->dataLoopNodes->Node(thisCoolingPLHP->sourceSideNodes.inlet).MassFlowRate = 0.2; + thisCoolingPLHP->running = true; + thisCoolingPLHP->setOperatingFlowRatesWSHP(*state, firstHVACIteration); + EXPECT_NEAR(0.0, thisCoolingPLHP->loadSideMassFlowRate, 0.001); + EXPECT_NEAR(0.2, thisCoolingPLHP->sourceSideMassFlowRate, 0.001); + + // call with run flag ON, flow locked at zero on source side + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).FlowLock = DataPlant::FlowLock::Locked; + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).FlowLock = DataPlant::FlowLock::Locked; + state->dataLoopNodes->Node(thisCoolingPLHP->loadSideNodes.inlet).MassFlowRate = 0.2; + state->dataLoopNodes->Node(thisCoolingPLHP->sourceSideNodes.inlet).MassFlowRate = 0.0; + thisCoolingPLHP->running = true; + thisCoolingPLHP->setOperatingFlowRatesWSHP(*state, firstHVACIteration); + EXPECT_NEAR(0.2, thisCoolingPLHP->loadSideMassFlowRate, 0.001); + EXPECT_NEAR(0.0, thisCoolingPLHP->sourceSideMassFlowRate, 0.001); + + // call with run flag ON, flow locked at zero on both sides + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).FlowLock = DataPlant::FlowLock::Locked; + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).FlowLock = DataPlant::FlowLock::Locked; + state->dataLoopNodes->Node(thisCoolingPLHP->loadSideNodes.inlet).MassFlowRate = 0.0; + state->dataLoopNodes->Node(thisCoolingPLHP->sourceSideNodes.inlet).MassFlowRate = 0.0; + thisCoolingPLHP->running = true; + thisCoolingPLHP->setOperatingFlowRatesWSHP(*state, firstHVACIteration); + EXPECT_NEAR(0.0, thisCoolingPLHP->loadSideMassFlowRate, 0.001); + EXPECT_NEAR(0.0, thisCoolingPLHP->sourceSideMassFlowRate, 0.001); + + // call with run flag ON, flow locked at nonzero both + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).FlowLock = DataPlant::FlowLock::Locked; + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).FlowLock = DataPlant::FlowLock::Locked; + state->dataLoopNodes->Node(thisCoolingPLHP->loadSideNodes.inlet).MassFlowRate = 0.14; + state->dataLoopNodes->Node(thisCoolingPLHP->sourceSideNodes.inlet).MassFlowRate = 0.13; + thisCoolingPLHP->running = true; + thisCoolingPLHP->setOperatingFlowRatesWSHP(*state, firstHVACIteration); + EXPECT_NEAR(0.14, thisCoolingPLHP->loadSideMassFlowRate, 0.001); + EXPECT_NEAR(0.13, thisCoolingPLHP->sourceSideMassFlowRate, 0.001); +} + +TEST_F(EnergyPlusFixture, OnInitLoopEquipTopologyErrorCases) +{ + std::string const idf_objects = delimited_string({"HeatPump:PlantLoop:EIR:Cooling,", + " hp cooling side,", + " node 1,", + " node 2,", + " WaterSource,", + " node 3,", + " node 4,", + " ,", + " ,", + " ,", + " 0.0001,", + " 0.0001,", + " ,", + " 1000,", + " 3.14,", + " ,", + " dummyCurve,", + " dummyCurve,", + " dummyCurve;", + "Curve:Linear,", + " dummyCurve,", + " 0.95,", + " 0,", + " 1,", + " 1;"}); + ASSERT_TRUE(process_idf(idf_objects)); + state->init_state(*state); + + // set up a couple simple plant loops with one branch per loop-side and one component per branch + state->dataPlnt->TotNumLoops = 2; + state->dataPlnt->PlantLoop.allocate(state->dataPlnt->TotNumLoops); + + state->dataPlnt->PlantLoop(1).FluidName = "WATER"; + state->dataPlnt->PlantLoop(1).glycol = Fluid::GetWater(*state); + state->dataPlnt->PlantLoop(1).LoopDemandCalcScheme = DataPlant::LoopDemandCalcScheme::SingleSetPoint; + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Demand).TotalBranches = 1; + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Demand).Branch.allocate(1); + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).TotalComponents = 1; + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp.allocate(1); + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).TotalBranches = 1; + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch.allocate(1); + 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); + + state->dataPlnt->PlantLoop(2).FluidName = "WATER"; + state->dataPlnt->PlantLoop(2).glycol = Fluid::GetWater(*state); + state->dataPlnt->PlantLoop(2).LoopDemandCalcScheme = DataPlant::LoopDemandCalcScheme::SingleSetPoint; + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).TotalBranches = 1; + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch.allocate(1); + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).TotalComponents = 1; + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp.allocate(1); + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Supply).TotalBranches = 1; + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Supply).Branch.allocate(1); + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).TotalComponents = 1; + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp.allocate(1); + auto &PLHPPlantSupplySideComp = state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp(1); + auto &PLHPPlantDemandSideComp = state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp(1); + auto &extraPLHPPlantSupplySideComp = state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp(1); + auto &extraPLHPPlantDemandSideComp = state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp(1); + PLHPPlantSupplySideComp.Type = DataPlant::PlantEquipmentType::HeatPumpEIRCooling; + PLHPPlantDemandSideComp.Type = DataPlant::PlantEquipmentType::HeatPumpEIRCooling; + extraPLHPPlantSupplySideComp.Type = DataPlant::PlantEquipmentType::HeatPumpEIRCooling; + extraPLHPPlantDemandSideComp.Type = DataPlant::PlantEquipmentType::HeatPumpEIRCooling; + + // call the factory with a valid name to trigger reading inputs + EIRPlantLoopHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpEIRCooling, "HP COOLING SIDE"); + EXPECT_EQ(1u, state->dataEIRPlantLoopHeatPump->heatPumps.size()); + EIRPlantLoopHeatPump *thisCoolingPLHP = &state->dataEIRPlantLoopHeatPump->heatPumps[0]; + + // init the plant component data with the name we have now from the factory call + PLHPPlantSupplySideComp.Name = thisCoolingPLHP->name; + PLHPPlantDemandSideComp.Name = thisCoolingPLHP->name; + + // the init call expects a "from" calling point + PlantLocation myLoadLocation = PlantLocation(1, DataPlant::LoopSideLocation::Supply, 1, 1); + + // set a couple global flags + state->dataGlobal->BeginEnvrnFlag = true; + state->dataPlnt->PlantFirstSizesOkayToFinalize = true; + + // test the case where the heat pump is connected to both the supply and demand sides of the same loop + PLHPPlantSupplySideComp.NodeNumIn = thisCoolingPLHP->loadSideNodes.inlet; + PLHPPlantDemandSideComp.NodeNumIn = thisCoolingPLHP->sourceSideNodes.inlet; + extraPLHPPlantSupplySideComp.NodeNumIn = -1; + extraPLHPPlantDemandSideComp.NodeNumIn = -1; + // call for all initialization, it should abort because the coil load and supply sides were on the same loop + EXPECT_THROW(thisCoolingPLHP->onInitLoopEquip(*state, myLoadLocation), std::runtime_error); + + // test the case where the heat pump source side cannot be found + PLHPPlantSupplySideComp.NodeNumIn = thisCoolingPLHP->loadSideNodes.inlet; + PLHPPlantDemandSideComp.NodeNumIn = -1; + extraPLHPPlantSupplySideComp.NodeNumIn = -1; + extraPLHPPlantDemandSideComp.NodeNumIn = -1; + // call for all initialization, it should abort because the coil source side inlet node is not found on plant + EXPECT_THROW(thisCoolingPLHP->onInitLoopEquip(*state, myLoadLocation), std::runtime_error); + + // test the case where the heat pump load side cannot be found + PLHPPlantSupplySideComp.NodeNumIn = -1; + PLHPPlantDemandSideComp.NodeNumIn = thisCoolingPLHP->sourceSideNodes.inlet; + extraPLHPPlantSupplySideComp.NodeNumIn = -1; + extraPLHPPlantDemandSideComp.NodeNumIn = -1; + // call for all initialization, it should abort because the coil load side inlet node is not found on plant + EXPECT_THROW(thisCoolingPLHP->onInitLoopEquip(*state, myLoadLocation), std::runtime_error); + + // test the case where the heat pump source side is found, but it's on the supply side of a loop + // still need to drop the load side onto a (extra) plant supply to trigger this condition + PLHPPlantSupplySideComp.NodeNumIn = thisCoolingPLHP->sourceSideNodes.inlet; + PLHPPlantDemandSideComp.NodeNumIn = -1; + extraPLHPPlantSupplySideComp.NodeNumIn = thisCoolingPLHP->loadSideNodes.inlet; + extraPLHPPlantDemandSideComp.NodeNumIn = -1; + // call for all initialization, it should abort because the coil source was found on a supply side + EXPECT_THROW(thisCoolingPLHP->onInitLoopEquip(*state, myLoadLocation), std::runtime_error); + + // test the case where the heat pump load side is found, but it's on the demand side of a loop + // still need to drop the source side onto a (extra) plant demand to trigger this condition + PLHPPlantSupplySideComp.NodeNumIn = -1; + PLHPPlantDemandSideComp.NodeNumIn = thisCoolingPLHP->loadSideNodes.inlet; + extraPLHPPlantSupplySideComp.NodeNumIn = -1; + extraPLHPPlantDemandSideComp.NodeNumIn = thisCoolingPLHP->sourceSideNodes.inlet; + // call for all initialization, it should abort because the coil load was found on a demand side + EXPECT_THROW(thisCoolingPLHP->onInitLoopEquip(*state, myLoadLocation), std::runtime_error); +} + +TEST_F(EnergyPlusFixture, CoolingSimulate_WaterSource) +{ + std::string const idf_objects = delimited_string({"HeatPump:PlantLoop:EIR:Cooling,", + " hp cooling side,", + " node 1,", + " node 2,", + " WaterSource,", + " node 3,", + " node 4,", + " ,", + " ,", + " ,", + " 0.0001,", + " 0.0001,", + " ,", + " 1000,", + " 3.14,", + " ,", + " dummyCurve,", + " dummyCurve,", + " dummyCurve;", + "Curve:Linear,", + " dummyCurve,", + " 0.95,", + " 0,", + " 1,", + " 1;"}); + ASSERT_TRUE(process_idf(idf_objects)); + state->init_state(*state); + + // set up the plant loops + // first the load side + state->dataPlnt->TotNumLoops = 2; + state->dataPlnt->PlantLoop.allocate(2); + + state->dataPlnt->PlantLoop(1).FluidName = "WATER"; + state->dataPlnt->PlantLoop(1).glycol = Fluid::GetWater(*state); + state->dataPlnt->PlantLoop(1).LoopDemandCalcScheme = DataPlant::LoopDemandCalcScheme::SingleSetPoint; + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).TotalBranches = 1; + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch.allocate(1); + 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 &PLHPPlantLoadSideComp = state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp(1); + PLHPPlantLoadSideComp.Type = DataPlant::PlantEquipmentType::HeatPumpEIRCooling; + PLHPPlantLoadSideComp.CurOpSchemeType = DataPlant::OpScheme::CompSetPtBased; + // then the source side + + state->dataPlnt->PlantLoop(2).FluidName = "WATER"; + state->dataPlnt->PlantLoop(2).glycol = Fluid::GetWater(*state); + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).TotalBranches = 1; + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch.allocate(1); + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).TotalComponents = 1; + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp.allocate(1); + auto &PLHPPlantLoadSourceComp = state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp(1); + PLHPPlantLoadSourceComp.Type = DataPlant::PlantEquipmentType::HeatPumpEIRCooling; + + // the init call expects a "from" calling point + PlantLocation myLoadLocation = PlantLocation(1, DataPlant::LoopSideLocation::Supply, 1, 1); + PlantLocation mySourceLocation = PlantLocation(2, DataPlant::LoopSideLocation::Demand, 1, 1); + + // call the factory with a valid name to trigger reading inputs + EIRPlantLoopHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpEIRCooling, "HP COOLING SIDE"); + + // verify the size of the vector and the processed condition + EXPECT_EQ(1u, state->dataEIRPlantLoopHeatPump->heatPumps.size()); + + // for now we know the order is maintained, so get each heat pump object + EIRPlantLoopHeatPump *thisCoolingPLHP = &state->dataEIRPlantLoopHeatPump->heatPumps[0]; + + // do a bit of extra wiring up to the plant + PLHPPlantLoadSideComp.Name = thisCoolingPLHP->name; + PLHPPlantLoadSideComp.NodeNumIn = thisCoolingPLHP->loadSideNodes.inlet; + PLHPPlantLoadSourceComp.Name = thisCoolingPLHP->name; + PLHPPlantLoadSourceComp.NodeNumIn = thisCoolingPLHP->sourceSideNodes.inlet; + + // call for all initialization + state->dataGlobal->BeginEnvrnFlag = true; + state->dataPlnt->PlantFirstSizesOkayToFinalize = true; + thisCoolingPLHP->onInitLoopEquip(*state, myLoadLocation); + + // call from load side location, firsthvac, no load, not running, verify the unit doesn't have any values lingering + thisCoolingPLHP->loadSideHeatTransfer = 1000; + thisCoolingPLHP->loadSideInletTemp = 23.0; + thisCoolingPLHP->loadSideOutletTemp = 42.0; + thisCoolingPLHP->powerUsage = 4.0; + thisCoolingPLHP->sourceSideHeatTransfer = 60.0; + thisCoolingPLHP->sourceSideInletTemp = 43.0; + thisCoolingPLHP->sourceSideOutletTemp = 83.0; + bool firstHVAC = true; + Real64 curLoad = 0.0; + bool runFlag = false; + thisCoolingPLHP->simulate(*state, myLoadLocation, firstHVAC, curLoad, runFlag); + EXPECT_NEAR(0.0, thisCoolingPLHP->loadSideHeatTransfer, 0.001); + EXPECT_NEAR(0.0, thisCoolingPLHP->sourceSideHeatTransfer, 0.001); + EXPECT_NEAR(0.0, thisCoolingPLHP->powerUsage, 0.001); + EXPECT_NEAR(thisCoolingPLHP->loadSideInletTemp, thisCoolingPLHP->loadSideOutletTemp, 0.001); + EXPECT_NEAR(thisCoolingPLHP->sourceSideInletTemp, thisCoolingPLHP->sourceSideOutletTemp, 0.001); + + // call from source side location, firsthvac, no load, not running, connected loop should be triggered to resimulate + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).SimLoopSideNeeded = false; + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).SimLoopSideNeeded = false; + thisCoolingPLHP->simulate(*state, mySourceLocation, firstHVAC, curLoad, runFlag); + EXPECT_TRUE(state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).SimLoopSideNeeded); + + thisCoolingPLHP->setPointNodeNum = thisCoolingPLHP->loadSideNodes.outlet; + + // now we can call it again from the load side, but this time there is load (still firsthvac, unit can meet load) + { + firstHVAC = true; + curLoad = -800; + runFlag = true; + Real64 constexpr expectedLoadMassFlowRate = 0.09999; + Real64 constexpr expectedCp = 4183; + Real64 constexpr specifiedLoadSetpoint = 15; + Real64 const calculatedLoadInletTemp = specifiedLoadSetpoint - curLoad / (expectedLoadMassFlowRate * expectedCp); + state->dataLoopNodes->Node(thisCoolingPLHP->setPointNodeNum).TempSetPoint = specifiedLoadSetpoint; + state->dataLoopNodes->Node(thisCoolingPLHP->loadSideNodes.inlet).Temp = calculatedLoadInletTemp; + state->dataLoopNodes->Node(thisCoolingPLHP->sourceSideNodes.inlet).Temp = 30; + thisCoolingPLHP->simulate(*state, myLoadLocation, firstHVAC, curLoad, runFlag); + // expect it to meet setpoint and have some pre-evaluated conditions + EXPECT_NEAR(specifiedLoadSetpoint, thisCoolingPLHP->loadSideOutletTemp, 0.001); + EXPECT_NEAR(-curLoad, thisCoolingPLHP->loadSideHeatTransfer, 0.001); + } + + // now we can call it again from the load side, but this time there is load (still firsthvac, unit cannot meet load) + { + firstHVAC = true; + curLoad = -1200; + Real64 availableCapacity = 950.0; + runFlag = true; + Real64 constexpr expectedLoadMassFlowRate = 0.09999; + Real64 constexpr expectedCp = 4183; + Real64 constexpr specifiedLoadSetpoint = 15; + Real64 const calculatedLoadInletTemp = specifiedLoadSetpoint - curLoad / (expectedLoadMassFlowRate * expectedCp); + state->dataLoopNodes->Node(thisCoolingPLHP->setPointNodeNum).TempSetPoint = specifiedLoadSetpoint; + state->dataLoopNodes->Node(thisCoolingPLHP->loadSideNodes.inlet).Temp = calculatedLoadInletTemp; + state->dataLoopNodes->Node(thisCoolingPLHP->sourceSideNodes.inlet).Temp = 30; + thisCoolingPLHP->simulate(*state, myLoadLocation, firstHVAC, curLoad, runFlag); + // expect it to miss setpoint and be at max capacity + EXPECT_NEAR(15.597, thisCoolingPLHP->loadSideOutletTemp, 0.001); + EXPECT_NEAR(availableCapacity, thisCoolingPLHP->loadSideHeatTransfer, 0.001); + } +} + +TEST_F(EnergyPlusFixture, HeatingSimulate_WaterSource) +{ + std::string const idf_objects = delimited_string({"HeatPump:PlantLoop:EIR:Heating,", + " hp heating side,", + " node 1,", + " node 2,", + " WaterSource,", + " node 3,", + " node 4,", + " ,", + " ,", + " ,", + " 0.0001,", + " 0.0001,", + " ,", + " 1000,", + " 3.14,", + " ,", + " dummyCurve,", + " dummyCurve,", + " dummyCurve;", + "Curve:Linear,", + " dummyCurve,", + " 0.95,", + " 0,", + " 1,", + " 1;"}); + ASSERT_TRUE(process_idf(idf_objects)); + state->init_state(*state); + + // set up the plant loops + // first the load side + state->dataPlnt->TotNumLoops = 2; + state->dataPlnt->PlantLoop.allocate(2); + + state->dataPlnt->PlantLoop(1).FluidName = "WATER"; + state->dataPlnt->PlantLoop(1).glycol = Fluid::GetWater(*state); + state->dataPlnt->PlantLoop(1).LoopDemandCalcScheme = DataPlant::LoopDemandCalcScheme::SingleSetPoint; + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).TotalBranches = 1; + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch.allocate(1); + 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 &PLHPPlantLoadSideComp = state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp(1); + PLHPPlantLoadSideComp.Type = DataPlant::PlantEquipmentType::HeatPumpEIRHeating; + PLHPPlantLoadSideComp.CurOpSchemeType = DataPlant::OpScheme::CompSetPtBased; + // then the source side + + state->dataPlnt->PlantLoop(2).FluidName = "WATER"; + state->dataPlnt->PlantLoop(2).glycol = Fluid::GetWater(*state); + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).TotalBranches = 1; + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch.allocate(1); + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).TotalComponents = 1; + state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp.allocate(1); + auto &PLHPPlantLoadSourceComp = state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp(1); + PLHPPlantLoadSourceComp.Type = DataPlant::PlantEquipmentType::HeatPumpEIRHeating; + + // the init call expects a "from" calling point + PlantLocation myLoadLocation = PlantLocation(1, DataPlant::LoopSideLocation::Supply, 1, 1); + + // call the factory with a valid name to trigger reading inputs + EIRPlantLoopHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpEIRHeating, "HP HEATING SIDE"); + + // verify the size of the vector and the processed condition + EXPECT_EQ(1u, state->dataEIRPlantLoopHeatPump->heatPumps.size()); + + // for now we know the order is maintained, so get each heat pump object + EIRPlantLoopHeatPump *thisHeatingPLHP = &state->dataEIRPlantLoopHeatPump->heatPumps[0]; + + // do a bit of extra wiring up to the plant + PLHPPlantLoadSideComp.Name = thisHeatingPLHP->name; + PLHPPlantLoadSideComp.NodeNumIn = thisHeatingPLHP->loadSideNodes.inlet; + PLHPPlantLoadSourceComp.Name = thisHeatingPLHP->name; + PLHPPlantLoadSourceComp.NodeNumIn = thisHeatingPLHP->sourceSideNodes.inlet; + + // call for all initialization + state->dataGlobal->BeginEnvrnFlag = true; + state->dataPlnt->PlantFirstSizesOkayToFinalize = true; + thisHeatingPLHP->onInitLoopEquip(*state, myLoadLocation); + + thisHeatingPLHP->setPointNodeNum = thisHeatingPLHP->loadSideNodes.outlet; + + // call it from the load side, but this time there is a negative (cooling) load - shouldn't try to run + { + bool firstHVAC = true; + Real64 curLoad = -900; + bool runFlag = true; // plant actually shouldn't do this but the component can be smart enough to handle it + Real64 constexpr specifiedLoadSetpoint = 45; + Real64 constexpr loadInletTemp = 46; + state->dataLoopNodes->Node(thisHeatingPLHP->setPointNodeNum).TempSetPoint = specifiedLoadSetpoint; + state->dataLoopNodes->Node(thisHeatingPLHP->loadSideNodes.inlet).Temp = loadInletTemp; + state->dataLoopNodes->Node(thisHeatingPLHP->sourceSideNodes.inlet).Temp = 30; + thisHeatingPLHP->simulate(*state, myLoadLocation, firstHVAC, curLoad, runFlag); + // expect it to meet setpoint and have some pre-evaluated conditions + EXPECT_NEAR(loadInletTemp, thisHeatingPLHP->loadSideOutletTemp, 0.001); + EXPECT_NEAR(0.0, thisHeatingPLHP->loadSideHeatTransfer, 0.001); + } + + // call it from the load side, but this time there is load (still firsthvac, unit can meet load) + { + bool firstHVAC = true; + Real64 curLoad = 800; + bool runFlag = true; + Real64 constexpr expectedLoadMassFlowRate = 0.09999; + Real64 constexpr expectedCp = 4180; + Real64 constexpr specifiedLoadSetpoint = 45; + Real64 const calculatedLoadInletTemp = specifiedLoadSetpoint - curLoad / (expectedLoadMassFlowRate * expectedCp); + state->dataLoopNodes->Node(thisHeatingPLHP->setPointNodeNum).TempSetPoint = specifiedLoadSetpoint; + state->dataLoopNodes->Node(thisHeatingPLHP->loadSideNodes.inlet).Temp = calculatedLoadInletTemp; + state->dataLoopNodes->Node(thisHeatingPLHP->sourceSideNodes.inlet).Temp = 30; + thisHeatingPLHP->simulate(*state, myLoadLocation, 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); + } + + // now we can call it again from the load side, but this time there is load (still firsthvac, unit cannot meet load) + { + bool firstHVAC = true; + Real64 curLoad = 1200; + Real64 availableCapacity = 950.0; + bool runFlag = true; + Real64 constexpr expectedLoadMassFlowRate = 0.09999; + Real64 constexpr expectedCp = 4180; + Real64 constexpr specifiedLoadSetpoint = 45; + Real64 const calculatedLoadInletTemp = specifiedLoadSetpoint - curLoad / (expectedLoadMassFlowRate * expectedCp); + state->dataLoopNodes->Node(thisHeatingPLHP->setPointNodeNum).TempSetPoint = specifiedLoadSetpoint; + state->dataLoopNodes->Node(thisHeatingPLHP->loadSideNodes.inlet).Temp = calculatedLoadInletTemp; + state->dataLoopNodes->Node(thisHeatingPLHP->sourceSideNodes.inlet).Temp = 30; + thisHeatingPLHP->simulate(*state, myLoadLocation, firstHVAC, curLoad, runFlag); + // expect it to miss setpoint and be at max capacity + EXPECT_NEAR(44.402, thisHeatingPLHP->loadSideOutletTemp, 0.001); + EXPECT_NEAR(availableCapacity, thisHeatingPLHP->loadSideHeatTransfer, 0.001); + } +} + +TEST_F(EnergyPlusFixture, TestConcurrentOperationChecking) +{ + state->dataEIRPlantLoopHeatPump->heatPumps.resize(4); + EIRPlantLoopHeatPump *coil1 = &state->dataEIRPlantLoopHeatPump->heatPumps[0]; + EIRPlantLoopHeatPump *coil2 = &state->dataEIRPlantLoopHeatPump->heatPumps[1]; + EIRPlantLoopHeatPump *coil3 = &state->dataEIRPlantLoopHeatPump->heatPumps[2]; + EIRPlantLoopHeatPump *coil4 = &state->dataEIRPlantLoopHeatPump->heatPumps[3]; + + // pair up the last two + coil3->companionHeatPumpCoil = coil4; + coil4->companionHeatPumpCoil = coil3; + + // set all of them to running + coil1->running = true; + coil2->running = true; + coil3->running = true; + coil4->running = true; + + // check to warn about concurrent operation + EIRPlantLoopHeatPump::checkConcurrentOperation(*state); + + // that will just add a recurring warning to the end, so to check whether + // a warning was actually made, I'll just check the warning index values + ASSERT_EQ(0, coil1->recurringConcurrentOperationWarningIndex); + ASSERT_EQ(0, coil2->recurringConcurrentOperationWarningIndex); + ASSERT_EQ(1, coil3->recurringConcurrentOperationWarningIndex); + ASSERT_EQ(1, coil4->recurringConcurrentOperationWarningIndex); +} + +TEST_F(EnergyPlusFixture, ConstructionFullObjectsHeatingAndCooling_AirSource) +{ + std::string const idf_objects = delimited_string({"HeatPump:PlantLoop:EIR:Heating,", + " hp heating side,", + " node 1,", + " node 2,", + " AirSource,", + " node 3,", + " node 4,", + " ,", + " ,", + " hp cooling side,", + " 0.001,", + " 0.001,", + " ,", + " 1000,", + " 3.14,", + " 2,", + " dummyCurve,", + " dummyCurve,", + " dummyCurve;", + "HeatPump:PlantLoop:EIR:Cooling,", + " hp cooling side,", + " node 1,", + " node 2,", + " AirSource,", + " node 3,", + " node 4,", + " ,", + " ,", + " hp heating side,", + " 0.001,", + " 0.001,", + " ,", + " 1000,", + " 3.14,", + " 2,", + " dummyCurve,", + " dummyCurve,", + " dummyCurve;", + "Curve:Linear,", + " dummyCurve,", + " 1,", + " 0,", + " 1,", + " 1;"}); + ASSERT_TRUE(process_idf(idf_objects)); + state->init_state(*state); + + // call the factory with a valid name to trigger reading inputs + EIRPlantLoopHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpEIRHeating, "HP HEATING SIDE"); + + // verify the size of the vector and the processed condition + EXPECT_EQ(2u, state->dataEIRPlantLoopHeatPump->heatPumps.size()); + + // for now we know the order is maintained, so get each heat pump object + EIRPlantLoopHeatPump *thisHeatingPLHP = &state->dataEIRPlantLoopHeatPump->heatPumps[1]; + EIRPlantLoopHeatPump *thisCoolingPLHP = &state->dataEIRPlantLoopHeatPump->heatPumps[0]; + + // validate the heating side + EXPECT_EQ("HP HEATING SIDE", thisHeatingPLHP->name); + EXPECT_ENUM_EQ(DataPlant::PlantEquipmentType::HeatPumpEIRHeating, thisHeatingPLHP->EIRHPType); + EXPECT_EQ(thisCoolingPLHP, thisHeatingPLHP->companionHeatPumpCoil); + EXPECT_EQ(1, thisHeatingPLHP->capFuncTempCurveIndex); + EXPECT_EQ(1, thisHeatingPLHP->powerRatioFuncTempCurveIndex); + EXPECT_EQ(1, thisHeatingPLHP->powerRatioFuncPLRCurveIndex); + + // validate the cooling side + EXPECT_EQ("HP COOLING SIDE", thisCoolingPLHP->name); + EXPECT_ENUM_EQ(DataPlant::PlantEquipmentType::HeatPumpEIRCooling, thisCoolingPLHP->EIRHPType); + EXPECT_EQ(thisHeatingPLHP, thisCoolingPLHP->companionHeatPumpCoil); + EXPECT_EQ(1, thisCoolingPLHP->capFuncTempCurveIndex); + EXPECT_EQ(1, thisCoolingPLHP->powerRatioFuncTempCurveIndex); + EXPECT_EQ(1, thisCoolingPLHP->powerRatioFuncPLRCurveIndex); + + // calling the factory with an invalid name or type will call ShowFatalError, which will trigger a runtime exception + EXPECT_THROW(EIRPlantLoopHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpEIRHeating, "fake"), std::runtime_error); + EXPECT_THROW(EIRPlantLoopHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpEIRCooling, "HP HEATING SIDE"), std::runtime_error); + EXPECT_THROW(EIRPlantLoopHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpEIRCooling, "fake"), std::runtime_error); + EXPECT_THROW(EIRPlantLoopHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpEIRHeating, "HP COOLING SIDE"), std::runtime_error); +} + +TEST_F(EnergyPlusFixture, CoolingSimulate_AirSource) +{ + std::string const idf_objects = delimited_string({"HeatPump:PlantLoop:EIR:Cooling,", + " hp cooling side,", + " node 1,", + " node 2,", + " AirSource,", + " node 3,", + " node 4,", + " ,", + " ,", + " ,", + " 0.0001,", + " 1,", + " ,", + " 1000,", + " 3.14,", + " ,", + " dummyCurve,", + " dummyCurve,", + " dummyCurve;", + "Curve:Linear,", + " dummyCurve,", + " 0.95,", + " 0,", + " 1,", + " 1;"}); + ASSERT_TRUE(process_idf(idf_objects)); + state->init_state(*state); + + // set up the plant loops + // first the load side + state->dataPlnt->TotNumLoops = 1; + state->dataPlnt->PlantLoop.allocate(1); + + state->dataPlnt->PlantLoop(1).FluidName = "WATER"; + state->dataPlnt->PlantLoop(1).glycol = Fluid::GetWater(*state); + state->dataPlnt->PlantLoop(1).LoopDemandCalcScheme = DataPlant::LoopDemandCalcScheme::SingleSetPoint; + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).TotalBranches = 1; + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch.allocate(1); + 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 &PLHPPlantLoadSideComp = state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp(1); + PLHPPlantLoadSideComp.Type = DataPlant::PlantEquipmentType::HeatPumpEIRCooling; + PLHPPlantLoadSideComp.CurOpSchemeType = DataPlant::OpScheme::CompSetPtBased; + + // the init call expects a "from" calling point + PlantLocation myLoadLocation = PlantLocation(1, DataPlant::LoopSideLocation::Supply, 1, 1); + + // call the factory with a valid name to trigger reading inputs + EIRPlantLoopHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpEIRCooling, "HP COOLING SIDE"); + + // verify the size of the vector and the processed condition + EXPECT_EQ(1u, state->dataEIRPlantLoopHeatPump->heatPumps.size()); + + // for now we know the order is maintained, so get each heat pump object + EIRPlantLoopHeatPump *thisCoolingPLHP = &state->dataEIRPlantLoopHeatPump->heatPumps[0]; + + // do a bit of extra wiring up to the plant + PLHPPlantLoadSideComp.Name = thisCoolingPLHP->name; + PLHPPlantLoadSideComp.NodeNumIn = thisCoolingPLHP->loadSideNodes.inlet; + + // call for all initialization + state->dataGlobal->BeginEnvrnFlag = true; + state->dataPlnt->PlantFirstSizesOkayToFinalize = true; + thisCoolingPLHP->onInitLoopEquip(*state, myLoadLocation); + thisCoolingPLHP->setPointNodeNum = thisCoolingPLHP->loadSideNodes.outlet; + + // call from load side location, firsthvac, no load, not running, verify the unit doesn't have any values lingering + thisCoolingPLHP->loadSideHeatTransfer = 1000; + thisCoolingPLHP->loadSideInletTemp = 23.0; + thisCoolingPLHP->loadSideOutletTemp = 42.0; + thisCoolingPLHP->powerUsage = 4.0; + thisCoolingPLHP->sourceSideHeatTransfer = 60.0; + thisCoolingPLHP->sourceSideInletTemp = 43.0; + thisCoolingPLHP->sourceSideOutletTemp = 83.0; + bool firstHVAC = true; + Real64 curLoad = 0.0; + bool runFlag = false; + thisCoolingPLHP->simulate(*state, myLoadLocation, firstHVAC, curLoad, runFlag); + EXPECT_NEAR(0.0, thisCoolingPLHP->loadSideHeatTransfer, 0.001); + EXPECT_NEAR(0.0, thisCoolingPLHP->sourceSideHeatTransfer, 0.001); + EXPECT_NEAR(0.0, thisCoolingPLHP->powerUsage, 0.001); + EXPECT_NEAR(thisCoolingPLHP->loadSideInletTemp, thisCoolingPLHP->loadSideOutletTemp, 0.001); + EXPECT_NEAR(thisCoolingPLHP->sourceSideInletTemp, thisCoolingPLHP->sourceSideOutletTemp, 0.001); + + // now we can call it again from the load side, but this time there is load (still firsthvac, unit can meet load) + { + firstHVAC = true; + curLoad = -800; + runFlag = true; + Real64 constexpr expectedLoadMassFlowRate = 0.09999; + Real64 constexpr expectedCp = 4183; + Real64 constexpr specifiedLoadSetpoint = 15; + Real64 const calculatedLoadInletTemp = specifiedLoadSetpoint - curLoad / (expectedLoadMassFlowRate * expectedCp); + state->dataLoopNodes->Node(thisCoolingPLHP->setPointNodeNum).TempSetPoint = specifiedLoadSetpoint; + state->dataLoopNodes->Node(thisCoolingPLHP->loadSideNodes.inlet).Temp = calculatedLoadInletTemp; + state->dataLoopNodes->Node(thisCoolingPLHP->sourceSideNodes.inlet).Temp = 30; + thisCoolingPLHP->simulate(*state, myLoadLocation, firstHVAC, curLoad, runFlag); + // expect it to meet setpoint and have some pre-evaluated conditions + EXPECT_NEAR(specifiedLoadSetpoint, thisCoolingPLHP->loadSideOutletTemp, 0.001); + EXPECT_NEAR(-curLoad, thisCoolingPLHP->loadSideHeatTransfer, 0.001); + } + + // now we can call it again from the load side, but this time there is load (still firsthvac, unit cannot meet load) + { + firstHVAC = true; + curLoad = -1200; + Real64 availableCapacity = 950.0; + runFlag = true; + Real64 constexpr expectedLoadMassFlowRate = 0.09999; + Real64 constexpr expectedCp = 4183; + Real64 constexpr specifiedLoadSetpoint = 15; + Real64 const calculatedLoadInletTemp = specifiedLoadSetpoint - curLoad / (expectedLoadMassFlowRate * expectedCp); + state->dataLoopNodes->Node(thisCoolingPLHP->setPointNodeNum).TempSetPoint = specifiedLoadSetpoint; + state->dataLoopNodes->Node(thisCoolingPLHP->loadSideNodes.inlet).Temp = calculatedLoadInletTemp; + state->dataLoopNodes->Node(thisCoolingPLHP->sourceSideNodes.inlet).Temp = 30; + thisCoolingPLHP->simulate(*state, myLoadLocation, firstHVAC, curLoad, runFlag); + // expect it to miss setpoint and be at max capacity + EXPECT_NEAR(15.597, thisCoolingPLHP->loadSideOutletTemp, 0.001); + EXPECT_NEAR(availableCapacity, thisCoolingPLHP->loadSideHeatTransfer, 0.001); + } +} + +TEST_F(EnergyPlusFixture, HeatingSimulate_AirSource) +{ + std::string const idf_objects = delimited_string({"HeatPump:PlantLoop:EIR:Heating,", + " hp heating side,", + " node 1,", + " node 2,", + " AirSource,", + " node 3,", + " node 4,", + " ,", + " ,", + " ,", + " 0.0001,", + " 1,", + " ,", + " 1000,", + " 3.14,", + " ,", + " dummyCurve,", + " dummyCurve,", + " dummyCurve,", + " 1.0,", + " HeatingCapacity,", + " Load,", + " ConstantFlow,", + " 0.5;", + "Curve:Linear,", + " dummyCurve,", + " 0.95,", + " 0,", + " 1,", + " 1;"}); + ASSERT_TRUE(process_idf(idf_objects)); + state->init_state(*state); + + // set up the plant loops + // first the load side + state->dataPlnt->TotNumLoops = 1; + state->dataPlnt->PlantLoop.allocate(1); + + state->dataPlnt->PlantLoop(1).FluidName = "WATER"; + state->dataPlnt->PlantLoop(1).glycol = Fluid::GetWater(*state); + state->dataPlnt->PlantLoop(1).LoopDemandCalcScheme = DataPlant::LoopDemandCalcScheme::SingleSetPoint; + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).TotalBranches = 1; + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch.allocate(1); + 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 &PLHPPlantLoadSideComp = state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp(1); + PLHPPlantLoadSideComp.Type = DataPlant::PlantEquipmentType::HeatPumpEIRHeating; + PLHPPlantLoadSideComp.CurOpSchemeType = DataPlant::OpScheme::CompSetPtBased; + + // the init call expects a "from" calling point + PlantLocation myLoadLocation = PlantLocation(1, DataPlant::LoopSideLocation::Supply, 1, 1); + + // call the factory with a valid name to trigger reading inputs + EIRPlantLoopHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpEIRHeating, "HP HEATING SIDE"); + + // verify the size of the vector and the processed condition + EXPECT_EQ(1u, state->dataEIRPlantLoopHeatPump->heatPumps.size()); + + // for now we know the order is maintained, so get each heat pump object + EIRPlantLoopHeatPump *thisHeatingPLHP = &state->dataEIRPlantLoopHeatPump->heatPumps[0]; + + // do a bit of extra wiring up to the plant + PLHPPlantLoadSideComp.Name = thisHeatingPLHP->name; + PLHPPlantLoadSideComp.NodeNumIn = thisHeatingPLHP->loadSideNodes.inlet; + + // call for all initialization + state->dataGlobal->BeginEnvrnFlag = true; + state->dataPlnt->PlantFirstSizesOkayToFinalize = true; + thisHeatingPLHP->onInitLoopEquip(*state, myLoadLocation); + thisHeatingPLHP->setPointNodeNum = thisHeatingPLHP->loadSideNodes.outlet; + + // call it from the load side, but this time there is a negative (cooling) load - shouldn't try to run + { + bool firstHVAC = true; + Real64 curLoad = -900; + bool runFlag = true; // plant actually shouldn't do this but the component can be smart enough to handle it + Real64 constexpr specifiedLoadSetpoint = 45; + Real64 constexpr loadInletTemp = 46; + state->dataLoopNodes->Node(thisHeatingPLHP->setPointNodeNum).TempSetPoint = specifiedLoadSetpoint; + state->dataLoopNodes->Node(thisHeatingPLHP->loadSideNodes.inlet).Temp = loadInletTemp; + state->dataLoopNodes->Node(thisHeatingPLHP->sourceSideNodes.inlet).Temp = 30; + thisHeatingPLHP->simulate(*state, myLoadLocation, firstHVAC, curLoad, runFlag); + // expect it to meet setpoint and have some pre-evaluated conditions + EXPECT_NEAR(loadInletTemp, thisHeatingPLHP->loadSideOutletTemp, 0.001); + EXPECT_NEAR(0.0, thisHeatingPLHP->loadSideHeatTransfer, 0.001); + } + + // call it from the load side, but this time there is load (still firsthvac, unit can meet load) + { + bool firstHVAC = true; + Real64 curLoad = 800; + bool runFlag = true; + Real64 constexpr expectedLoadMassFlowRate = 0.09999; + Real64 constexpr expectedCp = 4180; + Real64 constexpr specifiedLoadSetpoint = 45; + Real64 const calculatedLoadInletTemp = specifiedLoadSetpoint - curLoad / (expectedLoadMassFlowRate * expectedCp); + state->dataLoopNodes->Node(thisHeatingPLHP->setPointNodeNum).TempSetPoint = specifiedLoadSetpoint; + state->dataLoopNodes->Node(thisHeatingPLHP->loadSideNodes.inlet).Temp = calculatedLoadInletTemp; + state->dataLoopNodes->Node(thisHeatingPLHP->sourceSideNodes.inlet).Temp = 30; + thisHeatingPLHP->simulate(*state, myLoadLocation, 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); + } + + // now we can call it again from the load side, but this time there is load (still firsthvac, unit cannot meet load) + { + bool firstHVAC = true; + Real64 curLoad = 1200; + Real64 availableCapacity = 950.0; + bool runFlag = true; + Real64 constexpr expectedLoadMassFlowRate = 0.09999; + Real64 constexpr expectedCp = 4180; + Real64 constexpr specifiedLoadSetpoint = 45; + Real64 const calculatedLoadInletTemp = specifiedLoadSetpoint - curLoad / (expectedLoadMassFlowRate * expectedCp); + state->dataLoopNodes->Node(thisHeatingPLHP->setPointNodeNum).TempSetPoint = specifiedLoadSetpoint; + state->dataLoopNodes->Node(thisHeatingPLHP->loadSideNodes.inlet).Temp = calculatedLoadInletTemp; + state->dataLoopNodes->Node(thisHeatingPLHP->sourceSideNodes.inlet).Temp = 30; + thisHeatingPLHP->simulate(*state, myLoadLocation, firstHVAC, curLoad, runFlag); + // expect it to miss setpoint and be at max capacity + EXPECT_NEAR(44.402, thisHeatingPLHP->loadSideOutletTemp, 0.001); + EXPECT_NEAR(availableCapacity, thisHeatingPLHP->loadSideHeatTransfer, 0.001); + } + + // now we can call it again from the load side, but this time there is no load (still firsthvac) + { + bool firstHVAC = true; + Real64 curLoad = 0.0; + bool runFlag = true; + Real64 constexpr expectedLoadMassFlowRate = 0.09999; + Real64 constexpr expectedCp = 4180; + Real64 constexpr specifiedLoadSetpoint = 45; + Real64 const calculatedLoadInletTemp = specifiedLoadSetpoint - curLoad / (expectedLoadMassFlowRate * expectedCp); + state->dataLoopNodes->Node(thisHeatingPLHP->setPointNodeNum).TempSetPoint = specifiedLoadSetpoint; + state->dataLoopNodes->Node(thisHeatingPLHP->loadSideNodes.inlet).Temp = calculatedLoadInletTemp; + state->dataLoopNodes->Node(thisHeatingPLHP->sourceSideNodes.inlet).Temp = 30; + thisHeatingPLHP->simulate(*state, myLoadLocation, firstHVAC, curLoad, runFlag); + // expect it to miss setpoint and be at max capacity + EXPECT_NEAR(45.0, thisHeatingPLHP->loadSideOutletTemp, 0.001); + EXPECT_NEAR(30.0, thisHeatingPLHP->sourceSideOutletTemp, 0.001); + } + + // now we can call it again from the load side, but this time there is a very low load (still firsthvac) + { + bool firstHVAC = true; + Real64 curLoad = 100.0; + bool runFlag = true; + Real64 constexpr expectedLoadMassFlowRate = 0.09999; + Real64 constexpr expectedCp = 4180; + Real64 constexpr specifiedLoadSetpoint = 45; + Real64 const calculatedLoadInletTemp = specifiedLoadSetpoint - curLoad / (expectedLoadMassFlowRate * expectedCp); + state->dataLoopNodes->Node(thisHeatingPLHP->setPointNodeNum).TempSetPoint = specifiedLoadSetpoint; + state->dataLoopNodes->Node(thisHeatingPLHP->loadSideNodes.inlet).Temp = calculatedLoadInletTemp; + state->dataLoopNodes->Node(thisHeatingPLHP->sourceSideNodes.inlet).Temp = 30; + thisHeatingPLHP->simulate(*state, myLoadLocation, firstHVAC, curLoad, runFlag); + // expect it to miss setpoint and be at max capacity + EXPECT_NEAR((curLoad / thisHeatingPLHP->referenceCOP) * 0.95 * 0.95, thisHeatingPLHP->powerUsage, 0.001); + } +} + +TEST_F(EnergyPlusFixture, CoolingConstructionFullyAutoSized_AirSource) +{ + std::string const idf_objects = delimited_string({"HeatPump:PlantLoop:EIR:Cooling,", + " hp cooling side,", + " node 1,", + " node 2,", + " AirSource,", + " node 3,", + " node 4,", + " ,", + " ,", + " ,", + " Autosize,", + " Autosize,", + " ,", + " Autosize,", + " ,", + " 1,", + " dummyCurve,", + " dummyCurve,", + " dummyCurve;", + "Curve:Linear,", + " dummyCurve,", + " 1,", + " 0,", + " 1,", + " 1;"}); + ASSERT_TRUE(process_idf(idf_objects)); + state->init_state(*state); + + // call the factory with a valid name to trigger reading inputs + EIRPlantLoopHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpEIRCooling, "HP COOLING SIDE"); + + // verify the size of the vector and the processed condition + EXPECT_EQ(1u, state->dataEIRPlantLoopHeatPump->heatPumps.size()); + + // for now we know the order is maintained, so get each heat pump object + EIRPlantLoopHeatPump *thisCoolingPLHP = &state->dataEIRPlantLoopHeatPump->heatPumps[0]; + + // validate the cooling side + EXPECT_EQ("HP COOLING SIDE", thisCoolingPLHP->name); + EXPECT_ENUM_EQ(DataPlant::PlantEquipmentType::HeatPumpEIRCooling, thisCoolingPLHP->EIRHPType); + EXPECT_EQ(nullptr, thisCoolingPLHP->companionHeatPumpCoil); + EXPECT_EQ(1, thisCoolingPLHP->capFuncTempCurveIndex); + EXPECT_EQ(1, thisCoolingPLHP->powerRatioFuncTempCurveIndex); + EXPECT_EQ(1, thisCoolingPLHP->powerRatioFuncPLRCurveIndex); + + // calling the factory with an invalid name or type will call ShowFatalError, which will trigger a runtime exception + EXPECT_THROW(EIRPlantLoopHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpEIRCooling, "fake"), std::runtime_error); + EXPECT_THROW(EIRPlantLoopHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpEIRHeating, "HP COOLING SIDE"), std::runtime_error); +} + +TEST_F(EnergyPlusFixture, ClearState) +{ + std::string const idf_objects = delimited_string({"HeatPump:PlantLoop:EIR:Cooling,", + " hp cooling side,", + " node 1,", + " node 2,", + " AirSource,", + " node 3,", + " node 4,", + " ,", + " ,", + " ,", + " Autosize,", + " Autosize,", + " ,", + " Autosize,", + " ,", + " 1,", + " dummyCurve,", + " dummyCurve,", + " dummyCurve;", + "Curve:Linear,", + " dummyCurve,", + " 1,", + " 0,", + " 1,", + " 1;"}); + ASSERT_TRUE(process_idf(idf_objects)); + state->init_state(*state); + + // call the factory with a valid name to trigger reading inputs + EIRPlantLoopHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpEIRCooling, "HP COOLING SIDE"); + EXPECT_EQ(state->dataEIRPlantLoopHeatPump->heatPumps.size(), 1u); + + // test that vector is cleared + state->dataEIRPlantLoopHeatPump->clear_state(); + EXPECT_EQ(state->dataEIRPlantLoopHeatPump->heatPumps.size(), 0u); +} + +TEST_F(EnergyPlusFixture, Initialization2_AirSource) +{ + std::string const idf_objects = delimited_string({"HeatPump:PlantLoop:EIR:Cooling,", + " hp cooling side,", + " node 1,", + " node 2,", + " AirSource,", + " node 3,", + " node 4,", + " ,", + " ,", + " ,", + " 0.001,", + " 1,", + " ,", + " 1000,", + " 3.14,", + " ,", + " dummyCurve,", + " dummyCurve,", + " dummyCurve;", + "Curve:Linear,", + " dummyCurve,", + " 1,", + " 0,", + " 1,", + " 1;"}); + ASSERT_TRUE(process_idf(idf_objects)); + state->init_state(*state); + + bool firstHVACIteration = true; + // set up the plant loops + // first the load side + state->dataPlnt->TotNumLoops = 1; + state->dataPlnt->PlantLoop.allocate(1); + + state->dataPlnt->PlantLoop(1).FluidName = "WATER"; + state->dataPlnt->PlantLoop(1).glycol = Fluid::GetWater(*state); + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).TotalBranches = 1; + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch.allocate(1); + 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 &PLHPPlantLoadSideComp = state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp(1); + PLHPPlantLoadSideComp.Type = DataPlant::PlantEquipmentType::HeatPumpEIRCooling; + + // the init call expects a "from" calling point + PlantLocation myLocation = PlantLocation(1, DataPlant::LoopSideLocation::Supply, 1, 1); + + // call the factory with a valid name to trigger reading inputs + EIRPlantLoopHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpEIRCooling, "HP COOLING SIDE"); + + // verify the size of the vector and the processed condition + EXPECT_EQ(1u, state->dataEIRPlantLoopHeatPump->heatPumps.size()); + + // for now we know the order is maintained, so get each heat pump object + EIRPlantLoopHeatPump *thisCoolingPLHP = &state->dataEIRPlantLoopHeatPump->heatPumps[0]; + + // do a bit of extra wiring up to the plant + PLHPPlantLoadSideComp.Name = thisCoolingPLHP->name; + PLHPPlantLoadSideComp.NodeNumIn = thisCoolingPLHP->loadSideNodes.inlet; + + // call for all initialization + state->dataGlobal->BeginEnvrnFlag = true; + state->dataPlnt->PlantFirstSizesOkayToFinalize = true; + thisCoolingPLHP->onInitLoopEquip(*state, myLocation); + + // Componnent flow control type + auto &comp = DataPlant::CompData::getPlantComponent(*state, thisCoolingPLHP->loadSidePlantLoc); + comp.FlowCtrl = DataBranchAirLoopPlant::ControlType::Active; + // call with run flag off, loose limits on node min/max thisCoolingPLHP->running = false; Real64 constexpr currentLoad = 0.0; @@ -4038,6 +11741,10 @@ TEST_F(EnergyPlusFixture, Initialization2_AirSource) EXPECT_NEAR(0.2, thisCoolingPLHP->loadSideMassFlowRate, 0.001); EXPECT_NEAR(0.0, thisCoolingPLHP->sourceSideMassFlowRate, 0.001); + comp.FlowCtrl = DataBranchAirLoopPlant::ControlType::SeriesActive; + thisCoolingPLHP->setOperatingFlowRatesASHP(*state, firstHVACIteration, currentLoad); + EXPECT_NEAR(1.29, thisCoolingPLHP->sourceSideMassFlowRate, 0.01); + // call with run flag ON, flow locked at zero on both sides state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).FlowLock = DataPlant::FlowLock::Locked; state->dataLoopNodes->Node(thisCoolingPLHP->loadSideNodes.inlet).MassFlowRate = 0.0; @@ -4046,13 +11753,13 @@ TEST_F(EnergyPlusFixture, Initialization2_AirSource) EXPECT_NEAR(0.0, thisCoolingPLHP->loadSideMassFlowRate, 0.001); EXPECT_NEAR(0.0, thisCoolingPLHP->sourceSideMassFlowRate, 0.001); - // call with run flag ON, flow locked at zero on source side since there's no load + // call with run flag ON, flow locked at nonzero both state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).FlowLock = DataPlant::FlowLock::Locked; state->dataLoopNodes->Node(thisCoolingPLHP->loadSideNodes.inlet).MassFlowRate = 0.14; thisCoolingPLHP->running = true; thisCoolingPLHP->setOperatingFlowRatesASHP(*state, firstHVACIteration, currentLoad); EXPECT_NEAR(0.14, thisCoolingPLHP->loadSideMassFlowRate, 0.001); - EXPECT_NEAR(0.0, thisCoolingPLHP->sourceSideMassFlowRate, 0.001); + EXPECT_NEAR(1.29, thisCoolingPLHP->sourceSideMassFlowRate, 0.01); } TEST_F(EnergyPlusFixture, TestSizing_FullyAutosizedCoolingWithCompanion_AirSource) From c835e273cfde7b6f115d583ebe4c373780ee1177 Mon Sep 17 00:00:00 2001 From: lymereJ Date: Tue, 26 May 2026 12:24:08 -0700 Subject: [PATCH 4/7] Adjust consideration for series configuration, edit unit test, and add new test file. --- src/EnergyPlus/PlantLoopHeatPumpEIR.cc | 8 +- ...antLoopHeatPump_EIR_AirSource_Parallel.idf | 651 ++ .../unit/PlantLoopHeatPumpEIR.unit.cc | 7727 +---------------- 3 files changed, 677 insertions(+), 7709 deletions(-) create mode 100644 testfiles/PlantLoopHeatPump_EIR_AirSource_Parallel.idf diff --git a/src/EnergyPlus/PlantLoopHeatPumpEIR.cc b/src/EnergyPlus/PlantLoopHeatPumpEIR.cc index e5befbd1fbb..36f3ce42327 100644 --- a/src/EnergyPlus/PlantLoopHeatPumpEIR.cc +++ b/src/EnergyPlus/PlantLoopHeatPumpEIR.cc @@ -284,8 +284,7 @@ void EIRPlantLoopHeatPump::setOperatingFlowRatesWSHP(EnergyPlusData &state, bool void EIRPlantLoopHeatPump::setOperatingFlowRatesASHP(EnergyPlusData &state, bool FirstHVACIteration, [[maybe_unused]] Real64 const currentLoad) { - auto &comp = DataPlant::CompData::getPlantComponent(state, this->loadSidePlantLoc); - if ((std::abs(currentLoad) < HVAC::SmallLoad || !this->running) && comp.FlowCtrl != DataBranchAirLoopPlant::ControlType::SeriesActive) { + if (!this->running) { this->loadSideMassFlowRate = 0.0; this->sourceSideMassFlowRate = 0.0; PlantUtilities::SetComponentFlowRate( @@ -299,7 +298,10 @@ void EIRPlantLoopHeatPump::setOperatingFlowRatesASHP(EnergyPlusData &state, bool // Set flows if the heat pump is running } else { // the heat pump must run // apply min/max operating limits based on source side entering fluid temperature - if ((this->minSourceTempLimit > this->sourceSideInletTemp || this->maxSourceTempLimit < this->sourceSideInletTemp) && + // or if parallel configuration and no load, the unit is turned off + auto &comp = DataPlant::CompData::getPlantComponent(state, this->loadSidePlantLoc); + if (((this->minSourceTempLimit > this->sourceSideInletTemp || this->maxSourceTempLimit < this->sourceSideInletTemp) || + (std::abs(currentLoad) < HVAC::SmallLoad && comp.FlowCtrl != DataBranchAirLoopPlant::ControlType::SeriesActive)) && !this->heatRecoveryIsActive) { this->loadSideMassFlowRate = 0.0; this->sourceSideMassFlowRate = 0.0; diff --git a/testfiles/PlantLoopHeatPump_EIR_AirSource_Parallel.idf b/testfiles/PlantLoopHeatPump_EIR_AirSource_Parallel.idf new file mode 100644 index 00000000000..99f75b6d0bd --- /dev/null +++ b/testfiles/PlantLoopHeatPump_EIR_AirSource_Parallel.idf @@ -0,0 +1,651 @@ +! PlantLoopHeatPump_EIR_AirSource_Parallel.idf +! +! Basic file description: +! The EIR formulated air-to-water heat pump is demonstrated using a simple collection of plant loops. Building loads +! are represented using Plant Load Profile objects. The main difference with PlantLoopHeatPump_EIR_AirSource is that +! this file uses two air-to-water heat pumps in parallel. +! +! Run: 1 design day. +! Building: None. +! System: None. +! Plant: PLANT LOAD PROFILE with District Heating and Cooling as necessary. + + Version,26.2; + + Building, + Plant Load Profile Example, !- Name + 0.0, !- North Axis {deg} + Suburbs, !- Terrain + 0.04, !- Loads Convergence Tolerance Value {W} + 0.04, !- Temperature Convergence Tolerance Value {deltaC} + FullInteriorAndExterior, !- Solar Distribution + 25, !- Maximum Number of Warmup Days + 6; !- Minimum Number of Warmup Days + + Timestep,1; + + GlobalGeometryRules, + UpperLeftCorner, !- Starting Vertex Position + CounterClockWise, !- Vertex Entry Direction + Relative; !- Coordinate System + + Site:Location, + Chicago, !- Name + 42.00, !- Latitude {deg} + -87.88, !- Longitude {deg} + -6.00, !- Time Zone {hr} + 190.00; !- Elevation {m} + + SizingPeriod:DesignDay, + Chicago Summer 1%, !- Name + 7, !- Month + 21, !- Day of Month + SummerDesignDay, !- Day Type + 31.5, !- Maximum Dry-Bulb Temperature {C} + 10.7, !- Daily Dry-Bulb Temperature Range {deltaC} + , !- Dry-Bulb Temperature Range Modifier Type + , !- Dry-Bulb Temperature Range Modifier Day Schedule Name + Wetbulb, !- Humidity Condition Type + 23, !- Wetbulb or DewPoint at Maximum Dry-Bulb {C} + , !- Humidity Condition Day Schedule Name + , !- Humidity Ratio at Maximum Dry-Bulb {kgWater/kgDryAir} + , !- Enthalpy at Maximum Dry-Bulb {J/kg} + , !- Daily Wet-Bulb Temperature Range {deltaC} + 99063., !- Barometric Pressure {Pa} + 5.3, !- Wind Speed {m/s} + 230, !- Wind Direction {deg} + , !- Rain Indicator + , !- Snow Indicator + , !- Daylight Saving Time Indicator + , !- Solar Model Indicator + , !- Beam Solar Day Schedule Name + , !- Diffuse Solar Day Schedule Name + , !- ASHRAE Clear Sky Optical Depth for Beam Irradiance (taub) {dimensionless} + , !- ASHRAE Clear Sky Optical Depth for Diffuse Irradiance (taud) {dimensionless} + 1.0; !- Sky Clearness + + SizingPeriod:DesignDay, + Chicago Ohare Intl Ap Ann Htg 99.6% Condns DB, !- Name + 1, !- Month + 21, !- Day of Month + WinterDesignDay, !- Day Type + -20, !- Maximum Dry-Bulb Temperature {C} + 0.0, !- Daily Dry-Bulb Temperature Range {deltaC} + DefaultMultipliers, !- Dry-Bulb Temperature Range Modifier Type + , !- Dry-Bulb Temperature Range Modifier Day Schedule Name + Wetbulb, !- Humidity Condition Type + -20, !- Wetbulb or DewPoint at Maximum Dry-Bulb {C} + , !- Humidity Condition Day Schedule Name + , !- Humidity Ratio at Maximum Dry-Bulb {kgWater/kgDryAir} + , !- Enthalpy at Maximum Dry-Bulb {J/kg} + , !- Daily Wet-Bulb Temperature Range {deltaC} + 98934., !- Barometric Pressure {Pa} + 4.9, !- Wind Speed {m/s} + 270, !- Wind Direction {deg} + , !- Rain Indicator + , !- Snow Indicator + , !- Daylight Saving Time Indicator + , !- Solar Model Indicator + , !- Beam Solar Day Schedule Name + , !- Diffuse Solar Day Schedule Name + , !- ASHRAE Clear Sky Optical Depth for Beam Irradiance (taub) {dimensionless} + , !- ASHRAE Clear Sky Optical Depth for Diffuse Irradiance (taud) {dimensionless} + 1.0; !- Sky Clearness + + SimulationControl, + No, !- Do Zone Sizing Calculation + No, !- Do System Sizing Calculation + No, !- Do Plant Sizing Calculation + Yes, !- Run Simulation for Sizing Periods + No, !- Run Simulation for Weather File Run Periods + No, !- Do HVAC Sizing Simulation for Sizing Periods + 1; !- Maximum Number of HVAC Sizing Simulation Passes + + ScheduleTypeLimits, + Any Number; !- Name + + ScheduleTypeLimits, + On/Off, !- Name + 0, !- Lower Limit Value + 1, !- Upper Limit Value + Discrete; !- Numeric Type + + Schedule:Constant,Heating Coil Load Loop Setpoint Temperature Schedule,Any Number,65.0; + + Schedule:Constant,Cooling Coil Load Loop Setpoint Temperature Schedule,Any Number,20; + + Schedule:Constant,AlwaysOnSchedule,On/Off,1; + + ! Simulate a winter time, overnight hours load, but the pump is always running in that season + + Schedule:Compact, + Heating Coil Load Profile Load Schedule, !- Name + Any Number, !- Schedule Type Limits Name + THROUGH: 03/31, !- Field 1 + FOR: AllDays, !- Field 2 + UNTIL: 4:00,62000, !- Field 3 + UNTIL: 8:00,25000, !- Field 5 + UNTIL: 9:00,15000, !- Field 7 + UNTIL: 17:00,9000, !- Field 9 + UNTIL: 24:00,36000, !- Field 11 + THROUGH: 09/30, !- Field 13 + FOR: AllDays, !- Field 14 + UNTIL: 24:00,0.0, !- Field 15 + THROUGH: 12/31, !- Field 17 + FOR: AllDays, !- Field 18 + UNTIL: 4:00,62000, !- Field 19 + UNTIL: 8:00,25000, !- Field 21 + UNTIL: 9:00,15000, !- Field 23 + UNTIL: 17:00,9000, !- Field 25 + UNTIL: 24:00,36000; !- Field 27 + + Schedule:Compact, + Heating Coil Load Profile Flow Frac Schedule, !- Name + Any Number, !- Schedule Type Limits Name + THROUGH: 03/31, !- Field 1 + FOR: AllDays, !- Field 2 + UNTIL: 24:00,1.0, !- Field 3 + THROUGH: 09/30, !- Field 5 + FOR: AllDays, !- Field 6 + UNTIL: 24:00,0.0, !- Field 7 + THROUGH: 12/31, !- Field 9 + FOR: AllDays, !- Field 10 + UNTIL: 24:00,1.0; !- Field 11 + + ! Simulate a summer time, daytime hours load, but the pump is always running in that season + + Schedule:Compact, + Cooling Coil Load Profile Load Schedule, !- Name + Any Number, !- Schedule Type Limits Name + THROUGH: 03/31, !- Field 1 + FOR: AllDays, !- Field 2 + UNTIL: 24:00,0.0, !- Field 3 + THROUGH: 09/30, !- Field 5 + FOR: AllDays, !- Field 6 + UNTIL: 9:00,-200000, !- Field 7 + UNTIL: 17:00,-410000, !- Field 9 + UNTIL: 24:00,-250000, !- Field 11 + THROUGH: 12/31, !- Field 13 + FOR: AllDays, !- Field 14 + UNTIL: 24:00,0.0; !- Field 15 + + Schedule:Compact, + Cooling Coil Load Profile Flow Frac Schedule, !- Name + Any Number, !- Schedule Type Limits Name + THROUGH: 03/31, !- Field 1 + FOR: AllDays, !- Field 2 + UNTIL: 24:00,0.0, !- Field 3 + THROUGH: 09/30, !- Field 5 + FOR: AllDays, !- Field 6 + UNTIL: 24:00,1.0, !- Field 7 + THROUGH: 12/31, !- Field 9 + FOR: AllDays, !- Field 10 + UNTIL: 24:00,0.0; !- Field 11 + +! *** Heating Coil Loop + + PlantLoop, + Heating Coil Load Loop, !- Name + WATER, !- Fluid Type + , !- User Defined Fluid Type + Heating Coil Load Loop Operation, !- Plant Equipment Operation Scheme Name + Heating Coil Load Loop Supply Outlet Pipe Outlet Node, !- Loop Temperature Setpoint Node Name + 100, !- Maximum Loop Temperature {C} + 3, !- Minimum Loop Temperature {C} + 0.005, !- Maximum Loop Flow Rate {m3/s} + 0, !- Minimum Loop Flow Rate {m3/s} + 1, !- Plant Loop Volume {m3} + Heating Coil Load Loop Supply Inlet Node, !- Plant Side Inlet Node Name + Heating Coil Load Loop Supply Outlet Pipe Outlet Node, !- Plant Side Outlet Node Name + Heating Coil Load Loop Supply Branches, !- Plant Side Branch List Name + Heating Coil Load Loop Supply Connectors, !- Plant Side Connector List Name + Heating Coil Load Loop Demand Inlet Node, !- Demand Side Inlet Node Name + Heating Coil Load Loop Demand Outlet Node, !- Demand Side Outlet Node Name + Heating Coil Load Loop Demand Branches, !- Demand Side Branch List Name + , !- Demand Side Connector List Name + Optimal; !- Load Distribution Scheme + + SetpointManager:Scheduled, + Heating Coil Load Loop Setpoint Manager, !- Name + Temperature, !- Control Variable + Heating Coil Load Loop Setpoint Temperature Schedule, !- Schedule Name + Heating Coil Load Loop Supply Outlet Pipe Outlet Node; !- Setpoint Node or NodeList Name + + PlantEquipmentOperationSchemes, + Heating Coil Load Loop Operation, !- Name + PlantEquipmentOperation:HeatingLoad, !- Control Scheme 1 Object Type + Heating Coil Load Loop Heating Operation, !- Control Scheme 1 Name + AlwaysOnSchedule; !- Control Scheme 1 Schedule Name + + PlantEquipmentOperation:HeatingLoad, + Heating Coil Load Loop Heating Operation, !- Name + 0, !- Load Range 1 Lower Limit {W} + 26000, !- Load Range 1 Upper Limit {W} + Heating Coil Load Loop Heating Equipment 1, !- Range 1 Equipment List Name + 26000, !- Load Range 2 Lower Limit {W} + 10000000, !- Load Range 2 Upper Limit {W} + Heating Coil Load Loop Heating Equipment 2; !- Range 2 Equipment List Name + + PlantEquipmentList, + Heating Coil Load Loop Heating Equipment 1, !- Name + HeatPump:PlantLoop:EIR:Heating, !- Equipment 1 Object Type + Heating Coil 1; !- Equipment 1 Name + + PlantEquipmentList, + Heating Coil Load Loop Heating Equipment 2, !- Name + HeatPump:PlantLoop:EIR:Heating, !- Equipment 1 Object Type + Heating Coil 1, !- Equipment 1 Name + HeatPump:PlantLoop:EIR:Heating, !- Equipment 2 Object Type + Heating Coil 2; !- Equipment 2 Name + + BranchList, + Heating Coil Load Loop Supply Branches, !- Name + Heating Coil Load Loop Supply Inlet Branch, !- Branch 1 Name + Heating Coil 1 Branch, !- Branch 2 Name + Heating Coil 2 Branch, !- Branch 3 Name + Heating Coil Bypass Branch, !- Branch 4 Name + Heating Coil Load Loop Supply Outlet Branch; !- Branch 5 Name + + Branch, + Heating Coil Load Loop Supply Inlet Branch, !- Name + , !- Pressure Drop Curve Name + Pump:VariableSpeed, !- Component 1 Object Type + Heating Coil Load Loop Pump, !- Component 1 Name + Heating Coil Load Loop Supply Inlet Node, !- Component 1 Inlet Node Name + Heating Coil Load Loop Intermediate Node; !- Component 1 Outlet Node Name + + Branch, + Heating Coil 1 Branch, !- Name + , !- Pressure Drop Curve Name + HeatPump:PlantLoop:EIR:Heating, !- Component 1 Object Type + Heating Coil 1, !- Component 1 Name + Heating Coil 1 Inlet Node, !- Component 1 Inlet Node Name + Heating Coil 1 Outlet Node; !- Component 1 Outlet Node Name + + Branch, + Heating Coil 2 Branch, !- Name + , !- Pressure Drop Curve Name + HeatPump:PlantLoop:EIR:Heating, !- Component 1 Object Type + Heating Coil 2, !- Component 1 Name + Heating Coil 2 Inlet Node, !- Component 1 Inlet Node Name + Heating Coil 2 Outlet Node; !- Component 1 Outlet Node Name + + Connector:Splitter, + Heating Coil Load Loop Splitter, !- Name + Heating Coil Load Loop Supply Inlet Branch, !- Inlet Branch Name + Heating Coil 1 Branch, !- Outlet Branch 1 Name + Heating Coil 2 Branch, !- Outlet Branch 2 Name + Heating Coil Bypass Branch; !- Outlet Branch 3 Name + + Connector:Mixer, + Heating Coil Load Loop Mixer, !- Name + Heating Coil Load Loop Supply Outlet Branch, !- Inlet Branch Name + Heating Coil 1 Branch, !- Outlet Branch 1 Name + Heating Coil 2 Branch, !- Outlet Branch 2 Name + Heating Coil Bypass Branch; !- Outlet Branch 3 Name + + ConnectorList, + Heating Coil Load Loop Supply Connectors, + Connector:Splitter, !- Connector 1 Object Type + Heating Coil Load Loop Splitter, !- Connector 1 Name + Connector:Mixer, !- Connector 2 Object Type + Heating Coil Load Loop Mixer; !- Connector 2 Name + + Pipe:Adiabatic, + Heating Coil Bypass, !- Name + Heating Coil Bypass Inlet Node, !- Inlet Node Name + Heating Coil Bypass Outlet Node; !- Outlet Node Name + + Branch, + Heating Coil Bypass Branch, !- Name + , !- Pressure Drop Curve Name + Pipe:Adiabatic, !- Component 1 Object Type + Heating Coil Bypass, !- Component 1 Name + Heating Coil Bypass Inlet Node, !- Component 1 Inlet Node Name + Heating Coil Bypass Outlet Node; !- Component 1 Outlet Node Name + + Branch, + Heating Coil Load Loop Supply Outlet Branch, !- Name + , !- Pressure Drop Curve Name + Pipe:Adiabatic, !- Component 1 Object Type + Heating Coil Load Loop Supply Outlet Pipe, !- Component 1 Name + Heating Coil Load Loop Supply Outlet Pipe Inlet Node, !- Component 1 Inlet Node Name + Heating Coil Load Loop Supply Outlet Pipe Outlet Node; !- Component 1 Outlet Node Name + + Pipe:Adiabatic, + Heating Coil Load Loop Supply Outlet Pipe, !- Name + Heating Coil Load Loop Supply Outlet Pipe Inlet Node, !- Inlet Node Name + Heating Coil Load Loop Supply Outlet Pipe Outlet Node; !- Outlet Node Name + + Pump:VariableSpeed, + Heating Coil Load Loop Pump, !- Name + Heating Coil Load Loop Supply Inlet Node, !- Inlet Node Name + Heating Coil Load Loop Intermediate Node, !- Outlet Node Name + 0.005, !- Design Maximum Flow Rate {m3/s} + 3000, !- Design Pump Head {Pa} + 22, !- Design Power Consumption {W} + 0.87, !- Motor Efficiency + 0.0, !- Fraction of Motor Inefficiencies to Fluid Stream + 0, !- Coefficient 1 of the Part Load Performance Curve + 1, !- Coefficient 2 of the Part Load Performance Curve + 0, !- Coefficient 3 of the Part Load Performance Curve + 0, !- Coefficient 4 of the Part Load Performance Curve + 0, !- Design Minimum Flow Rate {m3/s} + INTERMITTENT; !- Pump Control Type + + HeatPump:PlantLoop:EIR:Heating, + Heating Coil 1, !- Name + Heating Coil 1 Inlet Node, !- Load Side Inlet Node Name + Heating Coil 1 Outlet Node, !- Load Side Outlet Node Name + AirSource, !- Condenser Type + Outdoor Air Inlet Node, !- Source Side Inlet Node Name + Outdoor Air Outlet Node, !- Source Side Outlet Node Name + , !- Heat Recovery Inlet Node Name + , !- Heat Recovery Outlet Node Name + Cooling Coil, !- Companion Heat Pump Name + 0.005, !- Load Side Reference Flow Rate {m3/s} + 2, !- Source Side Reference Flow Rate {m3/s} + , !- Heat Recovery Reference Flow Rate {m3/s} + 10000, !- Reference Capacity {W} + 3.5, !- Reference Coefficient of Performance {W/W} + , !- Sizing Factor + CapCurveFuncTemp, !- Capacity Modifier Function of Temperature Curve Name + EIRCurveFuncTemp, !- Electric Input to Output Ratio Modifier Function of Temperature Curve Name + EIRCurveFuncPLR; !- Electric Input to Output Ratio Modifier Function of Part Load Ratio Curve Name + + HeatPump:PlantLoop:EIR:Heating, + Heating Coil 2, !- Name + Heating Coil 2 Inlet Node, !- Load Side Inlet Node Name + Heating Coil 2 Outlet Node, !- Load Side Outlet Node Name + AirSource, !- Condenser Type + Outdoor Air 2 Inlet Node, !- Source Side Inlet Node Name + Outdoor Air 2 Outlet Node, !- Source Side Outlet Node Name + , !- Heat Recovery Inlet Node Name + , !- Heat Recovery Outlet Node Name + , !- Companion Heat Pump Name + 0.005, !- Load Side Reference Flow Rate {m3/s} + 2, !- Source Side Reference Flow Rate {m3/s} + , !- Heat Recovery Reference Flow Rate {m3/s} + 80000, !- Reference Capacity {W} + 3.5, !- Reference Coefficient of Performance {W/W} + 0.5, !- Sizing Factor + CapCurveFuncTemp, !- Capacity Modifier Function of Temperature Curve Name + EIRCurveFuncTemp, !- Electric Input to Output Ratio Modifier Function of Temperature Curve Name + EIRCurveFuncPLR; !- Electric Input to Output Ratio Modifier Function of Part Load Ratio Curve Name + + Curve:Biquadratic, + CapCurveFuncTemp, !- Name + 1.0, !- Coefficient1 Constant + 0.0, !- Coefficient2 x + 0.0, !- Coefficient3 x**2 + 0.0, !- Coefficient4 y + 0.0, !- Coefficient5 y**2 + 0.0, !- Coefficient6 x*y + 5.0, !- Minimum Value of x + 10.0, !- Maximum Value of x + 24.0, !- Minimum Value of y + 35.0, !- Maximum Value of y + , !- Minimum Curve Output + , !- Maximum Curve Output + Temperature, !- Input Unit Type for X + Temperature, !- Input Unit Type for Y + Dimensionless; !- Output Unit Type + + Curve:Biquadratic, + EIRCurveFuncTemp, !- Name + 1.0, !- Coefficient1 Constant + 0.0, !- Coefficient2 x + 0.0, !- Coefficient3 x**2 + 0.0, !- Coefficient4 y + 0.0, !- Coefficient5 y**2 + 0.0, !- Coefficient6 x*y + 5.0, !- Minimum Value of x + 10.0, !- Maximum Value of x + 24.0, !- Minimum Value of y + 35.0, !- Maximum Value of y + , !- Minimum Curve Output + , !- Maximum Curve Output + Temperature, !- Input Unit Type for X + Temperature, !- Input Unit Type for Y + Dimensionless; !- Output Unit Type + + Curve:Quadratic, + EIRCurveFuncPLR, !- Name + 1.0, !- Coefficient1 Constant + 0.0, !- Coefficient2 x + 0.0, !- Coefficient3 x**2 + 0.0, !- Minimum Value of x + 1.0; !- Maximum Value of x + + BranchList, + Heating Coil Load Loop Demand Branches, !- Name + Heating Coil Load Loop Demand Branch; !- Branch 1 Name + + Branch, + Heating Coil Load Loop Demand Branch, !- Name + , !- Pressure Drop Curve Name + LoadProfile:Plant, !- Component 1 Object Type + Heating Coil Load Profile, !- Component 1 Name + Heating Coil Load Loop Demand Inlet Node, !- Component 1 Inlet Node Name + Heating Coil Load Loop Demand Outlet Node; !- Component 1 Outlet Node Name + + LoadProfile:Plant, + Heating Coil Load Profile, !- Name + Heating Coil Load Loop Demand Inlet Node, !- Inlet Node Name + Heating Coil Load Loop Demand Outlet Node, !- Outlet Node Name + Heating Coil Load Profile Load Schedule, !- Load Schedule Name + 0.005, !- Peak Flow Rate {m3/s} + Heating Coil Load Profile Flow Frac Schedule, !- Flow Rate Fraction Schedule Name + ; !- Plant Loop Fluid Type + +! *** Cooling coil loop + + PlantLoop, + Cooling Coil Load Loop, !- Name + WATER, !- Fluid Type + , !- User Defined Fluid Type + Cooling Coil Load Loop Operation, !- Plant Equipment Operation Scheme Name + Cooling Coil Load Loop Supply Outlet Node, !- Loop Temperature Setpoint Node Name + 100, !- Maximum Loop Temperature {C} + 3, !- Minimum Loop Temperature {C} + 0.005, !- Maximum Loop Flow Rate {m3/s} + 0, !- Minimum Loop Flow Rate {m3/s} + 1, !- Plant Loop Volume {m3} + Cooling Coil Load Loop Supply Inlet Node, !- Plant Side Inlet Node Name + Cooling Coil Load Loop Supply Outlet Node, !- Plant Side Outlet Node Name + Cooling Coil Load Loop Supply Branches, !- Plant Side Branch List Name + , !- Plant Side Connector List Name + Cooling Coil Load Loop Demand Inlet Node, !- Demand Side Inlet Node Name + Cooling Coil Load Loop Demand Outlet Node, !- Demand Side Outlet Node Name + Cooling Coil Load Loop Demand Branches, !- Demand Side Branch List Name + , !- Demand Side Connector List Name + Optimal; !- Load Distribution Scheme + + SetpointManager:Scheduled, + Cooling Coil Load Loop Setpoint Manager, !- Name + Temperature, !- Control Variable + Cooling Coil Load Loop Setpoint Temperature Schedule, !- Schedule Name + Cooling Coil Load Loop Supply Outlet Node; !- Setpoint Node or NodeList Name + + PlantEquipmentOperationSchemes, + Cooling Coil Load Loop Operation, !- Name + PlantEquipmentOperation:CoolingLoad, !- Control Scheme 1 Object Type + Cooling Coil Load Loop Cooling Operation, !- Control Scheme 1 Name + AlwaysOnSchedule; !- Control Scheme 1 Schedule Name + + PlantEquipmentOperation:CoolingLoad, + Cooling Coil Load Loop Cooling Operation, !- Name + 0, !- Load Range 1 Lower Limit {W} + 10000000, !- Load Range 1 Upper Limit {W} + Cooling Coil Load Loop Cooling Equipment; !- Range 1 Equipment List Name + + PlantEquipmentList, + Cooling Coil Load Loop Cooling Equipment, !- Name + HeatPump:PlantLoop:EIR:Cooling, !- Equipment 1 Object Type + Cooling Coil; !- Equipment 1 Name + + BranchList, + Cooling Coil Load Loop Supply Branches, !- Name + Cooling Coil Load Loop Supply Branch; !- Branch 1 Name + + Branch, + Cooling Coil Load Loop Supply Branch, !- Name + , !- Pressure Drop Curve Name + Pump:VariableSpeed, !- Component 1 Object Type + Cooling Coil Load Loop Pump, !- Component 1 Name + Cooling Coil Load Loop Supply Inlet Node, !- Component 1 Inlet Node Name + Cooling Coil Load Loop Intermediate Node, !- Component 1 Outlet Node Name + HeatPump:PlantLoop:EIR:Cooling, !- Component 2 Object Type + Cooling Coil, !- Component 2 Name + Cooling Coil Load Loop Intermediate Node, !- Component 2 Inlet Node Name + Cooling Coil Load Loop Supply Outlet Node; !- Component 2 Outlet Node Name + + Pump:VariableSpeed, + Cooling Coil Load Loop Pump, !- Name + Cooling Coil Load Loop Supply Inlet Node, !- Inlet Node Name + Cooling Coil Load Loop Intermediate Node, !- Outlet Node Name + 0.005, !- Design Maximum Flow Rate {m3/s} + 3000, !- Design Pump Head {Pa} + 22, !- Design Power Consumption {W} + 0.87, !- Motor Efficiency + 0.0, !- Fraction of Motor Inefficiencies to Fluid Stream + 0, !- Coefficient 1 of the Part Load Performance Curve + 1, !- Coefficient 2 of the Part Load Performance Curve + 0, !- Coefficient 3 of the Part Load Performance Curve + 0, !- Coefficient 4 of the Part Load Performance Curve + 0, !- Design Minimum Flow Rate {m3/s} + INTERMITTENT; !- Pump Control Type + + HeatPump:PlantLoop:EIR:Cooling, + Cooling Coil, !- Name + Cooling Coil Load Loop Intermediate Node, !- Load Side Inlet Node Name + Cooling Coil Load Loop Supply Outlet Node, !- Load Side Outlet Node Name + AirSource, !- Condenser Type + Outdoor Air 3 Inlet Node, !- Source Side Inlet Node Name + Outdoor Air 3 Outlet Node, !- Source Side Outlet Node Name + , !- Heat Recovery Inlet Node Name + , !- Heat Recovery Outlet Node Name + Heating Coil 1, !- Companion Heat Pump Name + 0.005, !- Load Side Reference Flow Rate {m3/s} + 20, !- Source Side Reference Flow Rate {m3/s} + , !- Heat Recovery Reference Flow Rate {m3/s} + 400000, !- Reference Capacity {W} + 3.5, !- Reference Coefficient of Performance {W/W} + , !- Sizing Factor + CapCurveFuncTemp2, !- Capacity Modifier Function of Temperature Curve Name + EIRCurveFuncTemp2, !- Electric Input to Output Ratio Modifier Function of Temperature Curve Name + EIRCurveFuncPLR2; !- Electric Input to Output Ratio Modifier Function of Part Load Ratio Curve Name + + Curve:Biquadratic, + CapCurveFuncTemp2, !- Name + 1.0, !- Coefficient1 Constant + 0.0, !- Coefficient2 x + 0.0, !- Coefficient3 x**2 + 0.0, !- Coefficient4 y + 0.0, !- Coefficient5 y**2 + 0.0, !- Coefficient6 x*y + 5.0, !- Minimum Value of x + 10.0, !- Maximum Value of x + 24.0, !- Minimum Value of y + 35.0, !- Maximum Value of y + , !- Minimum Curve Output + , !- Maximum Curve Output + Temperature, !- Input Unit Type for X + Temperature, !- Input Unit Type for Y + Dimensionless; !- Output Unit Type + + Curve:Biquadratic, + EIRCurveFuncTemp2, !- Name + 1.0, !- Coefficient1 Constant + 0.0, !- Coefficient2 x + 0.0, !- Coefficient3 x**2 + 0.0, !- Coefficient4 y + 0.0, !- Coefficient5 y**2 + 0.0, !- Coefficient6 x*y + 5.0, !- Minimum Value of x + 10.0, !- Maximum Value of x + 24.0, !- Minimum Value of y + 35.0, !- Maximum Value of y + , !- Minimum Curve Output + , !- Maximum Curve Output + Temperature, !- Input Unit Type for X + Temperature, !- Input Unit Type for Y + Dimensionless; !- Output Unit Type + + Curve:Quadratic, + EIRCurveFuncPLR2, !- Name + 1.0, !- Coefficient1 Constant + 0.0, !- Coefficient2 x + 0.0, !- Coefficient3 x**2 + 0.0, !- Minimum Value of x + 1.0; !- Maximum Value of x + + BranchList, + Cooling Coil Load Loop Demand Branches, !- Name + Cooling Coil Load Loop Demand Branch; !- Branch 1 Name + + Branch, + Cooling Coil Load Loop Demand Branch, !- Name + , !- Pressure Drop Curve Name + LoadProfile:Plant, !- Component 1 Object Type + Cooling Coil Load Profile, !- Component 1 Name + Cooling Coil Load Loop Demand Inlet Node, !- Component 1 Inlet Node Name + Cooling Coil Load Loop Demand Outlet Node; !- Component 1 Outlet Node Name + + LoadProfile:Plant, + Cooling Coil Load Profile, !- Name + Cooling Coil Load Loop Demand Inlet Node, !- Inlet Node Name + Cooling Coil Load Loop Demand Outlet Node, !- Outlet Node Name + Cooling Coil Load Profile Load Schedule, !- Load Schedule Name + 0.005, !- Peak Flow Rate {m3/s} + Cooling Coil Load Profile Flow Frac Schedule, !- Flow Rate Fraction Schedule Name + ; !- Plant Loop Fluid Type + + OutdoorAir:Node, + Outdoor Air Inlet Node; !- Name + + OutdoorAir:Node, + Outdoor Air 3 Inlet Node; !- Name + + OutdoorAir:Node, + Outdoor Air 2 Inlet Node; !- Name + + OutdoorAir:Node, + Outdoor Air Outlet Node; !- Name + + OutdoorAir:Node, + Outdoor Air 3 Outlet Node; !- Name + + OutdoorAir:Node, + Outdoor Air 2 Outlet Node; !- Name + + Output:VariableDictionary,IDF; + + Output:Variable,*,Plant Load Profile Mass Flow Rate,Hourly; + + Output:Variable,*,Plant Load Profile Heat Transfer Rate,Hourly; + + Output:Variable,*,Heat Pump Load Side Heat Transfer Rate,Hourly; + + Output:Variable,*,Heat Pump Load Side Heat Transfer Energy,Hourly; + + Output:Variable,*,Heat Pump Source Side Heat Transfer Rate,Hourly; + + Output:Variable,*,Heat Pump Source Side Heat Transfer Energy,Hourly; + + Output:Variable,*,Heat Pump Load Side Inlet Temperature,Hourly; + + Output:Variable,*,Heat Pump Load Side Outlet Temperature,Hourly; + + Output:Variable,*,Heat Pump Source Side Inlet Temperature,Hourly; + + Output:Variable,*,Heat Pump Source Side Outlet Temperature,Hourly; + + Output:Variable,*,Heat Pump Electricity Rate,Hourly; + + Output:Variable,*,Heat Pump Electricity Energy,Hourly; + + Output:Variable,*,Heat Pump Load Side Mass Flow Rate,Hourly; + + Output:Variable,*,Heat Pump Source Side Mass Flow Rate,Hourly; + diff --git a/tst/EnergyPlus/unit/PlantLoopHeatPumpEIR.unit.cc b/tst/EnergyPlus/unit/PlantLoopHeatPumpEIR.unit.cc index 807dc9094f9..744ade1376e 100644 --- a/tst/EnergyPlus/unit/PlantLoopHeatPumpEIR.unit.cc +++ b/tst/EnergyPlus/unit/PlantLoopHeatPumpEIR.unit.cc @@ -3994,15 +3994,16 @@ TEST_F(EnergyPlusFixture, Initialization2_AirSource) // do a bit of extra wiring up to the plant PLHPPlantLoadSideComp.Name = thisCoolingPLHP->name; PLHPPlantLoadSideComp.NodeNumIn = thisCoolingPLHP->loadSideNodes.inlet; + PLHPPlantLoadSideComp.NodeNumOut = thisCoolingPLHP->loadSideNodes.outlet; // call for all initialization state->dataGlobal->BeginEnvrnFlag = true; state->dataPlnt->PlantFirstSizesOkayToFinalize = true; thisCoolingPLHP->onInitLoopEquip(*state, myLocation); - // Componnent flow control type + // Component flow control type auto &comp = DataPlant::CompData::getPlantComponent(*state, thisCoolingPLHP->loadSidePlantLoc); - comp.FlowCtrl = DataBranchAirLoopPlant::ControlType::Active; + comp.FlowCtrl = DataBranchAirLoopPlant::ControlType::SeriesActive; // call with run flag off, loose limits on node min/max thisCoolingPLHP->running = false; @@ -4034,17 +4035,13 @@ TEST_F(EnergyPlusFixture, Initialization2_AirSource) EXPECT_NEAR(0.0, thisCoolingPLHP->loadSideMassFlowRate, 0.001); EXPECT_NEAR(0, thisCoolingPLHP->sourceSideMassFlowRate, 0.001); - // call with run flag ON, flow locked at zero on source side since there's no load + // call with run flag ON, flow locked at zero on source side state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).FlowLock = DataPlant::FlowLock::Locked; state->dataLoopNodes->Node(thisCoolingPLHP->loadSideNodes.inlet).MassFlowRate = 0.2; thisCoolingPLHP->running = true; thisCoolingPLHP->setOperatingFlowRatesASHP(*state, firstHVACIteration, currentLoad); EXPECT_NEAR(0.2, thisCoolingPLHP->loadSideMassFlowRate, 0.001); - EXPECT_NEAR(0.0, thisCoolingPLHP->sourceSideMassFlowRate, 0.001); - - comp.FlowCtrl = DataBranchAirLoopPlant::ControlType::SeriesActive; - thisCoolingPLHP->setOperatingFlowRatesASHP(*state, firstHVACIteration, currentLoad); - EXPECT_NEAR(1.29, thisCoolingPLHP->sourceSideMassFlowRate, 0.01); + EXPECT_NEAR(1.29, thisCoolingPLHP->sourceSideMassFlowRate, 0.1); // call with run flag ON, flow locked at zero on both sides state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).FlowLock = DataPlant::FlowLock::Locked; @@ -4060,6 +4057,19 @@ TEST_F(EnergyPlusFixture, Initialization2_AirSource) thisCoolingPLHP->running = true; thisCoolingPLHP->setOperatingFlowRatesASHP(*state, firstHVACIteration, currentLoad); EXPECT_NEAR(0.14, thisCoolingPLHP->loadSideMassFlowRate, 0.001); + EXPECT_NEAR(1.29, thisCoolingPLHP->sourceSideMassFlowRate, 0.1); + + // Change control type + comp.FlowCtrl = DataBranchAirLoopPlant::ControlType::Active; + state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).FlowLock = DataPlant::FlowLock::Unlocked; + state->dataLoopNodes->Node(thisCoolingPLHP->loadSideNodes.inlet).MassFlowRateMinAvail = 0.0; + thisCoolingPLHP->running = true; + thisCoolingPLHP->setOperatingFlowRatesASHP(*state, firstHVACIteration, currentLoad); // no load + EXPECT_NEAR(0.0, thisCoolingPLHP->loadSideMassFlowRate, 0.001); + EXPECT_NEAR(0.0, thisCoolingPLHP->sourceSideMassFlowRate, 0.001); + thisCoolingPLHP->running = true; + thisCoolingPLHP->setOperatingFlowRatesASHP(*state, firstHVACIteration, -5000); + EXPECT_NEAR(1.0, thisCoolingPLHP->loadSideMassFlowRate, 0.01); EXPECT_NEAR(1.29, thisCoolingPLHP->sourceSideMassFlowRate, 0.01); } @@ -5282,7704 +5292,9 @@ TEST_F(EnergyPlusFixture, TestOperatingFlowRates_FullyAutosized_AirSource) // assign the plant sizing data state->dataPlnt->PlantLoop(1).PlantSizNum = 1; - // call with run flag ON, flow locked at nonzero both - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).FlowLock = DataPlant::FlowLock::Locked; - state->dataLoopNodes->Node(thisCoolingPLHP->loadSideNodes.inlet).MassFlowRate = 1.0; - thisCoolingPLHP->running = true; - thisCoolingPLHP->sizeLoadSide(*state); - thisCoolingPLHP->sizeSrcSideASHP(*state); - Real64 constexpr currentLoad = 0.0; - thisCoolingPLHP->setOperatingFlowRatesASHP(*state, firstHVACIteration, currentLoad); - EXPECT_NEAR(1.0, thisCoolingPLHP->loadSideMassFlowRate, 0.001); - EXPECT_TRUE(thisCoolingPLHP->running); - - // test thermosiphon model - state->dataLoopNodes->Node(thisCoolingPLHP->loadSideNodes.inlet).Temp = 10.0; - state->dataLoopNodes->Node(thisCoolingPLHP->loadSideNodes.outlet).Temp = 6.0; - state->dataLoopNodes->Node(thisCoolingPLHP->loadSideNodes.outlet).TempSetPoint = 6.0; - state->dataLoopNodes->Node(thisCoolingPLHP->sourceSideNodes.inlet).Temp = 12.0; // condenser inlet temp > evap outlet temp - state->dataPlnt->PlantLoop(1).LoopDemandCalcScheme = DataPlant::LoopDemandCalcScheme::SingleSetPoint; - state->dataPlnt->PlantLoop(1).TempSetPointNodeNum = thisCoolingPLHP->loadSideNodes.outlet; - Real64 CurLoad = -20000.0; - bool RunFlag = true; - EnergyPlus::PlantLocation calledFromLocation = PlantLocation(1, DataPlant::LoopSideLocation::Supply, 1, 1); - - thisCoolingPLHP->simulate(*state, calledFromLocation, firstHVACIteration, CurLoad, RunFlag); - EXPECT_GT(thisCoolingPLHP->partLoadRatio, 0.4); // load is large - EXPECT_EQ(thisCoolingPLHP->thermosiphonStatus, 0); // thermosiphon is off - EXPECT_GT(thisCoolingPLHP->powerUsage, 6300.0); // power is non-zero - - state->dataLoopNodes->Node(thisCoolingPLHP->sourceSideNodes.inlet).Temp = 5.0; // condenser inlet temp < evap outlet temp - - thisCoolingPLHP->simulate(*state, calledFromLocation, firstHVACIteration, CurLoad, RunFlag); - EXPECT_GT(thisCoolingPLHP->partLoadRatio, 0.4); // load is large - EXPECT_EQ(thisCoolingPLHP->thermosiphonStatus, 0); // thermosiphon is off - EXPECT_GT(thisCoolingPLHP->powerUsage, 6300.0); // power is non-zero - - CurLoad /= 20.0; // reduce load such that thermosiphon can meet load - thisCoolingPLHP->simulate(*state, calledFromLocation, firstHVACIteration, CurLoad, RunFlag); - EXPECT_GT(thisCoolingPLHP->partLoadRatio, 0.02); // load is small - EXPECT_EQ(thisCoolingPLHP->thermosiphonStatus, 1); // thermosiphon is on - EXPECT_EQ(thisCoolingPLHP->powerUsage, 0.0); // power is zero -} - -TEST_F(EnergyPlusFixture, Test_Curve_Negative_Energy) -{ - - std::string const idf_objects = delimited_string({"HeatPump:PlantLoop:EIR:Cooling,", - " hp cooling side,", - " node 1,", - " node 2,", - " WaterSource,", - " node 3,", - " node 4,", - " ,", - " ,", - " hp heating side,", - " 0.005,", - " 0.002,", - " ,", - " 20000,", - " 3.0,", - " 1,", - " CapCurveFuncTemp,", - " EIRCurveFuncTemp,", - " badEIRCurveFuncPLR;", - "HeatPump:PlantLoop:EIR:Heating,", - " hp heating side,", - " node 5,", - " node 6,", - " WaterSource,", - " node 7,", - " node 8,", - " ,", - " ,", - " hp cooling side,", - " 0.005,", - " 0.002,", - " ,", - " 20000,", - " 3.0,", - " 1,", - " CapCurveFuncTemp,", - " EIRCurveFuncTemp,", - " EIRCurveFuncPLR;", - "Curve:Biquadratic,", - " CapCurveFuncTemp,", - " 1.0,", - " 0.0,", - " 0.0,", - " 0.0,", - " 0.0,", - " 0.0,", - " 5.0,", - " 10.0,", - " 24.0,", - " 35.0,", - " ,", - " ,", - " Temperature,", - " Temperature,", - " Dimensionless;", - "Curve:Biquadratic,", - " EIRCurveFuncTemp,", - " 1.0,", - " 0.0,", - " 0.0,", - " 1.0,", - " 0.0,", - " 0.0,", - " 5.0,", - " 10.0,", - " 24.0,", - " 35.0,", - " ,", - " ,", - " Temperature,", - " Temperature,", - " Dimensionless;", - "Curve:Quadratic,", - " EIRCurveFuncPLR,", - " 1.0,", - " 0.0,", - " 0.0,", - " 0.0,", - " 1.0;" - "Curve:Quadratic,", - " badEIRCurveFuncPLR,", - " -1.0,", - " 0.0,", - " 0.0,", - " 0.0,", - " 1.0;"}); - - ASSERT_TRUE(process_idf(idf_objects)); - state->init_state(*state); - - // set up the plant loops - // first the load side - state->dataPlnt->TotNumLoops = 2; - state->dataPlnt->PlantLoop.allocate(2); - - state->dataPlnt->PlantLoop(1).FluidName = "WATER"; - state->dataPlnt->PlantLoop(1).glycol = Fluid::GetWater(*state); - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).TotalBranches = 1; - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch.allocate(1); - 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 &PLHPPlantLoadSideLoop = state->dataPlnt->PlantLoop(1); - auto &PLHPPlantLoadSideComp = state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp(1); - PLHPPlantLoadSideComp.Type = DataPlant::PlantEquipmentType::HeatPumpEIRCooling; - // then the source side - - state->dataPlnt->PlantLoop(2).FluidName = "WATER"; - state->dataPlnt->PlantLoop(2).glycol = Fluid::GetWater(*state); - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).TotalBranches = 1; - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch.allocate(1); - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).TotalComponents = 1; - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp.allocate(1); - - auto &PLHPPlantLoadSourceComp = state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp(1); - PLHPPlantLoadSourceComp.Type = DataPlant::PlantEquipmentType::HeatPumpEIRCooling; - - // call the factory with a valid name to trigger reading inputs - EIRPlantLoopHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpEIRCooling, "HP COOLING SIDE"); - - // verify the size of the vector and the processed condition - EXPECT_EQ(2u, state->dataEIRPlantLoopHeatPump->heatPumps.size()); - - // for now we know the order is maintained, so get each heat pump object - EIRPlantLoopHeatPump *thisCoolingPLHP = &state->dataEIRPlantLoopHeatPump->heatPumps[0]; - - // do a little setup here - thisCoolingPLHP->loadSidePlantLoc.loopNum = 1; - thisCoolingPLHP->loadSidePlantLoc.loopSideNum = DataPlant::LoopSideLocation::Supply; - thisCoolingPLHP->loadSidePlantLoc.branchNum = 1; - thisCoolingPLHP->loadSidePlantLoc.compNum = 1; - PlantUtilities::SetPlantLocationLinks(*state, thisCoolingPLHP->loadSidePlantLoc); - thisCoolingPLHP->loadSideNodes.outlet = 1; - thisCoolingPLHP->sourceSidePlantLoc.loopNum = 2; - PlantUtilities::SetPlantLocationLinks(*state, thisCoolingPLHP->sourceSidePlantLoc); - - // the factory would've called GetOnlySingleNode for the in/out pairs on the PLHP, add another one for the loop - // outlet setpoint node - state->dataLoopNodes->Node.allocate(5); - PLHPPlantLoadSideLoop.TempSetPointNodeNum = 5; - - // set up the plant setpoint conditions and test for single setpoint operation - PLHPPlantLoadSideLoop.LoopDemandCalcScheme = DataPlant::LoopDemandCalcScheme::SingleSetPoint; - PLHPPlantLoadSideComp.CurOpSchemeType = DataPlant::OpScheme::CompSetPtBased; - state->dataLoopNodes->Node(thisCoolingPLHP->loadSideNodes.outlet).TempSetPoint = 3.141; - state->dataLoopNodes->Node(5).TempSetPoint = 2.718; - PLHPPlantLoadSideComp.CurOpSchemeType = DataPlant::OpScheme::CoolingRB; - - // test for dual setpoint operation - PLHPPlantLoadSideLoop.LoopDemandCalcScheme = DataPlant::LoopDemandCalcScheme::DualSetPointDeadBand; - PLHPPlantLoadSideComp.CurOpSchemeType = DataPlant::OpScheme::CompSetPtBased; - state->dataLoopNodes->Node(thisCoolingPLHP->loadSideNodes.outlet).TempSetPointHi = 6.282; - state->dataLoopNodes->Node(5).TempSetPointHi = 5.436; - PLHPPlantLoadSideComp.CurOpSchemeType = DataPlant::OpScheme::CoolingRB; - - state->dataHVACGlobal->TimeStepSys = 60; - state->dataHVACGlobal->TimeStepSysSec = state->dataHVACGlobal->TimeStepSys * Constant::rSecsInHour; - - Real64 curLoad = -10000; - - thisCoolingPLHP->loadSideMassFlowRate = 0.3; - thisCoolingPLHP->sourceSideMassFlowRate = 0.8; - thisCoolingPLHP->loadSideInletTemp = 20; - thisCoolingPLHP->sourceSideInletTemp = 20; - thisCoolingPLHP->doPhysics(*state, curLoad); - thisCoolingPLHP->report(*state); - - // Power and energy are now zero since the curve is reset with zero values - EXPECT_NEAR(thisCoolingPLHP->powerUsage, 0.000, 1e-3); - EXPECT_NEAR(thisCoolingPLHP->powerEnergy, 0.000, 1e-3); - - EXPECT_NEAR(thisCoolingPLHP->sourceSideHeatTransfer, 10000.000, 1e-3); - EXPECT_NEAR(thisCoolingPLHP->sourceSideEnergy, 2160000000.000, 1e-3); - - EXPECT_NEAR(thisCoolingPLHP->loadSideOutletTemp, 12.095, 1e-3); - - EXPECT_NEAR(thisCoolingPLHP->sourceSideOutletTemp, 22.989, 1e-3); - - EXPECT_EQ(thisCoolingPLHP->eirModFPLRErrorIndex, 1); - - EXPECT_EQ(state->dataErrTracking->TotalWarningErrors, 1); - EXPECT_EQ(state->dataErrTracking->TotalSevereErrors, 0); - EXPECT_EQ(state->dataErrTracking->LastSevereError, "HeatPump:PlantLoop:EIR:Cooling \"HP COOLING SIDE\":"); - - EXPECT_EQ(state->dataErrTracking->RecurringErrors(1).Count, 1); - EXPECT_EQ(state->dataErrTracking->RecurringErrors(1).Message, - " ** Warning ** HeatPump:PlantLoop:EIR:Cooling \"HP COOLING SIDE\": EIR Modifier curve (function of PLR) output is negative warning " - "continues..."); -} - -TEST_F(EnergyPlusFixture, GAHP_HeatingConstructionFullObjectsNoCompanion) -{ - std::string const idf_objects = delimited_string({"HeatPump:AirToWater:FuelFired:Heating,", - " Fuel Fired hp heating side, ! A1", - " node w1, ! A2", - " node w2, ! A3", - " node a3, ! A4", - " , ! A5 Comanion coil", - " NaturalGas, ! A6 fuel type", - " GAHP_Custom, ! A7 end use cat", - " 3000, ! N1 capacity", - " 1.5, ! N2 nominal COP", - " 0.005, ! N3 design flow rate", - " 60, ! N4 Design Supply Temp", - " 11.1, ! N5 Design Lift", - " 1.0, ! N6 sizing factor", - " NotModulated, ! A8 flow mode", - " DryBulb, ! A9 oa temp var type", - " EnteringCondenser, ! A10 oa temp var type", - " CapCurveFuncTemp, ! A11 CapFoT", - " EIRCurveFuncTemp, ! A12 EIRFoT", - " EIRCurveFuncPLR, ! A13 EIRFoPLR", - " 0.2, ! N7 minPLR", - " 1.0, ! N8 maxPLR", - " OnDemand, ! A14 defrost control type", - " , ! N9 defrost time frac", - " , ! A15 EIRdefrost curve", - " 3.0, ! N10 max oa DBT for defrost", - " , ! N11 resistive defrost heater capacity", - " uniCRFCurve5, ! A16 crf curve name", - " 500, ! N12 nominal aux elec power", - " EIRCurveFuncTemp, ! A17 EIRAuxFoT", - " uniAuxElecEIRFoPLRCurve6, ! A18 EIRAuxFoPLR", - " 20; ! N13 standby elec power", - - "Curve:Biquadratic,", - " CapCurveFuncTemp,", - " 1.0,", - " 0.0,", - " 0.0,", - " 0.0,", - " 0.0,", - " 0.0,", - " 5.0,", - " 10.0,", - " 24.0,", - " 35.0,", - " ,", - " ,", - " Temperature,", - " Temperature,", - " Dimensionless;", - "Curve:Biquadratic,", - " EIRCurveFuncTemp,", - " 1.0,", - " 0.0,", - " 0.0,", - " 1.0,", - " 0.0,", - " 0.0,", - " 5.0,", - " 10.0,", - " 24.0,", - " 35.0,", - " ,", - " ,", - " Temperature,", - " Temperature,", - " Dimensionless;", - "Curve:Quadratic,", - " EIRCurveFuncPLR,", - " 1.0,", - " 0.0,", - " 0.0,", - " 0.0,", - " 1.0;" - - "Curve:Linear,", - " uniDefrostCurve4,", - " 1,", - " 0,", - " 1,", - " 1;" - "Curve:Linear,", - " uniCRFCurve5,", - " 1,", - " 0,", - " 1,", - " 1;" - "Curve:Linear,", - " uniAuxElecEIRFoPLRCurve6,", - " 1,", - " 0,", - " 1,", - " 1;"}); - ASSERT_TRUE(process_idf(idf_objects)); - state->init_state(*state); - - // call the factory with a valid name to trigger reading inputs - EIRFuelFiredHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpFuelFiredHeating, "FUEL FIRED HP HEATING SIDE"); - - // verify the size of the vector and the processed condition - EXPECT_EQ(1u, state->dataEIRFuelFiredHeatPump->heatPumps.size()); - - // for now we know the order is maintained, so get each heat pump object - EIRFuelFiredHeatPump *thisHeatingPLHP = &state->dataEIRFuelFiredHeatPump->heatPumps[0]; - - // validate the heating side - EXPECT_EQ("FUEL FIRED HP HEATING SIDE", thisHeatingPLHP->name); - EXPECT_ENUM_EQ(DataPlant::PlantEquipmentType::HeatPumpFuelFiredHeating, thisHeatingPLHP->EIRHPType); - EXPECT_EQ(nullptr, thisHeatingPLHP->companionHeatPumpCoil); - EXPECT_EQ(1, thisHeatingPLHP->capFuncTempCurveIndex); - EXPECT_EQ(2, thisHeatingPLHP->powerRatioFuncTempCurveIndex); - EXPECT_EQ(3, thisHeatingPLHP->powerRatioFuncPLRCurveIndex); - EXPECT_EQ(0, thisHeatingPLHP->defrostEIRCurveIndex); - EXPECT_EQ(5, thisHeatingPLHP->cycRatioCurveIndex); - EXPECT_EQ(2, thisHeatingPLHP->auxElecEIRFoTempCurveIndex); - EXPECT_EQ(6, thisHeatingPLHP->auxElecEIRFoPLRCurveIndex); - - EXPECT_EQ(500.0, thisHeatingPLHP->nominalAuxElecPower); - EXPECT_EQ(20.0, thisHeatingPLHP->standbyElecPower); - - // calling the factory with an invalid name or type will call ShowFatalError, which will trigger a runtime exception - EXPECT_THROW(EIRFuelFiredHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpFuelFiredHeating, "fake"), std::runtime_error); - EXPECT_THROW(EIRFuelFiredHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpFuelFiredCooling, "FUEL FIRED HP HEATING SIDE"), - std::runtime_error); -} - -TEST_F(EnergyPlusFixture, GAHP_HeatingConstructionFullObjectsNoCompanion_with_Defrost) -{ - std::string const idf_objects = delimited_string({"HeatPump:AirToWater:FuelFired:Heating,", - " Fuel Fired hp heating side, ! A1", - " node w1, ! A2", - " node w2, ! A3", - " node a3, ! A4", - " , ! A5 Comanion coil", - " NaturalGas, ! A6 fuel type", - " GAHP_Custom, ! A7 end use cat", - " 3000, ! N1 capacity", - " 1.5, ! N2 nominal COP", - " 0.005, ! N3 design flow rate", - " 60, ! N4 Design Supply Temp", - " 11.1, ! N5 Design Lift", - " 1.0, ! N6 sizing factor", - " NotModulated, ! A8 flow mode", - " DryBulb, ! A9 oa temp var type", - " EnteringCondenser, ! A10 oa temp var type", - " CapCurveFuncTemp, ! A11 CapFoT", - " EIRCurveFuncTemp, ! A12 EIRFoT", - " EIRCurveFuncPLR, ! A13 EIRFoPLR", - " 0.2, ! N7 minPLR", - " 1.0, ! N8 maxPLR", - " OnDemand, ! A14 defrost control type", - " , ! N9 defrost time frac", - " uniDefrostCurve4, ! A15 EIRdefrost curve", - " 3.0, ! N10 max oa DBT for defrost", - " , ! N11 resistive defrost heater capacity", - " uniCRFCurve5, ! A16 crf curve name", - " 500, ! N12 nominal aux elec power", - " EIRCurveFuncTemp, ! A17 EIRAuxFoT", - " uniAuxElecEIRFoPLRCurve6, ! A18 EIRAuxFoPLR", - " 20; ! N13 standby elec power", - - "Curve:Biquadratic,", - " CapCurveFuncTemp,", - " 1.0,", - " 0.0,", - " 0.0,", - " 0.0,", - " 0.0,", - " 0.0,", - " 5.0,", - " 10.0,", - " 24.0,", - " 35.0,", - " ,", - " ,", - " Temperature,", - " Temperature,", - " Dimensionless;", - "Curve:Biquadratic,", - " EIRCurveFuncTemp,", - " 1.0,", - " 0.0,", - " 0.0,", - " 1.0,", - " 0.0,", - " 0.0,", - " 5.0,", - " 10.0,", - " 24.0,", - " 35.0,", - " ,", - " ,", - " Temperature,", - " Temperature,", - " Dimensionless;", - "Curve:Quadratic,", - " EIRCurveFuncPLR,", - " 1.0,", - " 0.0,", - " 0.0,", - " 0.0,", - " 1.0;" - - "Curve:Linear,", - " uniDefrostCurve4,", - " 1,", - " 0,", - " 1,", - " 1;" - "Curve:Linear,", - " uniCRFCurve5,", - " 1,", - " 0,", - " 1,", - " 1;" - "Curve:Linear,", - " uniAuxElecEIRFoPLRCurve6,", - " 1,", - " 0,", - " 1,", - " 1;"}); - ASSERT_TRUE(process_idf(idf_objects)); - state->init_state(*state); - - // call the factory with a valid name to trigger reading inputs - EIRFuelFiredHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpFuelFiredHeating, "FUEL FIRED HP HEATING SIDE"); - - // verify the size of the vector and the processed condition - EXPECT_EQ(1u, state->dataEIRFuelFiredHeatPump->heatPumps.size()); - - // for now we know the order is maintained, so get each heat pump object - EIRFuelFiredHeatPump *thisHeatingPLHP = &state->dataEIRFuelFiredHeatPump->heatPumps[0]; - - // validate the heating side - EXPECT_EQ("FUEL FIRED HP HEATING SIDE", thisHeatingPLHP->name); - EXPECT_ENUM_EQ(DataPlant::PlantEquipmentType::HeatPumpFuelFiredHeating, thisHeatingPLHP->EIRHPType); - EXPECT_EQ(nullptr, thisHeatingPLHP->companionHeatPumpCoil); - EXPECT_EQ(1, thisHeatingPLHP->capFuncTempCurveIndex); - EXPECT_EQ(2, thisHeatingPLHP->powerRatioFuncTempCurveIndex); - EXPECT_EQ(3, thisHeatingPLHP->powerRatioFuncPLRCurveIndex); - EXPECT_EQ(4, thisHeatingPLHP->defrostEIRCurveIndex); - EXPECT_EQ(5, thisHeatingPLHP->cycRatioCurveIndex); - EXPECT_EQ(2, thisHeatingPLHP->auxElecEIRFoTempCurveIndex); - EXPECT_EQ(6, thisHeatingPLHP->auxElecEIRFoPLRCurveIndex); - - EXPECT_EQ(500.0, thisHeatingPLHP->nominalAuxElecPower); - EXPECT_EQ(20.0, thisHeatingPLHP->standbyElecPower); - - // calling the factory with an invalid name or type will call ShowFatalError, which will trigger a runtime exception - EXPECT_THROW(EIRFuelFiredHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpFuelFiredHeating, "fake"), std::runtime_error); - EXPECT_THROW(EIRFuelFiredHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpFuelFiredCooling, "FUEL FIRED HP HEATING SIDE"), - std::runtime_error); -} - -TEST_F(EnergyPlusFixture, GAHP_Initialization_Test) -{ - std::string const idf_objects = delimited_string({"HeatPump:AirToWater:FuelFired:Heating,", - " Fuel Fired hp heating side, ! A1", - " node w1, ! A2", - " node w2, ! A3", - " node a3, ! A4", - " , ! A5 Comanion coil", - " NaturalGas, ! A6 fuel type", - " GAHP_Custom, ! A7 end use cat", - " 3000, ! N1 capacity", - " 1.5, ! N2 nominal COP", - " 0.005, ! N3 design flow rate", - " 60, ! N4 Design Supply Temp", - " 11.1, ! N5 Design Lift", - " 1.0, ! N6 sizing factor", - " NotModulated, ! A8 flow mode", - " DryBulb, ! A9 oa temp var type", - " EnteringCondenser, ! A10 oa temp var type", - " CapCurveFuncTemp, ! A11 CapFoT", - " EIRCurveFuncTemp, ! A12 EIRFoT", - " EIRCurveFuncPLR, ! A13 EIRFoPLR", - " 0.2, ! N7 minPLR", - " 1.0, ! N8 maxPLR", - " OnDemand, ! A14 defrost control type", - " , ! N9 defrost time frac", - " , ! A15 EIRdefrost curve", - " , ! N10 resistive defrost heater capacity", - " 3.0, ! N11 max oa DBT for defrost", - " uniCRFCurve5, ! A16 crf curve name", - " 500, ! N12 nominal aux elec power", - " EIRCurveFuncTemp, ! A17 EIRAuxFoT", - " uniAuxElecEIRFoPLRCurve6, ! A18 EIRAuxFoPLR", - " 20; ! N13 standby elec power", - - "Curve:Biquadratic,", - " CapCurveFuncTemp,", - " 1.0,", - " 0.0,", - " 0.0,", - " 0.0,", - " 0.0,", - " 0.0,", - " 5.0,", - " 10.0,", - " 24.0,", - " 35.0,", - " ,", - " ,", - " Temperature,", - " Temperature,", - " Dimensionless;", - "Curve:Biquadratic,", - " EIRCurveFuncTemp,", - " 1.0,", - " 0.0,", - " 0.0,", - " 1.0,", - " 0.0,", - " 0.0,", - " 5.0,", - " 10.0,", - " 24.0,", - " 35.0,", - " ,", - " ,", - " Temperature,", - " Temperature,", - " Dimensionless;", - "Curve:Quadratic,", - " EIRCurveFuncPLR,", - " 1.0,", - " 0.0,", - " 0.0,", - " 0.0,", - " 1.0;" - - "Curve:Linear,", - " uniDefrostCurve4,", - " 1,", - " 0,", - " 1,", - " 1;" - "Curve:Linear,", - " uniCRFCurve5,", - " 1,", - " 0,", - " 1,", - " 1;" - "Curve:Linear,", - " uniAuxElecEIRFoPLRCurve6,", - " 1,", - " 0,", - " 1,", - " 1;"}); - ASSERT_TRUE(process_idf(idf_objects)); - state->init_state(*state); - - bool firstHVACIteration = true; - // set up the plant loops - // first the load side - state->dataPlnt->TotNumLoops = 1; - state->dataPlnt->PlantLoop.allocate(1); - - state->dataPlnt->PlantLoop(1).FluidName = "WATER"; - state->dataPlnt->PlantLoop(1).glycol = Fluid::GetWater(*state); - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).TotalBranches = 1; - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch.allocate(1); - 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 &PLHPPlantLoadSideComp = state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp(1); - PLHPPlantLoadSideComp.Type = DataPlant::PlantEquipmentType::HeatPumpFuelFiredHeating; - - // the init call expects a "from" calling point - PlantLocation myLocation = PlantLocation(1, DataPlant::LoopSideLocation::Supply, 1, 1); - - // call the factory with a valid name to trigger reading inputs - EIRFuelFiredHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpFuelFiredHeating, "FUEL FIRED HP HEATING SIDE"); - - // verify the size of the vector and the processed condition - EXPECT_EQ(1u, state->dataEIRFuelFiredHeatPump->heatPumps.size()); - - // for now we know the order is maintained, so get each heat pump object - EIRFuelFiredHeatPump *thisHeatingPLHP = &state->dataEIRFuelFiredHeatPump->heatPumps[0]; - - // do a bit of extra wiring up to the plant - PLHPPlantLoadSideComp.Name = thisHeatingPLHP->name; - PLHPPlantLoadSideComp.NodeNumIn = thisHeatingPLHP->loadSideNodes.inlet; - - // call for all initialization - state->dataGlobal->BeginEnvrnFlag = true; - state->dataPlnt->PlantFirstSizesOkayToFinalize = true; - thisHeatingPLHP->onInitLoopEquip(*state, myLocation); - - // call with run flag off, loose limits on node min/max - thisHeatingPLHP->running = false; - Real64 constexpr currentLoad = 0.0; - thisHeatingPLHP->setOperatingFlowRatesASHP(*state, firstHVACIteration, currentLoad); - EXPECT_NEAR(0.0, thisHeatingPLHP->loadSideMassFlowRate, 0.001); - EXPECT_NEAR(0.0, thisHeatingPLHP->sourceSideMassFlowRate, 0.001); - - // call with run flag off, nonzero minimums - state->dataLoopNodes->Node(thisHeatingPLHP->loadSideNodes.inlet).MassFlowRateMinAvail = 0.1; - thisHeatingPLHP->running = false; - thisHeatingPLHP->setOperatingFlowRatesASHP(*state, firstHVACIteration, currentLoad); - EXPECT_NEAR(0.1, thisHeatingPLHP->loadSideMassFlowRate, 0.001); - EXPECT_NEAR(0, thisHeatingPLHP->sourceSideMassFlowRate, 0.001); - - // call with run flag off, load side flow locked - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).FlowLock = DataPlant::FlowLock::Locked; - state->dataLoopNodes->Node(thisHeatingPLHP->loadSideNodes.inlet).MassFlowRate = 0.24; - thisHeatingPLHP->running = false; - thisHeatingPLHP->setOperatingFlowRatesASHP(*state, firstHVACIteration, currentLoad); - EXPECT_NEAR(0.24, thisHeatingPLHP->loadSideMassFlowRate, 0.001); - EXPECT_NEAR(0.0, thisHeatingPLHP->sourceSideMassFlowRate, 0.001); - - // call with run flag ON, flow locked at zero on load side - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).FlowLock = DataPlant::FlowLock::Locked; - state->dataLoopNodes->Node(thisHeatingPLHP->loadSideNodes.inlet).MassFlowRate = 0.0; - thisHeatingPLHP->running = true; - thisHeatingPLHP->setOperatingFlowRatesASHP(*state, firstHVACIteration, currentLoad); - EXPECT_NEAR(0.0, thisHeatingPLHP->loadSideMassFlowRate, 0.001); - EXPECT_NEAR(0, thisHeatingPLHP->sourceSideMassFlowRate, 0.001); - - // call with run flag ON, flow locked at zero on source side - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).FlowLock = DataPlant::FlowLock::Locked; - state->dataLoopNodes->Node(thisHeatingPLHP->loadSideNodes.inlet).MassFlowRate = 0.2; - thisHeatingPLHP->running = true; - thisHeatingPLHP->setOperatingFlowRatesASHP(*state, firstHVACIteration, currentLoad); - EXPECT_NEAR(0.2, thisHeatingPLHP->loadSideMassFlowRate, 0.001); - EXPECT_NEAR(1.29, thisHeatingPLHP->sourceSideMassFlowRate, 0.1); - - // call with run flag ON, flow locked at zero on both sides - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).FlowLock = DataPlant::FlowLock::Locked; - state->dataLoopNodes->Node(thisHeatingPLHP->loadSideNodes.inlet).MassFlowRate = 0.0; - thisHeatingPLHP->running = true; - thisHeatingPLHP->setOperatingFlowRatesASHP(*state, firstHVACIteration, currentLoad); - EXPECT_NEAR(0.0, thisHeatingPLHP->loadSideMassFlowRate, 0.001); - EXPECT_NEAR(0.0, thisHeatingPLHP->sourceSideMassFlowRate, 0.001); - - // call with run flag ON, flow locked at nonzero both - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).FlowLock = DataPlant::FlowLock::Locked; - state->dataLoopNodes->Node(thisHeatingPLHP->loadSideNodes.inlet).MassFlowRate = 0.14; - thisHeatingPLHP->running = true; - thisHeatingPLHP->setOperatingFlowRatesASHP(*state, firstHVACIteration, currentLoad); - EXPECT_NEAR(0.14, thisHeatingPLHP->loadSideMassFlowRate, 0.001); - EXPECT_NEAR(1.29, thisHeatingPLHP->sourceSideMassFlowRate, 0.1); -} - -TEST_F(EnergyPlusFixture, GAHP_HeatingSimulate_AirSource) -{ - std::string const idf_objects = delimited_string({"HeatPump:AirToWater:FuelFired:Heating,", - " Fuel Fired hp heating side, ! A1", - " node w1, ! A2", - " node w2, ! A3", - " node a3, ! A4", - " , ! A5 Comanion coil", - " NaturalGas, ! A6 fuel type", - " GAHP_Custom, ! A7 end use cat", - " 3000, ! N1 capacity", - " 1.5, ! N2 nominal COP", - " 0.005, ! N3 design flow rate", - " 60, ! N4 Design Supply Temp", - " 11.1, ! N5 Design Lift", - " 1.0, ! N6 sizing factor", - " NotModulated, ! A8 flow mode", - " DryBulb, ! A9 oa temp var type", - " EnteringCondenser, ! A10 oa temp var type", - " CapCurveFuncTemp, ! A11 CapFoT", - " EIRCurveFuncTemp, ! A12 EIRFoT", - " EIRCurveFuncPLR, ! A13 EIRFoPLR", - " 0.2, ! N7 minPLR", - " 1.0, ! N8 maxPLR", - " OnDemand, ! A14 defrost control type", - " , ! N9 defrost time frac", - " , ! A15 EIRdefrost curve", - " , ! N10 resistive defrost heater capacity", - " 3.0, ! N11 max oa DBT for defrost", - " uniCRFCurve5, ! A16 crf curve name", - " 500, ! N12 nominal aux elec power", - " EIRCurveFuncTemp, ! A17 EIRAuxFoT", - " uniAuxElecEIRFoPLRCurve6, ! A18 EIRAuxFoPLR", - " 20; ! N13 standby elec power", - - "Curve:Biquadratic,", - " CapCurveFuncTemp,", - " 1.0,", - " 0.0,", - " 0.0,", - " 0.0,", - " 0.0,", - " 0.0,", - " 5.0,", - " 10.0,", - " 24.0,", - " 35.0,", - " ,", - " ,", - " Temperature,", - " Temperature,", - " Dimensionless;", - "Curve:Biquadratic,", - " EIRCurveFuncTemp,", - " 1.0,", - " 0.0,", - " 0.0,", - " 1.0,", - " 0.0,", - " 0.0,", - " 5.0,", - " 10.0,", - " 24.0,", - " 35.0,", - " ,", - " ,", - " Temperature,", - " Temperature,", - " Dimensionless;", - "Curve:Quadratic,", - " EIRCurveFuncPLR,", - " 1.0,", - " 0.0,", - " 0.0,", - " 0.0,", - " 1.0;" - - "Curve:Linear,", - " uniDefrostCurve4,", - " 1,", - " 0,", - " 1,", - " 1;" - "Curve:Linear,", - " uniCRFCurve5,", - " 1,", - " 0,", - " 1,", - " 1;" - "Curve:Linear,", - " uniAuxElecEIRFoPLRCurve6,", - " 1,", - " 0,", - " 1,", - " 1;"}); - - ASSERT_TRUE(process_idf(idf_objects)); - state->init_state(*state); - - // set up the plant loops - // first the load side - state->dataPlnt->TotNumLoops = 1; - state->dataPlnt->PlantLoop.allocate(1); - - state->dataPlnt->PlantLoop(1).FluidName = "WATER"; - state->dataPlnt->PlantLoop(1).glycol = Fluid::GetWater(*state); - state->dataPlnt->PlantLoop(1).LoopDemandCalcScheme = DataPlant::LoopDemandCalcScheme::SingleSetPoint; - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).TotalBranches = 1; - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch.allocate(1); - 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 &PLHPPlantLoadSideComp = state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp(1); - PLHPPlantLoadSideComp.Type = DataPlant::PlantEquipmentType::HeatPumpFuelFiredHeating; - PLHPPlantLoadSideComp.CurOpSchemeType = DataPlant::OpScheme::CompSetPtBased; - - // the init call expects a "from" calling point - PlantLocation myLoadLocation = PlantLocation(1, DataPlant::LoopSideLocation::Supply, 1, 1); - - // call the factory with a valid name to trigger reading inputs - EIRFuelFiredHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpFuelFiredHeating, "FUEL FIRED HP HEATING SIDE"); - - // verify the size of the vector and the processed condition - EXPECT_EQ(1u, state->dataEIRFuelFiredHeatPump->heatPumps.size()); - - // for now we know the order is maintained, so get each heat pump object - EIRFuelFiredHeatPump *thisHeatingPLHP = &state->dataEIRFuelFiredHeatPump->heatPumps[0]; - - // do a bit of extra wiring up to the plant - PLHPPlantLoadSideComp.Name = thisHeatingPLHP->name; - PLHPPlantLoadSideComp.NodeNumIn = thisHeatingPLHP->loadSideNodes.inlet; - - // call for all initialization - state->dataGlobal->BeginEnvrnFlag = true; - state->dataPlnt->PlantFirstSizesOkayToFinalize = true; - thisHeatingPLHP->onInitLoopEquip(*state, myLoadLocation); - - // do a runflag = false to test out execution order - { - bool firstHVAC = true; - Real64 curLoad = -900; - bool runFlag = false; - Real64 constexpr specifiedLoadSetpoint = 45; - Real64 constexpr loadInletTemp = 46; - state->dataLoopNodes->Node(thisHeatingPLHP->loadSideNodes.outlet).TempSetPoint = specifiedLoadSetpoint; - state->dataLoopNodes->Node(thisHeatingPLHP->loadSideNodes.inlet).Temp = loadInletTemp; - state->dataLoopNodes->Node(thisHeatingPLHP->sourceSideNodes.inlet).Temp = 30; - thisHeatingPLHP->simulate(*state, myLoadLocation, firstHVAC, curLoad, runFlag); - } - - // call it from the load side, but this time there is a negative (cooling) load - shouldn't try to run - { - bool firstHVAC = true; - Real64 curLoad = -900; - bool runFlag = true; // plant actually shouldn't do this but the component can be smart enough to handle it - Real64 constexpr specifiedLoadSetpoint = 45; - Real64 constexpr loadInletTemp = 46; - state->dataLoopNodes->Node(thisHeatingPLHP->loadSideNodes.outlet).TempSetPoint = specifiedLoadSetpoint; - state->dataLoopNodes->Node(thisHeatingPLHP->loadSideNodes.inlet).Temp = loadInletTemp; - state->dataLoopNodes->Node(thisHeatingPLHP->sourceSideNodes.inlet).Temp = 30; - thisHeatingPLHP->simulate(*state, myLoadLocation, firstHVAC, curLoad, runFlag); - // expect it to meet setpoint and have some pre-evaluated conditions - EXPECT_NEAR(loadInletTemp, thisHeatingPLHP->loadSideOutletTemp, 0.001); - EXPECT_NEAR(0.0, thisHeatingPLHP->loadSideHeatTransfer, 0.001); - } - - // call it from the load side, but this time there is load (still firsthvac, unit can meet load) - { - bool firstHVAC = true; - Real64 curLoad = 800; - bool runFlag = true; - Real64 constexpr expectedLoadMassFlowRate = 0.09999; - Real64 constexpr expectedCp = 4180; - Real64 constexpr specifiedLoadSetpoint = 45; - Real64 const calculatedLoadInletTemp = specifiedLoadSetpoint - curLoad / (expectedLoadMassFlowRate * expectedCp); - state->dataLoopNodes->Node(thisHeatingPLHP->loadSideNodes.outlet).TempSetPoint = specifiedLoadSetpoint; - state->dataLoopNodes->Node(thisHeatingPLHP->loadSideNodes.inlet).Temp = calculatedLoadInletTemp; - state->dataLoopNodes->Node(thisHeatingPLHP->sourceSideNodes.inlet).Temp = 30; - thisHeatingPLHP->simulate(*state, myLoadLocation, 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); - } - - // now we can call it again from the load side, but this time there is load (still firsthvac, unit cannot meet load) - { - bool firstHVAC = true; - Real64 curLoad = 1200; - // Real64 availableCapacity = 950.0; - bool runFlag = true; - Real64 constexpr expectedLoadMassFlowRate = 0.09999; - Real64 constexpr expectedCp = 4180; - Real64 constexpr specifiedLoadSetpoint = 45; - Real64 const calculatedLoadInletTemp = specifiedLoadSetpoint - curLoad / (expectedLoadMassFlowRate * expectedCp); - state->dataLoopNodes->Node(thisHeatingPLHP->loadSideNodes.outlet).TempSetPoint = specifiedLoadSetpoint; - state->dataLoopNodes->Node(thisHeatingPLHP->loadSideNodes.inlet).Temp = calculatedLoadInletTemp; - state->dataLoopNodes->Node(thisHeatingPLHP->sourceSideNodes.inlet).Temp = 30; - thisHeatingPLHP->simulate(*state, myLoadLocation, firstHVAC, curLoad, runFlag); - // expect it to miss setpoint and be at max capacity - // EXPECT_NEAR(44.402, thisHeatingPLHP->loadSideOutletTemp, 0.001); - // EXPECT_NEAR(availableCapacity, thisHeatingPLHP->loadSideHeatTransfer, 0.001); - } - - // now we can call it again from the load side, but this time there is no load (still firsthvac) - { - bool firstHVAC = true; - Real64 curLoad = 0.0; - bool runFlag = true; - Real64 constexpr expectedLoadMassFlowRate = 0.09999; - Real64 constexpr expectedCp = 4180; - Real64 constexpr specifiedLoadSetpoint = 45; - Real64 const calculatedLoadInletTemp = specifiedLoadSetpoint - curLoad / (expectedLoadMassFlowRate * expectedCp); - state->dataLoopNodes->Node(thisHeatingPLHP->loadSideNodes.outlet).TempSetPoint = specifiedLoadSetpoint; - state->dataLoopNodes->Node(thisHeatingPLHP->loadSideNodes.inlet).Temp = calculatedLoadInletTemp; - state->dataLoopNodes->Node(thisHeatingPLHP->sourceSideNodes.inlet).Temp = 30; - thisHeatingPLHP->simulate(*state, myLoadLocation, firstHVAC, curLoad, runFlag); - // expect it to miss setpoint and be at max capacity - EXPECT_NEAR(45.0, thisHeatingPLHP->loadSideOutletTemp, 0.001); - EXPECT_NEAR(30.0, thisHeatingPLHP->sourceSideOutletTemp, 0.001); - } - - // Test cycling calcs - { - bool firstHVAC = true; - Real64 curLoad = 500.0; - bool runFlag = true; - Real64 constexpr expectedLoadMassFlowRate = 0.09999; - Real64 constexpr expectedCp = 4180; - Real64 constexpr specifiedLoadSetpoint = 45; - Real64 const calculatedLoadInletTemp = specifiedLoadSetpoint - curLoad / (expectedLoadMassFlowRate * expectedCp); - state->dataLoopNodes->Node(thisHeatingPLHP->loadSideNodes.outlet).TempSetPoint = specifiedLoadSetpoint; - state->dataLoopNodes->Node(thisHeatingPLHP->loadSideNodes.inlet).Temp = calculatedLoadInletTemp; - state->dataLoopNodes->Node(thisHeatingPLHP->sourceSideNodes.inlet).Temp = 30; - // Use user specified curve - thisHeatingPLHP->simulate(*state, myLoadLocation, firstHVAC, curLoad, runFlag); - EXPECT_NEAR(1.0, thisHeatingPLHP->cyclingRatioFraction, 0.001); - // Use default assumptions - thisHeatingPLHP->cycRatioCurveIndex = 0; - thisHeatingPLHP->simulate(*state, myLoadLocation, firstHVAC, curLoad, runFlag); - EXPECT_NEAR(0.861, thisHeatingPLHP->cyclingRatioFraction, 0.001); - } - - // call it from the load side, very low load - { - bool firstHVAC = true; - Real64 curLoad = 100; - bool runFlag = true; - Real64 constexpr specifiedLoadSetpoint = 45; - state->dataLoopNodes->Node(thisHeatingPLHP->loadSideNodes.outlet).TempSetPoint = specifiedLoadSetpoint; - state->dataLoopNodes->Node(thisHeatingPLHP->loadSideNodes.inlet).Temp = 40; - state->dataLoopNodes->Node(thisHeatingPLHP->sourceSideNodes.inlet).Temp = 30; - thisHeatingPLHP->simulate(*state, myLoadLocation, firstHVAC, curLoad, runFlag); - EXPECT_TRUE(thisHeatingPLHP->fuelRate > 0); - EXPECT_NEAR(5.0, thisHeatingPLHP->loadSideMassFlowRate, 0.001); - } - - { - bool firstHVAC = false; - Real64 curLoad = 2000; - bool runFlag = true; - Real64 constexpr expectedLoadMassFlowRate = 0.0478; - Real64 constexpr specifiedLoadSetpoint = 45; - state->dataLoopNodes->Node(thisHeatingPLHP->loadSideNodes.outlet).TempSetPoint = specifiedLoadSetpoint; - state->dataLoopNodes->Node(thisHeatingPLHP->loadSideNodes.inlet).Temp = 35; - state->dataLoopNodes->Node(thisHeatingPLHP->sourceSideNodes.inlet).Temp = 30; - thisHeatingPLHP->flowMode = DataPlant::FlowMode::LeavingSetpointModulated; - thisHeatingPLHP->simulate(*state, myLoadLocation, firstHVAC, curLoad, runFlag); - EXPECT_NEAR(expectedLoadMassFlowRate, thisHeatingPLHP->loadSideMassFlowRate, 0.001); - } -} - -TEST_F(EnergyPlusFixture, GAHP_HeatingSimulate_AirSource_with_Defrost) -{ - std::string const idf_objects = delimited_string({"HeatPump:AirToWater:FuelFired:Heating,", - " Fuel Fired hp heating side, ! A1", - " node w1, ! A2", - " node w2, ! A3", - " node a3, ! A4", - " , ! A5 Comanion coil", - " NaturalGas, ! A6 fuel type", - " GAHP_Custom, ! A7 end use cat", - " 3000, ! N1 capacity", - " 1.5, ! N2 nominal COP", - " 0.005, ! N3 design flow rate", - " 60, ! N4 Design Supply Temp", - " 11.1, ! N5 Design Lift", - " 1.0, ! N6 sizing factor", - " NotModulated, ! A8 flow mode", - " DryBulb, ! A9 oa temp var type", - " EnteringCondenser, ! A10 oa temp var type", - " CapCurveFuncTemp, ! A11 CapFoT", - " EIRCurveFuncTemp, ! A12 EIRFoT", - " EIRCurveFuncPLR, ! A13 EIRFoPLR", - " 0.2, ! N7 minPLR", - " 1.0, ! N8 maxPLR", - " OnDemand, ! A14 defrost control type", - " , ! N9 defrost time frac", - " uniDefrostCurve4, ! A15 EIRdefrost curve", - " , ! N10 resistive defrost heater capacity", - " 3.0, ! N11 max oa DBT for defrost", - " uniCRFCurve5, ! A16 crf curve name", - " 500, ! N12 nominal aux elec power", - " EIRCurveFuncTemp, ! A17 EIRAuxFoT", - " uniAuxElecEIRFoPLRCurve6, ! A18 EIRAuxFoPLR", - " 20; ! N13 standby elec power", - - "Curve:Biquadratic,", - " CapCurveFuncTemp,", - " 1.0,", - " 0.0,", - " 0.0,", - " 0.0,", - " 0.0,", - " 0.0,", - " 5.0,", - " 10.0,", - " 24.0,", - " 35.0,", - " ,", - " ,", - " Temperature,", - " Temperature,", - " Dimensionless;", - "Curve:Biquadratic,", - " EIRCurveFuncTemp,", - " 1.0,", - " 0.0,", - " 0.0,", - " 1.0,", - " 0.0,", - " 0.0,", - " 5.0,", - " 10.0,", - " 24.0,", - " 35.0,", - " ,", - " ,", - " Temperature,", - " Temperature,", - " Dimensionless;", - "Curve:Quadratic,", - " EIRCurveFuncPLR,", - " 1.0,", - " 0.0,", - " 0.0,", - " 0.0,", - " 1.0;" - - "Curve:Linear,", - " uniDefrostCurve4,", - " 1,", - " 0,", - " 1,", - " 1;" - "Curve:Linear,", - " uniCRFCurve5,", - " 1,", - " 0,", - " 1,", - " 1;" - "Curve:Linear,", - " uniAuxElecEIRFoPLRCurve6,", - " 1,", - " 0,", - " 1,", - " 1;"}); - - ASSERT_TRUE(process_idf(idf_objects)); - state->init_state(*state); - - state->dataHVACGlobal->TimeStepSys = 0.25; - state->dataHVACGlobal->TimeStepSysSec = state->dataHVACGlobal->TimeStepSys * Constant::rSecsInHour; - - state->init_state(*state); - - // set up the plant loops - // first the load side - state->dataPlnt->TotNumLoops = 1; - state->dataPlnt->PlantLoop.allocate(1); - - state->dataPlnt->PlantLoop(1).FluidName = "WATER"; - state->dataPlnt->PlantLoop(1).glycol = Fluid::GetWater(*state); - state->dataPlnt->PlantLoop(1).LoopDemandCalcScheme = DataPlant::LoopDemandCalcScheme::SingleSetPoint; - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).TotalBranches = 1; - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch.allocate(1); - 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 &PLHPPlantLoadSideComp = state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp(1); - PLHPPlantLoadSideComp.Type = DataPlant::PlantEquipmentType::HeatPumpFuelFiredHeating; - PLHPPlantLoadSideComp.CurOpSchemeType = DataPlant::OpScheme::CompSetPtBased; - - // the init call expects a "from" calling point - PlantLocation myLoadLocation = PlantLocation(1, DataPlant::LoopSideLocation::Supply, 1, 1); - - // call the factory with a valid name to trigger reading inputs - EIRFuelFiredHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpFuelFiredHeating, "FUEL FIRED HP HEATING SIDE"); - - // verify the size of the vector and the processed condition - EXPECT_EQ(1u, state->dataEIRFuelFiredHeatPump->heatPumps.size()); - - // for now we know the order is maintained, so get each heat pump object - EIRFuelFiredHeatPump *thisHeatingPLHP = &state->dataEIRFuelFiredHeatPump->heatPumps[0]; - auto thisEIRPlantLoopHP = &(*(EIRPlantLoopHeatPump *)thisHeatingPLHP); - - // do a bit of extra wiring up to the plant - PLHPPlantLoadSideComp.Name = thisHeatingPLHP->name; - PLHPPlantLoadSideComp.NodeNumIn = thisHeatingPLHP->loadSideNodes.inlet; - - // call for all initialization - state->dataGlobal->BeginEnvrnFlag = true; - state->dataPlnt->PlantFirstSizesOkayToFinalize = true; - thisHeatingPLHP->onInitLoopEquip(*state, myLoadLocation); - - // do a runflag = false to test out execution order - { - bool firstHVAC = true; - Real64 curLoad = -900; - bool runFlag = false; - Real64 constexpr specifiedLoadSetpoint = 45; - Real64 constexpr loadInletTemp = 46; - state->dataLoopNodes->Node(thisHeatingPLHP->loadSideNodes.outlet).TempSetPoint = specifiedLoadSetpoint; - state->dataLoopNodes->Node(thisHeatingPLHP->loadSideNodes.inlet).Temp = loadInletTemp; - state->dataLoopNodes->Node(thisHeatingPLHP->sourceSideNodes.inlet).Temp = 30; - thisHeatingPLHP->simulate(*state, myLoadLocation, firstHVAC, curLoad, runFlag); - } - - // call it from the load side, but this time there is a negative (cooling) load - shouldn't try to run - { - bool firstHVAC = true; - Real64 curLoad = -900; - bool runFlag = true; // plant actually shouldn't do this but the component can be smart enough to handle it - Real64 constexpr specifiedLoadSetpoint = 45; - Real64 constexpr loadInletTemp = 46; - state->dataLoopNodes->Node(thisHeatingPLHP->loadSideNodes.outlet).TempSetPoint = specifiedLoadSetpoint; - state->dataLoopNodes->Node(thisHeatingPLHP->loadSideNodes.inlet).Temp = loadInletTemp; - state->dataLoopNodes->Node(thisHeatingPLHP->sourceSideNodes.inlet).Temp = 30; - thisHeatingPLHP->simulate(*state, myLoadLocation, firstHVAC, curLoad, runFlag); - // expect it to meet setpoint and have some pre-evaluated conditions - EXPECT_NEAR(loadInletTemp, thisHeatingPLHP->loadSideOutletTemp, 0.001); - EXPECT_NEAR(0.0, thisHeatingPLHP->loadSideHeatTransfer, 0.001); - } - - // call it from the load side, but this time there is load (still firsthvac, unit can meet load) - { - bool firstHVAC = true; - Real64 curLoad = 800; - bool runFlag = true; - Real64 constexpr expectedLoadMassFlowRate = 0.09999; - Real64 constexpr expectedCp = 4180; - Real64 constexpr specifiedLoadSetpoint = 45; - Real64 const calculatedLoadInletTemp = specifiedLoadSetpoint - curLoad / (expectedLoadMassFlowRate * expectedCp); - state->dataLoopNodes->Node(thisHeatingPLHP->loadSideNodes.outlet).TempSetPoint = specifiedLoadSetpoint; - state->dataLoopNodes->Node(thisHeatingPLHP->loadSideNodes.inlet).Temp = calculatedLoadInletTemp; - state->dataLoopNodes->Node(thisHeatingPLHP->sourceSideNodes.inlet).Temp = 30; - thisHeatingPLHP->simulate(*state, myLoadLocation, firstHVAC, curLoad, runFlag); - EXPECT_NEAR(16533.333, thisHeatingPLHP->fuelRate, 0.001); - EXPECT_NEAR(14880000.0, thisHeatingPLHP->fuelEnergy, 0.001); - // 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); - EXPECT_NEAR(15520.0, thisEIRPlantLoopHP->powerUsage, 0.001); - } - - // now we can call it again from the load side, but this time there is load (still firsthvac, unit cannot meet load) - { - bool firstHVAC = true; - Real64 curLoad = 1200; - // Real64 availableCapacity = 950.0; - bool runFlag = true; - Real64 constexpr expectedLoadMassFlowRate = 0.09999; - Real64 constexpr expectedCp = 4180; - Real64 constexpr specifiedLoadSetpoint = 45; - Real64 const calculatedLoadInletTemp = specifiedLoadSetpoint - curLoad / (expectedLoadMassFlowRate * expectedCp); - state->dataLoopNodes->Node(thisHeatingPLHP->loadSideNodes.outlet).TempSetPoint = specifiedLoadSetpoint; - state->dataLoopNodes->Node(thisHeatingPLHP->loadSideNodes.inlet).Temp = calculatedLoadInletTemp; - state->dataLoopNodes->Node(thisHeatingPLHP->sourceSideNodes.inlet).Temp = 30; - thisHeatingPLHP->simulate(*state, myLoadLocation, firstHVAC, curLoad, runFlag); - EXPECT_NEAR(24800.0, thisHeatingPLHP->fuelRate, 0.001); - EXPECT_NEAR(22320000.0, thisHeatingPLHP->fuelEnergy, 0.001); - EXPECT_NEAR(15520.0, thisEIRPlantLoopHP->powerUsage, 0.001); - // expect it to miss setpoint and be at max capacity - // EXPECT_NEAR(44.402, thisHeatingPLHP->loadSideOutletTemp, 0.001); - // EXPECT_NEAR(availableCapacity, thisHeatingPLHP->loadSideHeatTransfer, 0.001); - } - - // now we can call it again from the load side, but this time there is no load (still firsthvac) - { - bool firstHVAC = true; - Real64 curLoad = 0.0; - bool runFlag = true; - Real64 constexpr expectedLoadMassFlowRate = 0.09999; - Real64 constexpr expectedCp = 4180; - Real64 constexpr specifiedLoadSetpoint = 45; - Real64 const calculatedLoadInletTemp = specifiedLoadSetpoint - curLoad / (expectedLoadMassFlowRate * expectedCp); - state->dataLoopNodes->Node(thisHeatingPLHP->loadSideNodes.outlet).TempSetPoint = specifiedLoadSetpoint; - state->dataLoopNodes->Node(thisHeatingPLHP->loadSideNodes.inlet).Temp = calculatedLoadInletTemp; - state->dataLoopNodes->Node(thisHeatingPLHP->sourceSideNodes.inlet).Temp = 30; - thisHeatingPLHP->simulate(*state, myLoadLocation, firstHVAC, curLoad, runFlag); - // expect it to miss setpoint and be at max capacity - EXPECT_NEAR(45.0, thisHeatingPLHP->loadSideOutletTemp, 0.001); - EXPECT_NEAR(30.0, thisHeatingPLHP->sourceSideOutletTemp, 0.001); - EXPECT_NEAR(0.0, thisEIRPlantLoopHP->powerUsage, 0.001); - } -} - -TEST_F(EnergyPlusFixture, Test_HeatRecoveryGetInputs_AirSource) -{ - std::string const idf_objects = delimited_string({"HeatPump:PlantLoop:EIR:Cooling,", - " hp cooling side,", - " node 1,", - " node 2,", - " AirSource,", - " node 3,", - " node 4,", - " node 5,", - " node 6,", - " hp heating side,", - " 0.005,", - " Autosize,", - " Autosize,", - " Autosize,", - " 1.0,", - " 1,", - " dummyCurve,", - " dummyCurve,", - " dummyCurve;", - "HeatPump:PlantLoop:EIR:Heating,", - " hp heating side,", - " node 7,", - " node 8,", - " AirSource,", - " node 9,", - " node 10,", - " node 11,", - " node 12,", - " hp cooling side,", - " 0.005,", - " Autosize,", - " Autosize,", - " Autosize,", - " 2.0,", - " 1,", - " dummyCurve,", - " dummyCurve,", - " dummyCurve;", - "Curve:Linear,", - " dummyCurve,", - " 1,", - " 0,", - " 1,", - " 1;"}); - ASSERT_TRUE(process_idf(idf_objects)); - state->init_state(*state); - - // call the factory with a valid name to trigger reading inputs - EIRPlantLoopHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpEIRCooling, "HP COOLING SIDE"); - - // verify the size of the vector and the processed condition - EXPECT_EQ(2u, state->dataEIRPlantLoopHeatPump->heatPumps.size()); - - // for now we know the order is maintained, so get each heat pump object - EIRPlantLoopHeatPump *thisCoolingPLHP = &state->dataEIRPlantLoopHeatPump->heatPumps[0]; - EIRPlantLoopHeatPump *thisHeatingPLHP = &state->dataEIRPlantLoopHeatPump->heatPumps[1]; - - // check heat recovery input fields - EXPECT_TRUE(thisCoolingPLHP->heatRecoveryAvailable); - EXPECT_TRUE(thisHeatingPLHP->heatRecoveryAvailable); - EXPECT_TRUE(thisCoolingPLHP->heatRecoveryDesignVolFlowRateWasAutoSized); - EXPECT_TRUE(thisHeatingPLHP->heatRecoveryDesignVolFlowRateWasAutoSized); - EXPECT_EQ(thisCoolingPLHP->maxHeatRecoveryTempLimit, 60.0); - EXPECT_EQ(thisHeatingPLHP->minHeatRecoveryTempLimit, 4.5); -} - -TEST_F(EnergyPlusFixture, Test_HeatRecoveryFlowSizing_AirSource) -{ - std::string const idf_objects = delimited_string({"HeatPump:PlantLoop:EIR:Cooling,", - " hp cooling side,", - " node 1,", - " node 2,", - " AirSource,", - " node 3,", - " node 4,", - " node 5,", - " node 6,", - " hp heating side,", - " 0.005,", - " Autosize,", - " Autosize,", - " Autosize,", - " 1.0,", - " 1,", - " dummyCurve,", - " dummyCurve,", - " dummyCurve;", - "HeatPump:PlantLoop:EIR:Heating,", - " hp heating side,", - " node 7,", - " node 8,", - " AirSource,", - " node 9,", - " node 10,", - " node 11,", - " node 12,", - " hp cooling side,", - " 0.005,", - " Autosize,", - " Autosize,", - " Autosize,", - " 2.0,", - " 1,", - " dummyCurve,", - " dummyCurve,", - " dummyCurve;", - "Curve:Linear,", - " dummyCurve,", - " 1,", - " 0,", - " 1,", - " 1;"}); - ASSERT_TRUE(process_idf(idf_objects)); - state->init_state(*state); - - // call the factory with a valid name to trigger reading inputs - EIRPlantLoopHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpEIRCooling, "HP COOLING SIDE"); - - // verify the size of the vector and the processed condition - EXPECT_EQ(2u, state->dataEIRPlantLoopHeatPump->heatPumps.size()); - - // for now we know the order is maintained, so get each heat pump object - EIRPlantLoopHeatPump *thisCoolingPLHP = &state->dataEIRPlantLoopHeatPump->heatPumps[0]; - EIRPlantLoopHeatPump *thisHeatingPLHP = &state->dataEIRPlantLoopHeatPump->heatPumps[1]; - - // check heat recovery input fields - EXPECT_TRUE(thisCoolingPLHP->heatRecoveryAvailable); - EXPECT_TRUE(thisHeatingPLHP->heatRecoveryAvailable); - EXPECT_TRUE(thisCoolingPLHP->heatRecoveryDesignVolFlowRateWasAutoSized); - EXPECT_TRUE(thisHeatingPLHP->heatRecoveryDesignVolFlowRateWasAutoSized); - EXPECT_EQ(thisHeatingPLHP->minHeatRecoveryTempLimit, 4.5); - EXPECT_EQ(thisCoolingPLHP->maxHeatRecoveryTempLimit, 60.0); - - // We'll set up two plant loops: load heating loop and load side cooling loop - state->dataPlnt->TotNumLoops = 2; - state->dataPlnt->PlantLoop.allocate(state->dataPlnt->TotNumLoops); - state->dataSize->PlantSizData.allocate(2); - // chilled water plant loop - state->dataPlnt->PlantLoop(1).FluidName = "WATER"; - state->dataPlnt->PlantLoop(1).glycol = Fluid::GetWater(*state); - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Demand).TotalBranches = 1; - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Demand).Branch.allocate(1); - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).TotalComponents = 1; - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp.allocate(1); - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).TotalBranches = 1; - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch.allocate(1); - 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 &loop1supplyComponent1 = state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp(1); - auto &loop1demandComponent1 = state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp(1); - loop1supplyComponent1.Type = DataPlant::PlantEquipmentType::HeatPumpEIRCooling; - loop1supplyComponent1.Name = thisCoolingPLHP->name; - loop1supplyComponent1.NodeNumIn = thisCoolingPLHP->loadSideNodes.inlet; - // heat recovery component on the demand side of loop2 - loop1demandComponent1.Type = DataPlant::PlantEquipmentType::HeatPumpEIRHeating; - loop1demandComponent1.Name = thisHeatingPLHP->name; - loop1demandComponent1.NodeNumIn = thisHeatingPLHP->heatRecoveryNodes.inlet; - // assign the CW plant sizing data - state->dataPlnt->PlantLoop(1).PlantSizNum = 1; - state->dataSize->PlantSizData(1).DeltaT = 6.67; - - // hot water plant loop - state->dataPlnt->PlantLoop(2).FluidName = "WATER"; - state->dataPlnt->PlantLoop(2).glycol = Fluid::GetWater(*state); - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).TotalBranches = 1; - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch.allocate(1); - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).TotalComponents = 1; - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp.allocate(1); - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Supply).TotalBranches = 1; - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Supply).Branch.allocate(1); - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).TotalComponents = 1; - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp.allocate(1); - auto &loop2supplyComponent1 = state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp(1); - auto &loop2demandComponent1 = state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp(1); - loop2supplyComponent1.Type = DataPlant::PlantEquipmentType::HeatPumpEIRHeating; - loop2supplyComponent1.Name = thisHeatingPLHP->name; - loop2supplyComponent1.NodeNumIn = thisHeatingPLHP->loadSideNodes.inlet; - // heat recovery component on the demand side of loop1 - loop2demandComponent1.Type = DataPlant::PlantEquipmentType::HeatPumpEIRCooling; - loop2demandComponent1.Name = thisCoolingPLHP->name; - loop2demandComponent1.NodeNumIn = thisCoolingPLHP->heatRecoveryNodes.inlet; - // assign the HW plant sizing data - state->dataPlnt->PlantLoop(2).PlantSizNum = 2; - state->dataSize->PlantSizData(2).DeltaT = 11.11; - - // the init call expects a "from" calling point - PlantLocation myCoolingLoadLocation = PlantLocation(1, DataPlant::LoopSideLocation::Supply, 1, 1); - PlantLocation myHWHeatRecoveryLocation = PlantLocation(2, DataPlant::LoopSideLocation::Demand, 1, 1); - PlantLocation myHeatingLoadLocation = PlantLocation(2, DataPlant::LoopSideLocation::Supply, 1, 1); - PlantLocation myCWHeatRecoveryLocation = PlantLocation(1, DataPlant::LoopSideLocation::Demand, 1, 1); - // set a couple global flags - state->dataGlobal->BeginEnvrnFlag = true; - state->dataPlnt->PlantFinalSizesOkayToReport = true; - state->dataPlnt->PlantFirstSizesOkayToReport = true; - state->dataPlnt->PlantFirstSizesOkayToFinalize = true; - // initialize so the components can find themselves on the plant - thisCoolingPLHP->onInitLoopEquip(*state, myCoolingLoadLocation); - thisHeatingPLHP->onInitLoopEquip(*state, myHeatingLoadLocation); - - // size the HW heat-recovery flow rate - // set properties at design HW temp (60.0C) - Real64 rhoHR = 983.20; - Real64 CpHR = 4185.00; - Real64 designHWHeatRecoveryHeatTransfer = thisCoolingPLHP->referenceCapacity * (1 + 1 / thisCoolingPLHP->referenceCOP); - Real64 expectedHWHeatRecoveryFlow = designHWHeatRecoveryHeatTransfer / (state->dataSize->PlantSizData(2).DeltaT * CpHR * rhoHR); - // size the CW heat-recovery flow rate - // reset properties at design CW temp (5.5C) - rhoHR = 999.90; - CpHR = 4197.93; - Real64 designCWHeatRecoveryHeatTransfer = thisHeatingPLHP->referenceCapacity * (1 - 1 / thisHeatingPLHP->referenceCOP); - Real64 expectedCWHeatRecoveryFlow = designCWHeatRecoveryHeatTransfer / (state->dataSize->PlantSizData(1).DeltaT * CpHR * rhoHR); - // check autosized heat recovery flow rates - EXPECT_NEAR(expectedHWHeatRecoveryFlow, thisCoolingPLHP->heatRecoveryDesignVolFlowRate, 0.00001); // 0.00612 - EXPECT_NEAR(expectedCWHeatRecoveryFlow, thisHeatingPLHP->heatRecoveryDesignVolFlowRate, 0.00001); // 0.00250 -} - -TEST_F(EnergyPlusFixture, CoolingwithHeatRecoverySimulate_AirSource) -{ - std::string const idf_objects = delimited_string({"HeatPump:PlantLoop:EIR:Cooling,", - " hp cooling side,", - " node 1,", - " node 2,", - " AirSource,", - " node 3,", - " node 4,", - " node 5,", - " node 6,", - " hp heating side,", - " 0.005,", - " Autosize,", - " Autosize,", - " Autosize,", - " 3.0,", - " 1,", - " dummyCurve,", - " dummyCurve,", - " dummyCurve;", - "HeatPump:PlantLoop:EIR:Heating,", - " hp heating side,", - " node 7,", - " node 8,", - " AirSource,", - " node 9,", - " node 10,", - " node 11,", - " node 12,", - " hp cooling side,", - " 0.005,", - " Autosize,", - " Autosize,", - " Autosize,", - " 3.0,", - " 1,", - " dummyCurve,", - " dummyCurve,", - " dummyCurve;", - "Curve:Linear,", - " dummyCurve,", - " 1,", - " 0,", - " 1,", - " 1;"}); - ASSERT_TRUE(process_idf(idf_objects)); - state->init_state(*state); - - // call the factory with a valid name to trigger reading inputs - EIRPlantLoopHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpEIRCooling, "HP COOLING SIDE"); - - // verify the size of the vector and the processed condition - EXPECT_EQ(2u, state->dataEIRPlantLoopHeatPump->heatPumps.size()); - - // for now we know the order is maintained, so get each heat pump object - EIRPlantLoopHeatPump *thisCoolingPLHP = &state->dataEIRPlantLoopHeatPump->heatPumps[0]; - EIRPlantLoopHeatPump *thisHeatingPLHP = &state->dataEIRPlantLoopHeatPump->heatPumps[1]; - - // check heat recovery input fields - EXPECT_TRUE(thisCoolingPLHP->heatRecoveryAvailable); - EXPECT_TRUE(thisHeatingPLHP->heatRecoveryAvailable); - EXPECT_TRUE(thisCoolingPLHP->heatRecoveryDesignVolFlowRateWasAutoSized); - EXPECT_TRUE(thisHeatingPLHP->heatRecoveryDesignVolFlowRateWasAutoSized); - EXPECT_EQ(thisHeatingPLHP->minHeatRecoveryTempLimit, 4.5); - EXPECT_EQ(thisCoolingPLHP->maxHeatRecoveryTempLimit, 60.0); - - // We'll set up two plant loops: load heating loop and load side cooling loop - state->dataPlnt->TotNumLoops = 2; - state->dataPlnt->PlantLoop.allocate(state->dataPlnt->TotNumLoops); - state->dataSize->PlantSizData.allocate(2); - // chilled water plant loop - state->dataPlnt->PlantLoop(1).FluidName = "WATER"; - state->dataPlnt->PlantLoop(1).glycol = Fluid::GetWater(*state); - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Demand).TotalBranches = 1; - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Demand).Branch.allocate(1); - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).TotalComponents = 1; - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp.allocate(1); - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).TotalBranches = 1; - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch.allocate(1); - 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 &loop1supplyComponent1 = state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp(1); - auto &loop1demandComponent1 = state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp(1); - loop1supplyComponent1.Type = DataPlant::PlantEquipmentType::HeatPumpEIRCooling; - loop1supplyComponent1.Name = thisCoolingPLHP->name; - loop1supplyComponent1.NodeNumIn = thisCoolingPLHP->loadSideNodes.inlet; - // heat recovery component on the demand side of loop2 - loop1demandComponent1.Type = DataPlant::PlantEquipmentType::HeatPumpEIRHeating; - loop1demandComponent1.Name = thisHeatingPLHP->name; - loop1demandComponent1.NodeNumIn = thisHeatingPLHP->heatRecoveryNodes.inlet; - // assign the CW plant sizing data - state->dataPlnt->PlantLoop(1).PlantSizNum = 1; - state->dataSize->PlantSizData(1).DeltaT = 6.67; - - state->dataPlnt->PlantLoop(1).LoopDemandCalcScheme = DataPlant::LoopDemandCalcScheme::SingleSetPoint; - auto &PLHPPlantLoadSideComp = state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp(1); - PLHPPlantLoadSideComp.Type = DataPlant::PlantEquipmentType::HeatPumpEIRCooling; - PLHPPlantLoadSideComp.CurOpSchemeType = DataPlant::OpScheme::CompSetPtBased; - - // hot water plant loop - state->dataPlnt->PlantLoop(2).FluidName = "WATER"; - state->dataPlnt->PlantLoop(2).glycol = Fluid::GetWater(*state); - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).TotalBranches = 1; - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch.allocate(1); - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).TotalComponents = 1; - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp.allocate(1); - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Supply).TotalBranches = 1; - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Supply).Branch.allocate(1); - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).TotalComponents = 1; - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp.allocate(1); - auto &loop2supplyComponent1 = state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp(1); - auto &loop2demandComponent1 = state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp(1); - loop2supplyComponent1.Type = DataPlant::PlantEquipmentType::HeatPumpEIRHeating; - loop2supplyComponent1.Name = thisHeatingPLHP->name; - loop2supplyComponent1.NodeNumIn = thisHeatingPLHP->loadSideNodes.inlet; - // heat recovery component on the demand side of loop1 - loop2demandComponent1.Type = DataPlant::PlantEquipmentType::HeatPumpEIRCooling; - loop2demandComponent1.Name = thisCoolingPLHP->name; - loop2demandComponent1.NodeNumIn = thisCoolingPLHP->heatRecoveryNodes.inlet; - // assign the HW plant sizing data - state->dataPlnt->PlantLoop(2).PlantSizNum = 2; - state->dataSize->PlantSizData(2).DeltaT = 11.11; - - // the init call expects a "from" calling point - PlantLocation myCoolingLoadLocation = PlantLocation(1, DataPlant::LoopSideLocation::Supply, 1, 1); - PlantLocation myHWHeatRecoveryLocation = PlantLocation(2, DataPlant::LoopSideLocation::Demand, 1, 1); - PlantLocation myHeatingLoadLocation = PlantLocation(2, DataPlant::LoopSideLocation::Supply, 1, 1); - PlantLocation myCWHeatRecoveryLocation = PlantLocation(1, DataPlant::LoopSideLocation::Demand, 1, 1); - // set a couple global flags - state->dataGlobal->BeginEnvrnFlag = true; - state->dataPlnt->PlantFinalSizesOkayToReport = true; - state->dataPlnt->PlantFirstSizesOkayToReport = true; - state->dataPlnt->PlantFirstSizesOkayToFinalize = true; - // initialize so the components can find themselves on the plant - thisCoolingPLHP->onInitLoopEquip(*state, myCoolingLoadLocation); - thisHeatingPLHP->onInitLoopEquip(*state, myHeatingLoadLocation); - - thisCoolingPLHP->setPointNodeNum = thisCoolingPLHP->loadSideNodes.outlet; - thisHeatingPLHP->setPointNodeNum = thisHeatingPLHP->loadSideNodes.outlet; - - // call from load side location, firsthvac, no load, not running, verify the unit doesn't have any values lingering - thisCoolingPLHP->loadSideHeatTransfer = 2000; - thisCoolingPLHP->loadSideInletTemp = 23.0; - thisCoolingPLHP->loadSideOutletTemp = 43.0; - thisCoolingPLHP->powerUsage = 100.0; - thisCoolingPLHP->sourceSideHeatTransfer = 60.0; - thisCoolingPLHP->sourceSideInletTemp = 33.0; - thisCoolingPLHP->sourceSideOutletTemp = 43.0; - thisCoolingPLHP->heatRecoveryInletTemp = 45.0; - thisCoolingPLHP->heatRecoveryOutletTemp = 55.0; - thisCoolingPLHP->sysControlType = ControlType::Setpoint; - bool firstHVAC = true; - Real64 curLoad = 0.0; - bool runFlag = false; - thisCoolingPLHP->simulate(*state, myCoolingLoadLocation, firstHVAC, curLoad, runFlag); - EXPECT_NEAR(0.0, thisCoolingPLHP->loadSideHeatTransfer, 0.001); - EXPECT_NEAR(0.0, thisCoolingPLHP->sourceSideHeatTransfer, 0.001); - EXPECT_NEAR(0.0, thisCoolingPLHP->powerUsage, 0.001); - EXPECT_NEAR(0.0, thisCoolingPLHP->heatRecoveryRate, 0.001); - EXPECT_NEAR(thisCoolingPLHP->loadSideInletTemp, thisCoolingPLHP->loadSideOutletTemp, 0.001); - EXPECT_NEAR(thisCoolingPLHP->sourceSideInletTemp, thisCoolingPLHP->sourceSideOutletTemp, 0.001); - EXPECT_NEAR(thisCoolingPLHP->heatRecoveryInletTemp, thisCoolingPLHP->heatRecoveryOutletTemp, 0.001); - - // now we can call it again from the load side, but this time there is load - { - firstHVAC = true; - curLoad = -69993.3; // current cooling load - runFlag = true; - Real64 expectedLoadMassFlowRate = thisCoolingPLHP->loadSideDesignMassFlowRate; - Real64 constexpr expectedCp = 4182.3220354805; - Real64 constexpr specifiedLoadSetpoint = 15.0; - Real64 const calculatedLoadInletTemp = specifiedLoadSetpoint - curLoad / (expectedLoadMassFlowRate * expectedCp); - state->dataLoopNodes->Node(thisCoolingPLHP->loadSideNodes.outlet).TempSetPoint = specifiedLoadSetpoint; - state->dataLoopNodes->Node(thisCoolingPLHP->loadSideNodes.inlet).Temp = calculatedLoadInletTemp; - state->dataLoopNodes->Node(thisCoolingPLHP->sourceSideNodes.inlet).Temp = 30; - state->dataLoopNodes->Node(thisCoolingPLHP->heatRecoveryNodes.inlet).Temp = 45.0; - thisCoolingPLHP->simulate(*state, myCoolingLoadLocation, firstHVAC, curLoad, runFlag); - // expect it to meet the setpoint while operating at part load - EXPECT_NEAR(15.0, thisCoolingPLHP->loadSideOutletTemp, 0.001); - EXPECT_NEAR(0.5, thisCoolingPLHP->partLoadRatio, 0.001); - EXPECT_NEAR(69993.3, thisCoolingPLHP->loadSideHeatTransfer, 0.001); - EXPECT_NEAR(23331.1, thisCoolingPLHP->powerUsage, 0.001); - EXPECT_NEAR(93324.4, thisCoolingPLHP->heatRecoveryRate, 0.001); - EXPECT_NEAR(0.0, thisCoolingPLHP->sourceSideHeatTransfer, 0.001); - EXPECT_NEAR(45.0, thisCoolingPLHP->heatRecoveryInletTemp, 0.001); - EXPECT_NEAR(50.469, thisCoolingPLHP->heatRecoveryOutletTemp, 0.001); - } - - // now we can call it again from the load side, but this time there is load - // higher heat recovery temperature - { - firstHVAC = true; - curLoad = -69993.3; // current cooling load - runFlag = true; - Real64 expectedLoadMassFlowRate = thisCoolingPLHP->loadSideDesignMassFlowRate; - Real64 constexpr expectedCp = 4182.3220354805; - Real64 constexpr specifiedLoadSetpoint = 15.0; - Real64 const calculatedLoadInletTemp = specifiedLoadSetpoint - curLoad / (expectedLoadMassFlowRate * expectedCp); - state->dataLoopNodes->Node(thisCoolingPLHP->loadSideNodes.outlet).TempSetPoint = specifiedLoadSetpoint; - state->dataLoopNodes->Node(thisCoolingPLHP->loadSideNodes.inlet).Temp = calculatedLoadInletTemp; - state->dataLoopNodes->Node(thisCoolingPLHP->sourceSideNodes.inlet).Temp = 30; - state->dataLoopNodes->Node(thisCoolingPLHP->heatRecoveryNodes.inlet).Temp = 58.0; - thisCoolingPLHP->simulate(*state, myCoolingLoadLocation, firstHVAC, curLoad, runFlag); - // expect it to meet the setpoint while operating at part load - EXPECT_NEAR(15.0, thisCoolingPLHP->loadSideOutletTemp, 0.001); - EXPECT_NEAR(0.5, thisCoolingPLHP->partLoadRatio, 0.001); - EXPECT_NEAR(69993.3, thisCoolingPLHP->loadSideHeatTransfer, 0.001); - EXPECT_NEAR(23331.1, thisCoolingPLHP->powerUsage, 0.001); - // energy balance or energy conservation at the condenser - Real64 energyBalanceCondenser = thisCoolingPLHP->loadSideHeatTransfer + thisCoolingPLHP->powerUsage; - EXPECT_NEAR(93324.4, energyBalanceCondenser, 0.001); - // heat rejected is split b/n heat recovery and source side heat transfer due to tem limit - EXPECT_NEAR(34164.275, thisCoolingPLHP->heatRecoveryRate, 0.001); - EXPECT_NEAR(59160.125, thisCoolingPLHP->sourceSideHeatTransfer, 0.001); - Real64 totalHeatRejected = thisCoolingPLHP->heatRecoveryRate + thisCoolingPLHP->sourceSideHeatTransfer; - EXPECT_NEAR(93324.4, totalHeatRejected, 0.001); - // total heat rejected == energy balance at the condenser - EXPECT_NEAR(energyBalanceCondenser, totalHeatRejected, 0.001); - EXPECT_NEAR(58.0, thisCoolingPLHP->heatRecoveryInletTemp, 0.001); - // heat recovery outlet temperature is capped @ 60C. - EXPECT_NEAR(60.0, thisCoolingPLHP->heatRecoveryOutletTemp, 0.001); - } -} - -TEST_F(EnergyPlusFixture, HeatingwithHeatRecoverySimulate_AirSource) -{ - std::string const idf_objects = delimited_string({"HeatPump:PlantLoop:EIR:Cooling,", - " hp cooling side,", - " node 1,", - " node 2,", - " AirSource,", - " node 3,", - " node 4,", - " node 5,", - " node 6,", - " hp heating side,", - " 0.005,", - " Autosize,", - " Autosize,", - " Autosize,", - " 3.0,", - " 1,", - " dummyCurve,", - " dummyCurve,", - " dummyCurve;", - "HeatPump:PlantLoop:EIR:Heating,", - " hp heating side,", - " node 7,", - " node 8,", - " AirSource,", - " node 9,", - " node 10,", - " node 11,", - " node 12,", - " hp cooling side,", - " 0.005,", - " Autosize,", - " Autosize,", - " Autosize,", - " 3.0,", - " 1,", - " dummyCurve,", - " dummyCurve,", - " dummyCurve;", - "Curve:Linear,", - " dummyCurve,", - " 1,", - " 0,", - " 1,", - " 1;"}); - ASSERT_TRUE(process_idf(idf_objects)); - state->init_state(*state); - - // call the factory with a valid name to trigger reading inputs - EIRPlantLoopHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpEIRCooling, "HP COOLING SIDE"); - - // verify the size of the vector and the processed condition - EXPECT_EQ(2u, state->dataEIRPlantLoopHeatPump->heatPumps.size()); - - // for now we know the order is maintained, so get each heat pump object - EIRPlantLoopHeatPump *thisCoolingPLHP = &state->dataEIRPlantLoopHeatPump->heatPumps[0]; - EIRPlantLoopHeatPump *thisHeatingPLHP = &state->dataEIRPlantLoopHeatPump->heatPumps[1]; - - // check heat recovery input fields - EXPECT_TRUE(thisCoolingPLHP->heatRecoveryAvailable); - EXPECT_TRUE(thisHeatingPLHP->heatRecoveryAvailable); - EXPECT_TRUE(thisCoolingPLHP->heatRecoveryDesignVolFlowRateWasAutoSized); - EXPECT_TRUE(thisHeatingPLHP->heatRecoveryDesignVolFlowRateWasAutoSized); - EXPECT_EQ(thisHeatingPLHP->minHeatRecoveryTempLimit, 4.5); - EXPECT_EQ(thisCoolingPLHP->maxHeatRecoveryTempLimit, 60.0); - - // We'll set up two plant loops: load heating loop and load side cooling loop - state->dataPlnt->TotNumLoops = 2; - state->dataPlnt->PlantLoop.allocate(state->dataPlnt->TotNumLoops); - state->dataSize->PlantSizData.allocate(2); - // chilled water plant loop - state->dataPlnt->PlantLoop(1).FluidName = "WATER"; - state->dataPlnt->PlantLoop(1).glycol = Fluid::GetWater(*state); - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Demand).TotalBranches = 1; - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Demand).Branch.allocate(1); - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).TotalComponents = 1; - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp.allocate(1); - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).TotalBranches = 1; - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch.allocate(1); - 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 &loop1supplyComponent1 = state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp(1); - auto &loop1demandComponent1 = state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp(1); - loop1supplyComponent1.Type = DataPlant::PlantEquipmentType::HeatPumpEIRCooling; - loop1supplyComponent1.Name = thisCoolingPLHP->name; - loop1supplyComponent1.NodeNumIn = thisCoolingPLHP->loadSideNodes.inlet; - // heat recovery component on the demand side of loop1 - loop1demandComponent1.Type = DataPlant::PlantEquipmentType::HeatPumpEIRHeating; - loop1demandComponent1.Name = thisHeatingPLHP->name; - loop1demandComponent1.NodeNumIn = thisHeatingPLHP->heatRecoveryNodes.inlet; - // assign the CW plant sizing data - state->dataPlnt->PlantLoop(1).PlantSizNum = 1; - state->dataSize->PlantSizData(1).DeltaT = 6.67; - - state->dataPlnt->PlantLoop(1).LoopDemandCalcScheme = DataPlant::LoopDemandCalcScheme::SingleSetPoint; - auto &PLHPPlantLoadSideComp = state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp(1); - PLHPPlantLoadSideComp.Type = DataPlant::PlantEquipmentType::HeatPumpEIRCooling; - PLHPPlantLoadSideComp.CurOpSchemeType = DataPlant::OpScheme::CompSetPtBased; - - // hot water plant loop - state->dataPlnt->PlantLoop(2).FluidName = "WATER"; - state->dataPlnt->PlantLoop(2).glycol = Fluid::GetWater(*state); - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).TotalBranches = 1; - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch.allocate(1); - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).TotalComponents = 1; - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp.allocate(1); - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Supply).TotalBranches = 1; - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Supply).Branch.allocate(1); - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).TotalComponents = 1; - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp.allocate(1); - auto &loop2supplyComponent1 = state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp(1); - auto &loop2demandComponent1 = state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp(1); - loop2supplyComponent1.Type = DataPlant::PlantEquipmentType::HeatPumpEIRHeating; - loop2supplyComponent1.Name = thisHeatingPLHP->name; - loop2supplyComponent1.NodeNumIn = thisHeatingPLHP->loadSideNodes.inlet; - // heat recovery component on the demand side of loop2 - loop2demandComponent1.Type = DataPlant::PlantEquipmentType::HeatPumpEIRCooling; - loop2demandComponent1.Name = thisCoolingPLHP->name; - loop2demandComponent1.NodeNumIn = thisCoolingPLHP->heatRecoveryNodes.inlet; - // assign the HW plant sizing data - state->dataPlnt->PlantLoop(2).PlantSizNum = 2; - state->dataSize->PlantSizData(2).DeltaT = 11.11; - - // the init call expects a "from" calling point - PlantLocation myCoolingLoadLocation = PlantLocation(1, DataPlant::LoopSideLocation::Supply, 1, 1); - PlantLocation myHWHeatRecoveryLocation = PlantLocation(2, DataPlant::LoopSideLocation::Demand, 1, 1); - PlantLocation myHeatingLoadLocation = PlantLocation(2, DataPlant::LoopSideLocation::Supply, 1, 1); - PlantLocation myCWHeatRecoveryLocation = PlantLocation(1, DataPlant::LoopSideLocation::Demand, 1, 1); - - state->dataPlnt->PlantLoop(2).LoopDemandCalcScheme = DataPlant::LoopDemandCalcScheme::SingleSetPoint; - auto &PLHPPlantLoadSideComp2 = state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp(1); - PLHPPlantLoadSideComp2.Type = DataPlant::PlantEquipmentType::HeatPumpEIRHeating; - PLHPPlantLoadSideComp2.CurOpSchemeType = DataPlant::OpScheme::CompSetPtBased; - - // set a couple global flags - state->dataGlobal->BeginEnvrnFlag = true; - state->dataPlnt->PlantFinalSizesOkayToReport = true; - state->dataPlnt->PlantFirstSizesOkayToReport = true; - state->dataPlnt->PlantFirstSizesOkayToFinalize = true; - // initialize so the components can find themselves on the plant - thisCoolingPLHP->onInitLoopEquip(*state, myCoolingLoadLocation); - thisHeatingPLHP->onInitLoopEquip(*state, myHeatingLoadLocation); - - thisCoolingPLHP->setPointNodeNum = thisCoolingPLHP->loadSideNodes.outlet; - thisHeatingPLHP->setPointNodeNum = thisHeatingPLHP->loadSideNodes.outlet; - - // call from load side location, firsthvac, no load, not running, verify the unit doesn't have any values lingering - thisHeatingPLHP->loadSideHeatTransfer = 5000; - thisHeatingPLHP->loadSideInletTemp = 43.0; - thisHeatingPLHP->loadSideOutletTemp = 53.0; - thisHeatingPLHP->powerUsage = 200.0; - thisHeatingPLHP->sourceSideHeatTransfer = 5200.0; - thisHeatingPLHP->sourceSideInletTemp = 13.0; - thisHeatingPLHP->sourceSideOutletTemp = 8.0; - thisHeatingPLHP->heatRecoveryInletTemp = 15.0; - thisHeatingPLHP->heatRecoveryOutletTemp = 10.0; - thisHeatingPLHP->sysControlType = ControlType::Setpoint; - bool firstHVAC = true; - Real64 curLoad = 0.0; - bool runFlag = false; - thisHeatingPLHP->simulate(*state, myHeatingLoadLocation, firstHVAC, curLoad, runFlag); - EXPECT_NEAR(0.0, thisHeatingPLHP->loadSideHeatTransfer, 0.001); - EXPECT_NEAR(0.0, thisHeatingPLHP->sourceSideHeatTransfer, 0.001); - EXPECT_NEAR(0.0, thisHeatingPLHP->powerUsage, 0.001); - EXPECT_NEAR(0.0, thisHeatingPLHP->heatRecoveryRate, 0.001); - EXPECT_NEAR(thisHeatingPLHP->loadSideInletTemp, thisHeatingPLHP->loadSideOutletTemp, 0.001); - EXPECT_NEAR(thisHeatingPLHP->sourceSideInletTemp, thisHeatingPLHP->sourceSideOutletTemp, 0.001); - EXPECT_NEAR(thisHeatingPLHP->heatRecoveryInletTemp, thisHeatingPLHP->heatRecoveryOutletTemp, 0.001); - - // now we can call it again from the load side, but this time there is heating load - { - firstHVAC = true; - curLoad = 69993.3; // current heating load - runFlag = true; - Real64 expectedLoadMassFlowRate = thisHeatingPLHP->loadSideDesignMassFlowRate; - Real64 constexpr expectedCp = 4182.3220354805; - Real64 constexpr specifiedLoadSetpoint = 55.0; - Real64 const calculatedLoadInletTemp = specifiedLoadSetpoint - curLoad / (expectedLoadMassFlowRate * expectedCp); - state->dataLoopNodes->Node(thisHeatingPLHP->loadSideNodes.outlet).TempSetPoint = specifiedLoadSetpoint; - state->dataLoopNodes->Node(thisHeatingPLHP->loadSideNodes.inlet).Temp = calculatedLoadInletTemp; - state->dataLoopNodes->Node(thisHeatingPLHP->sourceSideNodes.inlet).Temp = 13; - state->dataLoopNodes->Node(thisHeatingPLHP->heatRecoveryNodes.inlet).Temp = 15.0; - thisHeatingPLHP->simulate(*state, myHeatingLoadLocation, firstHVAC, curLoad, runFlag); - // expect it to meet the setpoint while operating at part load - EXPECT_NEAR(55.0, thisHeatingPLHP->loadSideOutletTemp, 0.001); - EXPECT_NEAR(0.5, thisHeatingPLHP->partLoadRatio, 0.001); - EXPECT_NEAR(69982.238, thisHeatingPLHP->loadSideHeatTransfer, 0.001); - EXPECT_NEAR(23327.413, thisHeatingPLHP->powerUsage, 0.001); - EXPECT_NEAR(46654.825, thisHeatingPLHP->heatRecoveryRate, 0.001); - EXPECT_NEAR(0.0, thisHeatingPLHP->sourceSideHeatTransfer, 0.001); - EXPECT_NEAR(15.0, thisHeatingPLHP->heatRecoveryInletTemp, 0.001); - EXPECT_NEAR(11.655, thisHeatingPLHP->heatRecoveryOutletTemp, 0.001); - } - - // now we can call it again from the load side, but this time there is heating load - { - firstHVAC = true; - curLoad = 69993.3; // current heating load - runFlag = true; - Real64 expectedLoadMassFlowRate = thisHeatingPLHP->loadSideDesignMassFlowRate; - Real64 constexpr expectedCp = 4182.3220354805; - Real64 constexpr specifiedLoadSetpoint = 55.0; - Real64 const calculatedLoadInletTemp = specifiedLoadSetpoint - curLoad / (expectedLoadMassFlowRate * expectedCp); - state->dataLoopNodes->Node(thisHeatingPLHP->loadSideNodes.outlet).TempSetPoint = specifiedLoadSetpoint; - state->dataLoopNodes->Node(thisHeatingPLHP->loadSideNodes.inlet).Temp = calculatedLoadInletTemp; - state->dataLoopNodes->Node(thisHeatingPLHP->sourceSideNodes.inlet).Temp = 13; - state->dataLoopNodes->Node(thisHeatingPLHP->heatRecoveryNodes.inlet).Temp = 7.0; - thisHeatingPLHP->simulate(*state, myHeatingLoadLocation, firstHVAC, curLoad, runFlag); - // expect it to meet the setpoint while operating at part load - EXPECT_NEAR(55.0, thisHeatingPLHP->loadSideOutletTemp, 0.001); - EXPECT_NEAR(0.5, thisHeatingPLHP->partLoadRatio, 0.001); - EXPECT_NEAR(69982.238, thisHeatingPLHP->loadSideHeatTransfer, 0.001); - EXPECT_NEAR(23327.413, thisHeatingPLHP->powerUsage, 0.001); - EXPECT_NEAR(34956.434, thisHeatingPLHP->heatRecoveryRate, 0.001); - EXPECT_NEAR(11698.391, thisHeatingPLHP->sourceSideHeatTransfer, 0.001); - // heat balance or energy conservation applied at the evaporator (source side) - Real64 heatBalanceEvap = thisHeatingPLHP->loadSideHeatTransfer - thisHeatingPLHP->powerUsage; - EXPECT_NEAR(46654.825, heatBalanceEvap, 0.001); - // heat rejected is split b/n heat recovery and source side heat transfer due to temp limit - EXPECT_NEAR(34956.434, thisHeatingPLHP->heatRecoveryRate, 0.001); - EXPECT_NEAR(11698.391, thisHeatingPLHP->sourceSideHeatTransfer, 0.001); - // check the heat recovered at the evaporator (source) side - Real64 chilledWaterEnergyRecovered = thisHeatingPLHP->heatRecoveryRate + thisHeatingPLHP->sourceSideHeatTransfer; - EXPECT_NEAR(46654.825, chilledWaterEnergyRecovered, 0.001); - // check energy balance - EXPECT_NEAR(heatBalanceEvap, chilledWaterEnergyRecovered, 0.001); - EXPECT_NEAR(7.0, thisHeatingPLHP->heatRecoveryInletTemp, 0.001); - // heat recovery outlet temperature is capped @ 4.5C. - EXPECT_NEAR(4.5, thisHeatingPLHP->heatRecoveryOutletTemp, 0.001); - } -} - -TEST_F(EnergyPlusFixture, CoolingSimulate_WSHP_SourceSideOutletTemp) -{ - std::string const idf_objects = delimited_string({"HeatPump:PlantLoop:EIR:Cooling,", - " hp cooling side,", - " node 1,", - " node 2,", - " WaterSource,", - " node 3,", - " node 4,", - " ,", - " ,", - " ,", - " 0.0002,", - " 0.0002,", - " ,", - " 2000,", - " 3.00,", - " ,", - " dummyCurve,", - " dummyCurve,", - " dummyCurve;", - "Curve:Linear,", - " dummyCurve,", - " 0.95,", - " 0,", - " 1,", - " 1;"}); - ASSERT_TRUE(process_idf(idf_objects)); - state->init_state(*state); - - // set up the plant loops - // first the load side - state->dataPlnt->TotNumLoops = 2; - state->dataPlnt->PlantLoop.allocate(2); - - state->dataPlnt->PlantLoop(1).FluidName = "WATER"; - state->dataPlnt->PlantLoop(1).glycol = Fluid::GetWater(*state); - state->dataPlnt->PlantLoop(1).LoopDemandCalcScheme = DataPlant::LoopDemandCalcScheme::SingleSetPoint; - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).TotalBranches = 1; - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch.allocate(1); - 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 &PLHPPlantLoadSideComp = state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp(1); - PLHPPlantLoadSideComp.Type = DataPlant::PlantEquipmentType::HeatPumpEIRCooling; - PLHPPlantLoadSideComp.CurOpSchemeType = DataPlant::OpScheme::CompSetPtBased; - // then the source side - - state->dataPlnt->PlantLoop(2).FluidName = "WATER"; - state->dataPlnt->PlantLoop(2).glycol = Fluid::GetWater(*state); - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).TotalBranches = 1; - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch.allocate(1); - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).TotalComponents = 1; - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp.allocate(1); - auto &PLHPPlantLoadSourceComp = state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp(1); - PLHPPlantLoadSourceComp.Type = DataPlant::PlantEquipmentType::HeatPumpEIRCooling; - - // the init call expects a "from" calling point - PlantLocation myLoadLocation = PlantLocation(1, DataPlant::LoopSideLocation::Supply, 1, 1); - PlantLocation mySourceLocation = PlantLocation(2, DataPlant::LoopSideLocation::Demand, 1, 1); - - // call the factory with a valid name to trigger reading inputs - EIRPlantLoopHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpEIRCooling, "HP COOLING SIDE"); - - // verify the size of the vector and the processed condition - EXPECT_EQ(1u, state->dataEIRPlantLoopHeatPump->heatPumps.size()); - - // for now we know the order is maintained, so get each heat pump object - EIRPlantLoopHeatPump *thisCoolingPLHP = &state->dataEIRPlantLoopHeatPump->heatPumps[0]; - - // do a bit of extra wiring up to the plant - PLHPPlantLoadSideComp.Name = thisCoolingPLHP->name; - PLHPPlantLoadSideComp.NodeNumIn = thisCoolingPLHP->loadSideNodes.inlet; - PLHPPlantLoadSourceComp.Name = thisCoolingPLHP->name; - PLHPPlantLoadSourceComp.NodeNumIn = thisCoolingPLHP->sourceSideNodes.inlet; - - // call for all initialization - state->dataGlobal->BeginEnvrnFlag = true; - state->dataPlnt->PlantFirstSizesOkayToFinalize = true; - thisCoolingPLHP->onInitLoopEquip(*state, myLoadLocation); - - // call from load side location, firsthvac, no load, not running, verify the unit doesn't have any values lingering - thisCoolingPLHP->loadSideHeatTransfer = 2000; - thisCoolingPLHP->loadSideInletTemp = 23.0; - thisCoolingPLHP->loadSideOutletTemp = 42.0; - thisCoolingPLHP->powerUsage = 400.0; - thisCoolingPLHP->sourceSideHeatTransfer = 2000.0; - thisCoolingPLHP->sourceSideInletTemp = 45.0; - thisCoolingPLHP->sourceSideOutletTemp = 83.0; - bool firstHVAC = true; - Real64 curLoad = 0.0; - bool runFlag = false; - thisCoolingPLHP->heatRecoveryHeatPump = true; - thisCoolingPLHP->simulate(*state, myLoadLocation, firstHVAC, curLoad, runFlag); - EXPECT_NEAR(0.0, thisCoolingPLHP->loadSideHeatTransfer, 0.001); - EXPECT_NEAR(0.0, thisCoolingPLHP->sourceSideHeatTransfer, 0.001); - EXPECT_NEAR(0.0, thisCoolingPLHP->powerUsage, 0.001); - EXPECT_NEAR(thisCoolingPLHP->loadSideInletTemp, thisCoolingPLHP->loadSideOutletTemp, 0.001); - EXPECT_NEAR(thisCoolingPLHP->sourceSideInletTemp, thisCoolingPLHP->sourceSideOutletTemp, 0.001); - - // call from source side location, firsthvac, no load, not running, connected loop should be triggered to resimulate - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).SimLoopSideNeeded = false; - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).SimLoopSideNeeded = false; - thisCoolingPLHP->simulate(*state, mySourceLocation, firstHVAC, curLoad, runFlag); - EXPECT_TRUE(state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).SimLoopSideNeeded); - - thisCoolingPLHP->setPointNodeNum = thisCoolingPLHP->loadSideNodes.outlet; - - // now we can call it again from the load side, but this time there is load (still firsthvac, unit can meet load) - { - firstHVAC = true; - curLoad = -1900; - runFlag = true; - Real64 constexpr expectedLoadMassFlowRate = 0.200; - state->dataLoopNodes->Node(thisCoolingPLHP->loadSideNodes.inlet).MassFlowRate = expectedLoadMassFlowRate; - state->dataLoopNodes->Node(thisCoolingPLHP->sourceSideNodes.inlet).MassFlowRate = expectedLoadMassFlowRate; - Real64 constexpr expectedCp = 4183; - Real64 constexpr specifiedLoadSetpoint = 15; - Real64 const calculatedLoadInletTemp = specifiedLoadSetpoint - curLoad / (expectedLoadMassFlowRate * expectedCp); - state->dataLoopNodes->Node(thisCoolingPLHP->setPointNodeNum).TempSetPoint = specifiedLoadSetpoint; - state->dataLoopNodes->Node(thisCoolingPLHP->loadSideNodes.inlet).Temp = calculatedLoadInletTemp; - state->dataLoopNodes->Node(thisCoolingPLHP->sourceSideNodes.inlet).Temp = 45; - thisCoolingPLHP->maxSourceTempLimit = 50.0; - thisCoolingPLHP->simulate(*state, myLoadLocation, firstHVAC, curLoad, runFlag); - // expect it to meet setpoint and have some pre-evaluated conditions - EXPECT_NEAR(1.000, thisCoolingPLHP->partLoadRatio, 0.01); - EXPECT_NEAR(specifiedLoadSetpoint, thisCoolingPLHP->loadSideOutletTemp, 0.01); - EXPECT_NEAR(-curLoad, thisCoolingPLHP->loadSideHeatTransfer, 0.01); - EXPECT_NEAR(2471.583, thisCoolingPLHP->sourceSideHeatTransfer, 0.01); - EXPECT_NEAR(47.957, thisCoolingPLHP->sourceSideOutletTemp, 0.01); - } - - // now we can call it again from the load side, but this time there is source side temperature limit exceeded - { - firstHVAC = true; - curLoad = -1900; - runFlag = true; - Real64 constexpr expectedLoadMassFlowRate = 0.200; - state->dataLoopNodes->Node(thisCoolingPLHP->loadSideNodes.inlet).MassFlowRate = expectedLoadMassFlowRate; - state->dataLoopNodes->Node(thisCoolingPLHP->sourceSideNodes.inlet).MassFlowRate = expectedLoadMassFlowRate; - Real64 constexpr expectedCp = 4183; - Real64 constexpr specifiedLoadSetpoint = 15; - Real64 const calculatedLoadInletTemp = specifiedLoadSetpoint - curLoad / (expectedLoadMassFlowRate * expectedCp); - state->dataLoopNodes->Node(thisCoolingPLHP->setPointNodeNum).TempSetPoint = specifiedLoadSetpoint; - state->dataLoopNodes->Node(thisCoolingPLHP->loadSideNodes.inlet).Temp = calculatedLoadInletTemp; - state->dataLoopNodes->Node(thisCoolingPLHP->sourceSideNodes.inlet).Temp = 48; - thisCoolingPLHP->maxSourceTempLimit = 50.0; - thisCoolingPLHP->simulate(*state, myLoadLocation, firstHVAC, curLoad, runFlag); - // expect it to meet setpoint and have some pre-evaluated conditions - // reduced PLR to meet the source side outlet temperature limit specified - EXPECT_NEAR(0.68, thisCoolingPLHP->partLoadRatio, 0.01); - EXPECT_NEAR(15.73, thisCoolingPLHP->loadSideOutletTemp, 0.01); - EXPECT_NEAR(1285.38, thisCoolingPLHP->loadSideHeatTransfer, 0.01); - EXPECT_NEAR(386.69, thisCoolingPLHP->powerUsage, 0.01); - EXPECT_NEAR(1672.07, thisCoolingPLHP->loadSideHeatTransfer + thisCoolingPLHP->powerUsage, 0.01); - EXPECT_NEAR(1672.07, thisCoolingPLHP->sourceSideHeatTransfer, 0.01); - 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)); - state->init_state(*state); - - // set up the plant loops - // first the load side - state->dataPlnt->TotNumLoops = 1; - state->dataPlnt->PlantLoop.allocate(1); - - state->dataPlnt->PlantLoop(1).FluidName = "WATER"; - state->dataPlnt->PlantLoop(1).glycol = Fluid::GetWater(*state); - 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 hardcoded 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->curves(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->curves(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->curves(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->curves(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->curves(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->curves(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->curves(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->curves(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->curves(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->curves(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 -// EnergyPlus, Copyright (c) 1996-present, The Board of Trustees of the University of Illinois, -// The Regents of the University of California, through Lawrence Berkeley National Laboratory -// (subject to receipt of any required approvals from the U.S. Dept. of Energy), Oak Ridge -// National Laboratory, managed by UT-Battelle, Alliance for Energy Innovation, LLC, and other -// contributors. All rights reserved. -// -// NOTICE: This Software was developed under funding from the U.S. Department of Energy and the -// U.S. Government consequently retains certain rights. As such, the U.S. Government has been -// granted for itself and others acting on its behalf a paid-up, nonexclusive, irrevocable, -// worldwide license in the Software to reproduce, distribute copies to the public, prepare -// derivative works, and perform publicly and display publicly, and to permit others to do so. -// -// Redistribution and use in source and binary forms, with or without modification, are permitted -// provided that the following conditions are met: -// -// (1) Redistributions of source code must retain the above copyright notice, this list of -// conditions and the following disclaimer. -// -// (2) Redistributions in binary form must reproduce the above copyright notice, this list of -// conditions and the following disclaimer in the documentation and/or other materials -// provided with the distribution. -// -// (3) Neither the name of the University of California, Lawrence Berkeley National Laboratory, -// the University of Illinois, U.S. Dept. of Energy nor the names of its contributors may be -// used to endorse or promote products derived from this software without specific prior -// written permission. -// -// (4) Use of EnergyPlus(TM) Name. If Licensee (i) distributes the software in stand-alone form -// without changes from the version obtained under this License, or (ii) Licensee makes a -// reference solely to the software portion of its product, Licensee must refer to the -// software as "EnergyPlus version X" software, where "X" is the version number Licensee -// obtained under this License and may not use a different name for the software. Except as -// specifically required in this Section (4), Licensee shall not use in a company name, a -// product name, in advertising, publicity, or other promotional activities any name, trade -// name, trademark, logo, or other designation of "EnergyPlus", "E+", "e+" or confusingly -// similar designation, without the U.S. Department of Energy's prior written consent. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR -// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY -// AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR -// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -// POSSIBILITY OF SUCH DAMAGE. - -#pragma clang diagnostic push -#pragma ide diagnostic ignored "OCDFAInspection" -#pragma ide diagnostic ignored "cert-err58-cpp" -#pragma ide diagnostic ignored "modernize-use-equals-delete" - -#include - -// Google Test Headers -#include - -// EnergyPlus Headers -#include "Fixtures/EnergyPlusFixture.hh" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -using namespace EnergyPlus; -using namespace EnergyPlus::EIRPlantLoopHeatPumps; - -TEST_F(EnergyPlusFixture, ConstructionFullObjectsHeatingAndCooling_WaterSource) -{ - std::string const idf_objects = delimited_string({"HeatPump:PlantLoop:EIR:Heating,", - " hp heating side,", - " node 1,", - " node 2,", - " WaterSource,", - " node 3,", - " node 4,", - " ,", - " ,", - " hp cooling side,", - " 0.001,", - " 0.001,", - " ,", - " 1000,", - " 3.14,", - " 2,", - " dummyCurve,", - " dummyCurve,", - " dummyCurve;", - "HeatPump:PlantLoop:EIR:Cooling,", - " hp cooling side,", - " node 1,", - " node 2,", - " WaterSource,", - " node 3,", - " node 4,", - " ,", - " ,", - " hp heating side,", - " 0.001,", - " 0.001,", - " ,", - " 1000,", - " 3.14,", - " 2,", - " dummyCurve,", - " dummyCurve,", - " dummyCurve;", - "Curve:Linear,", - " dummyCurve,", - " 1,", - " 0,", - " 1,", - " 1;"}); - ASSERT_TRUE(process_idf(idf_objects)); - state->init_state(*state); - - // call the factory with a valid name to trigger reading inputs - EIRPlantLoopHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpEIRHeating, "HP HEATING SIDE"); - - // verify the size of the vector and the processed condition - EXPECT_EQ(2u, state->dataEIRPlantLoopHeatPump->heatPumps.size()); - - // for now we know the order is maintained, so get each heat pump object - EIRPlantLoopHeatPump *thisHeatingPLHP = &state->dataEIRPlantLoopHeatPump->heatPumps[1]; - EIRPlantLoopHeatPump *thisCoolingPLHP = &state->dataEIRPlantLoopHeatPump->heatPumps[0]; - - // validate the heating side - EXPECT_EQ("HP HEATING SIDE", thisHeatingPLHP->name); - EXPECT_ENUM_EQ(DataPlant::PlantEquipmentType::HeatPumpEIRHeating, thisHeatingPLHP->EIRHPType); - EXPECT_EQ(thisCoolingPLHP, thisHeatingPLHP->companionHeatPumpCoil); - EXPECT_EQ(1, thisHeatingPLHP->capFuncTempCurveIndex); - EXPECT_EQ(1, thisHeatingPLHP->powerRatioFuncTempCurveIndex); - EXPECT_EQ(1, thisHeatingPLHP->powerRatioFuncPLRCurveIndex); - - // validate the cooling side - EXPECT_EQ("HP COOLING SIDE", thisCoolingPLHP->name); - EXPECT_ENUM_EQ(DataPlant::PlantEquipmentType::HeatPumpEIRCooling, thisCoolingPLHP->EIRHPType); - EXPECT_EQ(thisHeatingPLHP, thisCoolingPLHP->companionHeatPumpCoil); - EXPECT_EQ(1, thisCoolingPLHP->capFuncTempCurveIndex); - EXPECT_EQ(1, thisCoolingPLHP->powerRatioFuncTempCurveIndex); - EXPECT_EQ(1, thisCoolingPLHP->powerRatioFuncPLRCurveIndex); - - // calling the factory with an invalid name or type will call ShowFatalError, which will trigger a runtime exception - EXPECT_THROW(EIRPlantLoopHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpEIRHeating, "fake"), std::runtime_error); - EXPECT_THROW(EIRPlantLoopHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpEIRCooling, "HP HEATING SIDE"), std::runtime_error); - EXPECT_THROW(EIRPlantLoopHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpEIRCooling, "fake"), std::runtime_error); - EXPECT_THROW(EIRPlantLoopHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpEIRHeating, "HP COOLING SIDE"), std::runtime_error); -} - -TEST_F(EnergyPlusFixture, PairingCompanionCoils) -{ - state->dataEIRPlantLoopHeatPump->heatPumps.resize(2); - EIRPlantLoopHeatPump *coil1 = &state->dataEIRPlantLoopHeatPump->heatPumps[0]; - EIRPlantLoopHeatPump *coil2 = &state->dataEIRPlantLoopHeatPump->heatPumps[1]; - - { - // a successful try - coil1->name = "name1"; - coil1->companionCoilName = "name2"; - coil1->EIRHPType = DataPlant::PlantEquipmentType::HeatPumpEIRCooling; - coil1->companionHeatPumpCoil = nullptr; - coil2->name = "name2"; - coil2->companionCoilName = "name1"; - coil2->EIRHPType = DataPlant::PlantEquipmentType::HeatPumpEIRHeating; - coil2->companionHeatPumpCoil = nullptr; - EIRPlantLoopHeatPumps::EIRPlantLoopHeatPump::pairUpCompanionCoils(*state); - EXPECT_EQ(coil2, coil1->companionHeatPumpCoil); - EXPECT_EQ(coil1, coil2->companionHeatPumpCoil); - } - - { - // but what if we can't find a companion! - coil1->name = "name1"; - coil1->companionCoilName = "name6"; - coil1->EIRHPType = DataPlant::PlantEquipmentType::HeatPumpEIRCooling; - coil1->companionHeatPumpCoil = nullptr; - coil2->name = "name2"; - coil2->companionCoilName = "name1"; - coil2->EIRHPType = DataPlant::PlantEquipmentType::HeatPumpEIRHeating; - coil2->companionHeatPumpCoil = nullptr; - EXPECT_THROW(EIRPlantLoopHeatPumps::EIRPlantLoopHeatPump::pairUpCompanionCoils(*state), std::runtime_error); - } - - { - // or what if we find a companion but it's the same coil type - coil1->name = "name1"; - coil1->companionCoilName = "name2"; - coil1->EIRHPType = DataPlant::PlantEquipmentType::HeatPumpEIRCooling; - coil1->companionHeatPumpCoil = nullptr; - coil2->name = "name2"; - coil2->companionCoilName = "name1"; - coil2->EIRHPType = DataPlant::PlantEquipmentType::HeatPumpEIRCooling; - coil2->companionHeatPumpCoil = nullptr; - EXPECT_THROW(EIRPlantLoopHeatPumps::EIRPlantLoopHeatPump::pairUpCompanionCoils(*state), std::runtime_error); - } -} - -TEST_F(EnergyPlusFixture, HeatingConstructionFullObjectsNoCompanion) -{ - std::string const idf_objects = delimited_string({"HeatPump:PlantLoop:EIR:Heating,", - " hp heating side,", - " node 1,", - " node 2,", - " WaterSource,", - " node 3,", - " node 4,", - " ,", - " ,", - " ,", - " 0.001,", - " 0.001,", - " ,", - " 1000,", - " 3.14,", - " 1,", - " dummyCurve,", - " dummyCurve,", - " dummyCurve;", - "Curve:Linear,", - " dummyCurve,", - " 1,", - " 0,", - " 1,", - " 1;"}); - ASSERT_TRUE(process_idf(idf_objects)); - state->init_state(*state); - - // call the factory with a valid name to trigger reading inputs - EIRPlantLoopHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpEIRHeating, "HP HEATING SIDE"); - - // verify the size of the vector and the processed condition - EXPECT_EQ(1u, state->dataEIRPlantLoopHeatPump->heatPumps.size()); - - // for now we know the order is maintained, so get each heat pump object - EIRPlantLoopHeatPump *thisHeatingPLHP = &state->dataEIRPlantLoopHeatPump->heatPumps[0]; - - // validate the heating side - EXPECT_EQ("HP HEATING SIDE", thisHeatingPLHP->name); - EXPECT_ENUM_EQ(DataPlant::PlantEquipmentType::HeatPumpEIRHeating, thisHeatingPLHP->EIRHPType); - EXPECT_EQ(nullptr, thisHeatingPLHP->companionHeatPumpCoil); - EXPECT_EQ(1, thisHeatingPLHP->capFuncTempCurveIndex); - EXPECT_EQ(1, thisHeatingPLHP->powerRatioFuncTempCurveIndex); - EXPECT_EQ(1, thisHeatingPLHP->powerRatioFuncPLRCurveIndex); - - // calling the factory with an invalid name or type will call ShowFatalError, which will trigger a runtime exception - EXPECT_THROW(EIRPlantLoopHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpEIRHeating, "fake"), std::runtime_error); - EXPECT_THROW(EIRPlantLoopHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpEIRCooling, "HP HEATING SIDE"), std::runtime_error); -} - -TEST_F(EnergyPlusFixture, CoolingConstructionFullObjectsNoCompanion) -{ - std::string const idf_objects = delimited_string({"HeatPump:PlantLoop:EIR:Cooling,", - " hp cooling side,", - " node 1,", - " node 2,", - " WaterSource,", - " node 3,", - " node 4,", - " ,", - " ,", - " ,", - " 0.001,", - " 0.001,", - " ,", - " 1000,", - " 3.14,", - " 1,", - " dummyCurve,", - " dummyCurve,", - " dummyCurve;", - "Curve:Linear,", - " dummyCurve,", - " 1,", - " 0,", - " 1,", - " 1;"}); - ASSERT_TRUE(process_idf(idf_objects)); - state->init_state(*state); - - // call the factory with a valid name to trigger reading inputs - EIRPlantLoopHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpEIRCooling, "HP COOLING SIDE"); - - // verify the size of the vector and the processed condition - EXPECT_EQ(1u, state->dataEIRPlantLoopHeatPump->heatPumps.size()); - - // for now we know the order is maintained, so get each heat pump object - EIRPlantLoopHeatPump *thisCoolingPLHP = &state->dataEIRPlantLoopHeatPump->heatPumps[0]; - - // validate the cooling side - EXPECT_EQ("HP COOLING SIDE", thisCoolingPLHP->name); - EXPECT_ENUM_EQ(DataPlant::PlantEquipmentType::HeatPumpEIRCooling, thisCoolingPLHP->EIRHPType); - EXPECT_EQ(nullptr, thisCoolingPLHP->companionHeatPumpCoil); - EXPECT_EQ(1, thisCoolingPLHP->capFuncTempCurveIndex); - EXPECT_EQ(1, thisCoolingPLHP->powerRatioFuncTempCurveIndex); - EXPECT_EQ(1, thisCoolingPLHP->powerRatioFuncPLRCurveIndex); - - // calling the factory with an invalid name or type will call ShowFatalError, which will trigger a runtime exception - EXPECT_THROW(EIRPlantLoopHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpEIRCooling, "fake"), std::runtime_error); - EXPECT_THROW(EIRPlantLoopHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpEIRHeating, "HP COOLING SIDE"), std::runtime_error); -} - -TEST_F(EnergyPlusFixture, CoolingConstructionFullObjectWithDefaults) -{ - std::string const idf_objects = delimited_string({"HeatPump:PlantLoop:EIR:Cooling,", - " hp cooling side,", - " node 1,", - " node 2,", - " WaterSource,", - " node 3,", - " node 4,", - " ,", - " ,", - " ,", - " 0.001,", - " 0.001,", - " ,", - " 1000,", - " ,", - " ,", - " dummyCurve,", - " dummyCurve,", - " dummyCurve;", - "Curve:Linear,", - " dummyCurve,", - " 1,", - " 0,", - " 1,", - " 1;"}); - ASSERT_TRUE(process_idf(idf_objects)); - state->init_state(*state); - - // call the factory with a valid name to trigger reading inputs - EIRPlantLoopHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpEIRCooling, "HP COOLING SIDE"); - - // verify the size of the vector and the processed condition - EXPECT_EQ(1u, state->dataEIRPlantLoopHeatPump->heatPumps.size()); - - // for now we know the order is maintained, so get each heat pump object - EIRPlantLoopHeatPump *thisCoolingPLHP = &state->dataEIRPlantLoopHeatPump->heatPumps[0]; - - // validate the cooling side - EXPECT_EQ("HP COOLING SIDE", thisCoolingPLHP->name); - EXPECT_ENUM_EQ(DataPlant::PlantEquipmentType::HeatPumpEIRCooling, thisCoolingPLHP->EIRHPType); - EXPECT_NEAR(1, thisCoolingPLHP->sizingFactor, 0.001); -} - -TEST_F(EnergyPlusFixture, CoolingConstructionFullyAutoSized_WaterSource) -{ - std::string const idf_objects = delimited_string({"HeatPump:PlantLoop:EIR:Cooling,", - " hp cooling side,", - " node 1,", - " node 2,", - " WaterSource,", - " node 3,", - " node 4,", - " ,", - " ,", - " ,", - " Autosize,", - " Autosize,", - " ,", - " Autosize,", - " ,", - " 1,", - " dummyCurve,", - " dummyCurve,", - " dummyCurve;", - "Curve:Linear,", - " dummyCurve,", - " 1,", - " 0,", - " 1,", - " 1;"}); - ASSERT_TRUE(process_idf(idf_objects)); - state->init_state(*state); - - // call the factory with a valid name to trigger reading inputs - EIRPlantLoopHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpEIRCooling, "HP COOLING SIDE"); - - // verify the size of the vector and the processed condition - EXPECT_EQ(1u, state->dataEIRPlantLoopHeatPump->heatPumps.size()); - - // for now we know the order is maintained, so get each heat pump object - EIRPlantLoopHeatPump *thisCoolingPLHP = &state->dataEIRPlantLoopHeatPump->heatPumps[0]; - - // validate the cooling side - EXPECT_EQ("HP COOLING SIDE", thisCoolingPLHP->name); - EXPECT_ENUM_EQ(DataPlant::PlantEquipmentType::HeatPumpEIRCooling, thisCoolingPLHP->EIRHPType); - EXPECT_EQ(nullptr, thisCoolingPLHP->companionHeatPumpCoil); - EXPECT_EQ(1, thisCoolingPLHP->capFuncTempCurveIndex); - EXPECT_EQ(1, thisCoolingPLHP->powerRatioFuncTempCurveIndex); - EXPECT_EQ(1, thisCoolingPLHP->powerRatioFuncPLRCurveIndex); - - // calling the factory with an invalid name or type will call ShowFatalError, which will trigger a runtime exception - EXPECT_THROW(EIRPlantLoopHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpEIRCooling, "fake"), std::runtime_error); - EXPECT_THROW(EIRPlantLoopHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpEIRHeating, "HP COOLING SIDE"), std::runtime_error); -} - -TEST_F(EnergyPlusFixture, CatchErrorsOnBadCurves) -{ - std::string const idf_objects = delimited_string({"HeatPump:PlantLoop:EIR:Cooling,", - " hp cooling side,", - " node 1,", - " node 2,", - " WaterSource,", - " node 3,", - " node 4,", - " ,", - " ,", - " ,", - " Autosize,", - " Autosize,", - " ,", - " Autosize,", - " ,", - " 1,", - " dummyCurveA,", - " dummyCurveB,", - " dummyCurveC;"}); - ASSERT_TRUE(process_idf(idf_objects)); - state->init_state(*state); - - // call the factory with a valid name to trigger reading inputs, it should throw for the bad curves - EXPECT_THROW(EIRPlantLoopHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpEIRCooling, "HP COOLING SIDE"), std::runtime_error); -} - -TEST_F(EnergyPlusFixture, HeatingSimulate_AirSource_AWHP) -{ - std::string const idf_objects = delimited_string({"HeatPump:AirToWater,", - "test_AWHP, !- Name", - ", !- Availability Schedule Name Heating", - ", !- Availability Schedule Name Cooling", - "Load , !- Operating Mode Control Method", - "SingleMode, !- Operating Mode Control Option for Multiple Unit", - ", !- Operating Mode Control Schedule Name", - ", !- Minimum Part Load Ratio", - "20 , !- Rated Inlet Air Temperature in Heating Mode", - "1.0, !- Rated Air Flow Rate in Heating Mode", - "50 , !- Rated Leaving Water Temperature in Heating Mode", - "0.0001, !- Rated Water Flow Rate in Heating Mode", - ", !- Minimum Outdoor Air Temperature in Heating Mode", - ", !- Maximum Outdoor Air Temperature in Heating Mode", - ", !- Minimum Leaving Water Temperature Curve Name in Heating Mode", - ", !- Maximum Leaving Water Temperature Curve Name in Heating Mode", - "1.0, !- Sizing Factor for Heating", - "25, !- Rated Inlet Air Temperature in Cooling Mode", - "0.002, !- Rated Air Flow Rate in Cooling Mode", - "22 , !- Rated Leaving Water Temperature in Cooling Mode", - "0.005, !- Rated Water Flow Rate in Cooling Mode", - ", !- Minimum Outdoor Air Temperature in Cooling Mode", - ", !- Maximum Outdoor Air Temperature in Cooling Mode", - ", !- Minimum Leaving Water Temperature Curve Name in Cooling Mode", - ", !- Maximum Leaving Water Temperature Curve Name in Cooling Mode", - "0.9, !- Sizing Factor for Cooling", - "Outdoor Air Inlet Node , !- Air Inlet Node Name", - "Outdoor Air Outlet Node, !- Air Outlet Node Name", - "Heating Coil Load Loop Intermediate Node, !- Hot Water Inlet Node Name", - "Heating Coil Load Loop Supply Outlet Node, !- Hot Water Outlet Node Name", - "Cooling Coil Load Loop Intermediate Node , !- Chilled Water Inlet Node Name", - "Cooling Coil Load Loop Supply Outlet Node , !- Chilled Water Outlet Node Name", - ", !- Maximum Outdoor Dry Bulb Temperature For Defrost Operation", - ", !- Heat Pump Defrost Control", - ", !- Defrost Time Period Fraction", - ", !- Resistive Defrost Heater Capacity", - ", !- Defrost Energy Input Ratio Function of Temperature Curve Name", - "1 , !- Compressor Multiplier", - "FixedSpeed , !- Control Type", - "100 , !- Crankcase Heater Capacity", - "EIRCurveFuncPLR, !- Crankcase Heater Capacity Function of Temperature Curve Name", - "20 , !- Maximum Ambient Temperature for Crankcase Heater Operation", - "1 , !- Number of Speeds for Heating", - "1000, !- Rated Heating Capacity at Speed 1", - "3.14, !- Rated COP for Heating at Speed 1", - "dummyCurve, !- Normalized Heating Capacity Function of Temperature Curve Name at Speed 1", - "dummyCurve, !- Heating Energy Input Ratio Function of Temperature Curve Name at Speed 1", - "dummyCurve, !- Heating Energy Input Ratio Function of PLR Curve Name at Speed 1", - ", !- Rated Heating Capacity at Speed 2", - ", !- Rated COP for Heating at Speed 2", - ", !- Normalized Heating Capacity Function of Temperature Curve Name at Speed 2", - ", !- Heating Energy Input Ratio Function of Temperature Curve Name at Speed 2", - ", !- Heating Energy Input Ratio Function of PLR Curve Name at Speed 2", - ", !- Rated Heating Capacity at Speed 3", - ", !- Rated COP for Heating at Speed 3", - ", !- Normalized Heating Capacity Function of Temperature Curve Name at Speed 3", - ", !- Heating Energy Input Ratio Function of Temperature Curve Name at Speed 3", - ", !- Heating Energy Input Ratio Function of PLR Curve Name at Speed 3", - ", !- Rated Heating Capacity at Speed 4", - ", !- Rated COP for Heating at Speed 4", - ", !- Normalized Heating Capacity Function of Temperature Curve Name at Speed 4", - ", !- Heating Energy Input Ratio Function of Temperature Curve Name at Speed 4", - ", !- Heating Energy Input Ratio Function of PLR Curve Name at Speed 4", - ", !- Rated Heating Capacity at Speed 5", - ", !- Rated COP for Heating at Speed 5", - ", !- Normalized Heating Capacity Function of Temperature Curve Name at Speed 5", - ", !- Heating Energy Input Ratio Function of Temperature Curve Name at Speed 5", - ", !- Heating Energy Input Ratio Function of PLR Curve Name at Speed 5", - ", !- Booster Mode On Heating", - ", !- Rated Heating Capacity in Booster Mode", - ", !- Rated Heating COP in Booster Mode", - ", !- Normalized Heating Capacity Function of Temperature Curve Name in Booster Mode", - ", !- Heating Energy Input Ratio Function of Temperature Curve Name in Booster Mode", - ", !- Heating Energy Input Ratio Function of PLR Curve Name in Booster Mode", - "1, !- Number of Speeds for Cooling", - "20000, !- Rated Cooling Capacity at Speed 1", - "3, !- Rated COP for Cooling at Speed 1", - "dummyCurve, !- Normalized Cooling Capacity Function of Temperature Curve Name at Speed 1", - "dummyCurve, !- Cooling Energy Input Ratio Function of Temperature Curve Name at Speed 1", - "dummyCurve, !- Cooling Energy Input Ratio Function of PLR Curve Name at Speed 1", - ", !- Rated Cooling Capacity at Speed 2", - ", !- Rated COP for Cooling at Speed 2", - ", !- Normalized Cooling Capacity Function of Temperature Curve Name at Speed 2", - ", !- Cooling Energy Input Ratio Function of Temperature Curve Name at Speed 2", - ", !- Cooling Energy Input Ratio Function of PLR Curve Name at Speed 2", - ", !- Rated Cooling Capacity at Speed 3", - ", !- Rated COP for Cooling at Speed 3", - ", !- Normalized Cooling Capacity Function of Temperature Curve Name at Speed 3", - ", !- Cooling Energy Input Ratio Function of Temperature Curve Name at Speed 3", - ", !- Cooling Energy Input Ratio Function of PLR Curve Name at Speed 3", - ", !- Rated Cooling Capacity at Speed 4", - ", !- Rated COP for Cooling at Speed 4", - ", !- Normalized Cooling Capacity Function of Temperature Curve Name at Speed 4", - ", !- Cooling Energy Input Ratio Function of Temperature Curve Name at Speed 4", - ", !- Cooling Energy Input Ratio Function of PLR Curve Name at Speed 4", - ", !- Rated Cooling Capacity at Speed 5", - ", !- Rated COP for Cooling at Speed 5", - ", !- Normalized Cooling Capacity Function of Temperature Curve Name at Speed 5", - ", !- Cooling Energy Input Ratio Function of Temperature Curve Name at Speed 5", - ", !- Cooling Energy Input Ratio Function of PLR Curve Name at Speed 5", - ", !- Booster Mode On Cooling", - ", !- Rated Cooling Capacity in Booster Mode", - ", !- Rated Cooling COP in Booster Mode", - ", !- Normalized Cooling Capacity Function of Temperature Curve Name in Booster Mode", - ", !- Cooling Energy Input Ratio Function of Temperature Curve Name in Booster Mode", - "; !- Cooling Energy Input Ratio Function of PLR Curve Name in Booster Mode", - - "Curve:Linear,", - " dummyCurve,", - " 0.95,", - " 0,", - " 1,", - " 1;"}); - ASSERT_TRUE(process_idf(idf_objects)); - state->init_state(*state); - - // set up the plant loops - // first the load side - state->dataPlnt->TotNumLoops = 2; - state->dataPlnt->PlantLoop.allocate(state->dataPlnt->TotNumLoops); - - state->dataPlnt->PlantLoop(1).FluidName = "WATER"; - state->dataPlnt->PlantLoop(1).glycol = Fluid::GetWater(*state); - state->dataPlnt->PlantLoop(1).LoopDemandCalcScheme = DataPlant::LoopDemandCalcScheme::SingleSetPoint; - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).TotalBranches = 1; - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch.allocate(1); - 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::HeatPumpAirToWaterHeating; - PLHPPlantLoadSideCompHeating.CurOpSchemeType = DataPlant::OpScheme::CompSetPtBased; - - state->dataPlnt->PlantLoop(2).FluidName = "WATER"; - state->dataPlnt->PlantLoop(2).glycol = Fluid::GetWater(*state); - state->dataPlnt->PlantLoop(2).LoopDemandCalcScheme = DataPlant::LoopDemandCalcScheme::SingleSetPoint; - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Supply).TotalBranches = 1; - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Supply).Branch.allocate(1); - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).TotalComponents = 1; - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp.allocate(1); - auto &PLHPPlantLoadSideCompCooling = state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp(1); - PLHPPlantLoadSideCompCooling.Type = DataPlant::PlantEquipmentType::HeatPumpAirToWaterCooling; - PLHPPlantLoadSideCompCooling.CurOpSchemeType = DataPlant::OpScheme::CompSetPtBased; - // the init call expects a "from" calling point - PlantLocation myLoadLocation = PlantLocation(1, DataPlant::LoopSideLocation::Supply, 1, 1); - - // call the factory with a valid name to trigger reading inputs - DataPlant::PlantEquipmentType equipType = DataPlant::PlantEquipmentType::HeatPumpAirToWaterHeating; - HeatPumpAirToWater::factory(*state, equipType, "TEST_AWHP"); - - // verify the size of the vector and the processed condition - EXPECT_EQ(2u, state->dataHeatPumpAirToWater->heatPumps.size()); - - // for now we know the order is maintained, so get each heat pump object - HeatPumpAirToWater *thisAWHPHeating = &state->dataHeatPumpAirToWater->heatPumps[1]; - HeatPumpAirToWater *thisAWHPCooling = &state->dataHeatPumpAirToWater->heatPumps[0]; - - // do a bit of extra wiring up to the plant - PLHPPlantLoadSideCompHeating.Name = thisAWHPHeating->name; - PLHPPlantLoadSideCompHeating.NodeNumIn = thisAWHPHeating->loadSideNodes.inlet; - PLHPPlantLoadSideCompCooling.Name = thisAWHPCooling->name; - PLHPPlantLoadSideCompCooling.NodeNumIn = thisAWHPCooling->loadSideNodes.inlet; - - // call for all initialization - state->dataGlobal->BeginEnvrnFlag = true; - state->dataPlnt->PlantFirstSizesOkayToFinalize = true; - thisAWHPHeating->onInitLoopEquip(*state, myLoadLocation); - thisAWHPHeating->loadSidePlantLoc.loopNum = 1; - thisAWHPHeating->loadSidePlantLoc.loopSideNum = EnergyPlus::DataPlant::LoopSideLocation::Supply; - thisAWHPHeating->loadSidePlantLoc.branchNum = 1; - thisAWHPHeating->loadSidePlantLoc.compNum = 1; - thisAWHPCooling->loadSidePlantLoc.loopNum = 2; - thisAWHPCooling->loadSidePlantLoc.loopSideNum = EnergyPlus::DataPlant::LoopSideLocation::Supply; - thisAWHPCooling->loadSidePlantLoc.branchNum = 1; - thisAWHPCooling->loadSidePlantLoc.compNum = 1; - - thisAWHPHeating->setPointNodeNum = thisAWHPHeating->loadSideNodes.outlet; - - // call it from the load side, but this time there is a negative (cooling) load - shouldn't try to run - { - bool firstHVAC = true; - Real64 curLoad = -900; - bool runFlag = true; // plant actually shouldn't do this but the component can be smart enough to handle it - Real64 constexpr specifiedLoadSetpoint = 45; - Real64 constexpr loadInletTemp = 46; - state->dataLoopNodes->Node(thisAWHPHeating->setPointNodeNum).TempSetPoint = specifiedLoadSetpoint; - state->dataLoopNodes->Node(thisAWHPHeating->loadSideNodes.inlet).Temp = loadInletTemp; - state->dataLoopNodes->Node(thisAWHPHeating->sourceSideNodes.inlet).Temp = 30; - thisAWHPHeating->simulate(*state, myLoadLocation, firstHVAC, curLoad, runFlag); - // expect it to meet setpoint and have some pre-evaluated conditions - EXPECT_NEAR(loadInletTemp, thisAWHPHeating->loadSideOutletTemp, 0.001); - EXPECT_NEAR(0.0, thisAWHPHeating->loadSideHeatTransfer, 0.001); - } - - // call it from the load side, but this time there is load (still firsthvac, unit can meet load) - { - bool firstHVAC = true; - Real64 curLoad = 800; - bool runFlag = true; - Real64 constexpr expectedLoadMassFlowRate = 0.09999; - Real64 constexpr expectedCp = 4180; - Real64 constexpr specifiedLoadSetpoint = 45; - Real64 const calculatedLoadInletTemp = specifiedLoadSetpoint - curLoad / (expectedLoadMassFlowRate * expectedCp); - state->dataLoopNodes->Node(thisAWHPHeating->setPointNodeNum).TempSetPoint = specifiedLoadSetpoint; - state->dataLoopNodes->Node(thisAWHPHeating->loadSideNodes.inlet).Temp = calculatedLoadInletTemp; - state->dataLoopNodes->Node(thisAWHPHeating->sourceSideNodes.inlet).Temp = 30; - // if need to test defrost, uncomment this - // state->dataEnvrn->OutBaroPress = 98934; - thisAWHPHeating->simulate(*state, myLoadLocation, firstHVAC, curLoad, runFlag); - // expect it to meet setpoint and have some pre-evaluated conditions - EXPECT_NEAR(specifiedLoadSetpoint, thisAWHPHeating->loadSideOutletTemp, 0.001); - EXPECT_NEAR(curLoad, thisAWHPHeating->loadSideHeatTransfer, 0.001); - } - - // now we can call it again from the load side, but this time there is load (still firsthvac, unit cannot meet load) - { - bool firstHVAC = true; - Real64 curLoad = 1200; - Real64 availableCapacity = 950.0; - bool runFlag = true; - Real64 constexpr expectedLoadMassFlowRate = 0.09999; - Real64 constexpr expectedCp = 4180; - Real64 constexpr specifiedLoadSetpoint = 45; - Real64 const calculatedLoadInletTemp = specifiedLoadSetpoint - curLoad / (expectedLoadMassFlowRate * expectedCp); - state->dataLoopNodes->Node(thisAWHPHeating->setPointNodeNum).TempSetPoint = specifiedLoadSetpoint; - state->dataLoopNodes->Node(thisAWHPHeating->loadSideNodes.inlet).Temp = calculatedLoadInletTemp; - state->dataLoopNodes->Node(thisAWHPHeating->sourceSideNodes.inlet).Temp = 30; - thisAWHPHeating->simulate(*state, myLoadLocation, firstHVAC, curLoad, runFlag); - // expect it to miss setpoint and be at max capacity - EXPECT_NEAR(44.402, thisAWHPHeating->loadSideOutletTemp, 0.001); - EXPECT_NEAR(availableCapacity, thisAWHPHeating->loadSideHeatTransfer, 0.001); - } - - // now we can call it again from the load side, but this time there is no load (still firsthvac) - { - bool firstHVAC = true; - Real64 curLoad = 0.0; - bool runFlag = true; - Real64 constexpr expectedLoadMassFlowRate = 0.09999; - Real64 constexpr expectedCp = 4180; - Real64 constexpr specifiedLoadSetpoint = 45; - Real64 const calculatedLoadInletTemp = specifiedLoadSetpoint - curLoad / (expectedLoadMassFlowRate * expectedCp); - state->dataLoopNodes->Node(thisAWHPHeating->setPointNodeNum).TempSetPoint = specifiedLoadSetpoint; - state->dataLoopNodes->Node(thisAWHPHeating->loadSideNodes.inlet).Temp = calculatedLoadInletTemp; - state->dataLoopNodes->Node(thisAWHPHeating->sourceSideNodes.inlet).Temp = 30; - thisAWHPHeating->simulate(*state, myLoadLocation, firstHVAC, curLoad, runFlag); - // expect it to miss setpoint and be at max capacity - EXPECT_NEAR(45.0, thisAWHPHeating->loadSideOutletTemp, 0.001); - EXPECT_NEAR(30.0, thisAWHPHeating->sourceSideOutletTemp, 0.001); - } -} - -TEST_F(EnergyPlusFixture, processInputForEIRPLHP_AWHP) -{ - std::string const idf_objects = delimited_string({ - "HeatPump:AirToWater,", - "test_AWHP, !- Name", - ", !- Availability Schedule Name Heating", - ", !- Availability Schedule Name Cooling", - "Load , !- Operating Mode Control Method", - "SingleMode, !- Operating Mode Control Option for Multiple Unit", - ", !- Operating Mode Control Schedule Name", - ", !- Minimum Part Load Ratio", - "20 , !- Rated Inlet Air Temperature in Heating Mode", - "0.1 , !- Rated Air Flow Rate in Heating Mode", - "50 , !- Rated Leaving Water Temperature in Heating Mode", - "0.02 , !- Rated Water Flow Rate in Heating Mode", - "-20, !- Minimum Outdoor Air Temperature in Heating Mode", - "25, !- Maximum Outdoor Air Temperature in Heating Mode", - "MinLWTvsOAT, !- Minimum Leaving Water Temperature Curve Name in Heating Mode", - "MaxLWTvsOAT, !- Maximum Leaving Water Temperature Curve Name in Heating Mode", - "1.0, !- Sizing Factor for Heating", - "25, !- Rated Inlet Air Temperature in Cooling Mode", - "0.1 , !- Rated Air Flow Rate in Cooling Mode", - "22 , !- Rated Leaving Water Temperature in Cooling Mode", - "0.05 , !- Rated Water Flow Rate in Cooling Mode", - "18 , !- Minimum Outdoor Air Temperature in Cooling Mode", - "40, !- Maximum Outdoor Air Temperature in Cooling Mode", - "MinLWTvsOAT, !- Minimum Leaving Water Temperature Curve Name in Cooling Mode", - "MaxLWTvsOAT, !- Maximum Leaving Water Temperature Curve Name in Cooling Mode", - "0.9, !- Sizing Factor for Cooling", - "Outdoor Air Inlet Node , !- Air Inlet Node Name", - "Outdoor Air Outlet Node, !- Air Outlet Node Name", - "Heating Coil Load Loop Intermediate Node, !- Hot Water Inlet Node Name", - "Heating Coil Load Loop Supply Outlet Node, !- Hot Water Outlet Node Name", - "Cooling Coil Load Loop Intermediate Node , !- Chilled Water Inlet Node Name", - "Cooling Coil Load Loop Supply Outlet Node , !- Chilled Water Outlet Node Name", - "10, !- Maximum Outdoor Dry Bulb Temperature For Defrost Operation", - "Timed, !- Heat Pump Defrost Control", - "0.2, !- Defrost Time Period Fraction", - "150, !- Resistive Defrost Heater Capacity", - ", !- Defrost Energy Input Ratio Function of Temperature Curve Name", - "1 , !- Compressor Multiplier", - "FixedSpeed , !- Control Type", - "100 , !- Crankcase Heater Capacity", - "EIRCurveFuncPLR, !- Crankcase Heater Capacity Function of Temperature Curve Name", - "20 , !- Maximum Ambient Temperature for Crankcase Heater Operation", - "2 , !- Number of Speeds for Heating", - "100 , !- Rated Heating Capacity at Speed 1", - "3, !- Rated COP for Heating at Speed 1", - "CapCurveFuncTemp, !- Normalized Heating Capacity Function of Temperature Curve Name at Speed 1", - "EIRCurveFuncTemp, !- Heating Energy Input Ratio Function of Temperature Curve Name at Speed 1", - "EIRCurveFuncPLR, !- Heating Energy Input Ratio Function of PLR Curve Name at Speed 1", - "200, !- Rated Heating Capacity at Speed 2", - "3.5, !- Rated COP for Heating at Speed 2", - "CapCurveFuncTemp, !- Normalized Heating Capacity Function of Temperature Curve Name at Speed 2", - "EIRCurveFuncTemp, !- Heating Energy Input Ratio Function of Temperature Curve Name at Speed 2", - "EIRCurveFuncPLR, !- Heating Energy Input Ratio Function of PLR Curve Name at Speed 2", - ", !- Rated Heating Capacity at Speed 3", - ", !- Rated COP for Heating at Speed 3", - ", !- Normalized Heating Capacity Function of Temperature Curve Name at Speed 3", - ", !- Heating Energy Input Ratio Function of Temperature Curve Name at Speed 3", - ", !- Heating Energy Input Ratio Function of PLR Curve Name at Speed 3", - ", !- Rated Heating Capacity at Speed 4", - ", !- Rated COP for Heating at Speed 4", - ", !- Normalized Heating Capacity Function of Temperature Curve Name at Speed 4", - ", !- Heating Energy Input Ratio Function of Temperature Curve Name at Speed 4", - ", !- Heating Energy Input Ratio Function of PLR Curve Name at Speed 4", - ", !- Rated Heating Capacity at Speed 5", - ", !- Rated COP for Heating at Speed 5", - ", !- Normalized Heating Capacity Function of Temperature Curve Name at Speed 5", - ", !- Heating Energy Input Ratio Function of Temperature Curve Name at Speed 5", - ", !- Heating Energy Input Ratio Function of PLR Curve Name at Speed 5", - "Yes, !- Booster Mode On Heating", - "50000, !- Rated Heating Capacity in Booster Mode", - "2.5, !- Rated Heating COP in Booster Mode", - "CapCurveFuncTemp, !- Normalized Heating Capacity Function of Temperature Curve Name in Booster Mode", - "EIRCurveFuncTemp, !- Heating Energy Input Ratio Function of Temperature Curve Name in Booster Mode", - "EIRCurveFuncPLR, !- Heating Energy Input Ratio Function of PLR Curve Name in Booster Mode", - "2, !- Number of Speeds for Cooling", - "120 , !- Rated Cooling Capacity at Speed 1", - "4, !- Rated COP for Cooling at Speed 1", - "CapCurveFuncTemp, !- Normalized Cooling Capacity Function of Temperature Curve Name at Speed 1", - "EIRCurveFuncTemp, !- Cooling Energy Input Ratio Function of Temperature Curve Name at Speed 1", - "EIRCurveFuncPLR, !- Cooling Energy Input Ratio Function of PLR Curve Name at Speed 1", - "240, !- Rated Cooling Capacity at Speed 2", - "3.5, !- Rated COP for Cooling at Speed 2", - "CapCurveFuncTemp, !- Normalized Cooling Capacity Function of Temperature Curve Name at Speed 2", - "EIRCurveFuncTemp, !- Cooling Energy Input Ratio Function of Temperature Curve Name at Speed 2", - "EIRCurveFuncPLR, !- Cooling Energy Input Ratio Function of PLR Curve Name at Speed 2", - ", !- Rated Cooling Capacity at Speed 3", - ", !- Rated COP for Cooling at Speed 3", - ", !- Normalized Cooling Capacity Function of Temperature Curve Name at Speed 3", - ", !- Cooling Energy Input Ratio Function of Temperature Curve Name at Speed 3", - ", !- Cooling Energy Input Ratio Function of PLR Curve Name at Speed 3", - ", !- Rated Cooling Capacity at Speed 4", - ", !- Rated COP for Cooling at Speed 4", - ", !- Normalized Cooling Capacity Function of Temperature Curve Name at Speed 4", - ", !- Cooling Energy Input Ratio Function of Temperature Curve Name at Speed 4", - ", !- Cooling Energy Input Ratio Function of PLR Curve Name at Speed 4", - ", !- Rated Cooling Capacity at Speed 5", - ", !- Rated COP for Cooling at Speed 5", - ", !- Normalized Cooling Capacity Function of Temperature Curve Name at Speed 5", - ", !- Cooling Energy Input Ratio Function of Temperature Curve Name at Speed 5", - ", !- Cooling Energy Input Ratio Function of PLR Curve Name at Speed 5", - "Yes, !- Booster Mode On Cooling", - "500, !- Rated Cooling Capacity in Booster Mode", - "2.0, !- Rated Cooling COP in Booster Mode", - "CapCurveFuncTemp, !- Normalized Cooling Capacity Function of Temperature Curve Name in Booster Mode", - "EIRCurveFuncTemp, !- Cooling Energy Input Ratio Function of Temperature Curve Name in Booster Mode", - "EIRCurveFuncPLR; !- Cooling Energy Input Ratio Function of PLR Curve Name in Booster Mode", - - "HeatPump:AirToWater,", - "test_AWHP_defaults, !- Name", - ", !- Availability Schedule Name Heating", - ", !- Availability Schedule Name Cooling", - ", !- Operating Mode Control Method", - ", !- Operating Mode Control Option for Multiple Unit", - ", !- Operating Mode Control Schedule Name", - ", !- Minimum Part Load Ratio", - ", !- Rated Inlet Air Temperature in Heating Mode", - ", !- Rated Air Flow Rate in Heating Mode", - ", !- Rated Leaving Water Temperature in Heating Mode", - ", !- Rated Water Flow Rate in Heating Mode", - ", !- Minimum Outdoor Air Temperature in Heating Mode", - ", !- Maximum Outdoor Air Temperature in Heating Mode", - ", !- Minimum Leaving Water Temperature Curve Name in Heating Mode", - ", !- Maximum Leaving Water Temperature Curve Name in Heating Mode", - ", !- Sizing Factor for Heating", - ", !- Rated Inlet Air Temperature in Cooling Mode", - ", !- Rated Air Flow Rate in Cooling Mode", - ", !- Rated Leaving Water Temperature in Cooling Mode", - ", !- Rated Water Flow Rate in Cooling Mode", - ", !- Minimum Outdoor Air Temperature in Cooling Mode", - ", !- Maximum Outdoor Air Temperature in Cooling Mode", - ", !- Minimum Leaving Water Temperature Curve Name in Cooling Mode", - ", !- Maximum Leaving Water Temperature Curve Name in Cooling Mode", - ", !- Sizing Factor for Cooling", - "Outdoor Air Inlet Node , !- Air Inlet Node Name", - "Outdoor Air Outlet Node, !- Air Outlet Node Name", - "Heating Coil Load Loop Intermediate Node, !- Hot Water Inlet Node Name", - "Heating Coil Load Loop Supply Outlet Node, !- Hot Water Outlet Node Name", - "Cooling Coil Load Loop Intermediate Node , !- Chilled Water Inlet Node Name", - "Cooling Coil Load Loop Supply Outlet Node , !- Chilled Water Outlet Node Name", - ", !- Maximum Outdoor Dry Bulb Temperature For Defrost Operation", - ", !- Heat Pump Defrost Control", - ", !- Defrost Time Period Fraction", - ", !- Resistive Defrost Heater Capacity", - ", !- Defrost Energy Input Ratio Function of Temperature Curve Name", - ", !- Compressor Multiplier", - ", !- Control Type", - ", !- Crankcase Heater Capacity", - ", !- Crankcase Heater Capacity Function of Temperature Curve Name", - ", !- Maximum Ambient Temperature for Crankcase Heater Operation", - ", !- Number of Speeds for Heating", - ", !- Rated Heating Capacity at Speed 1", - ", !- Rated COP for Heating at Speed 1", - "CapCurveFuncTemp, !- Normalized Heating Capacity Function of Temperature Curve Name at Speed 1", - "EIRCurveFuncTemp, !- Heating Energy Input Ratio Function of Temperature Curve Name at Speed 1", - "EIRCurveFuncPLR, !- Heating Energy Input Ratio Function of PLR Curve Name at Speed 1", - ", !- Rated Heating Capacity at Speed 2", - ", !- Rated COP for Heating at Speed 2", - ", !- Normalized Heating Capacity Function of Temperature Curve Name at Speed 2", - ", !- Heating Energy Input Ratio Function of Temperature Curve Name at Speed 2", - ", !- Heating Energy Input Ratio Function of PLR Curve Name at Speed 2", - ", !- Rated Heating Capacity at Speed 3", - ", !- Rated COP for Heating at Speed 3", - ", !- Normalized Heating Capacity Function of Temperature Curve Name at Speed 3", - ", !- Heating Energy Input Ratio Function of Temperature Curve Name at Speed 3", - ", !- Heating Energy Input Ratio Function of PLR Curve Name at Speed 3", - ", !- Rated Heating Capacity at Speed 4", - ", !- Rated COP for Heating at Speed 4", - ", !- Normalized Heating Capacity Function of Temperature Curve Name at Speed 4", - ", !- Heating Energy Input Ratio Function of Temperature Curve Name at Speed 4", - ", !- Heating Energy Input Ratio Function of PLR Curve Name at Speed 4", - ", !- Rated Heating Capacity at Speed 5", - ", !- Rated COP for Heating at Speed 5", - ", !- Normalized Heating Capacity Function of Temperature Curve Name at Speed 5", - ", !- Heating Energy Input Ratio Function of Temperature Curve Name at Speed 5", - ", !- Heating Energy Input Ratio Function of PLR Curve Name at Speed 5", - ", !- Booster Mode On Heating", - ", !- Rated Heating Capacity in Booster Mode", - ", !- Rated Heating COP in Booster Mode", - ", !- Normalized Heating Capacity Function of Temperature Curve Name in Booster Mode", - ", !- Heating Energy Input Ratio Function of Temperature Curve Name in Booster Mode", - ", !- Heating Energy Input Ratio Function of PLR Curve Name in Booster Mode", - ", !- Number of Speeds for Cooling", - ", !- Rated Cooling Capacity at Speed 1", - ", !- Rated COP for Cooling at Speed 1", - "CapCurveFuncTemp, !- Normalized Cooling Capacity Function of Temperature Curve Name at Speed 1", - "EIRCurveFuncTemp, !- Cooling Energy Input Ratio Function of Temperature Curve Name at Speed 1", - "EIRCurveFuncPLR, !- Cooling Energy Input Ratio Function of PLR Curve Name at Speed 1", - ", !- Rated Cooling Capacity at Speed 2", - ", !- Rated COP for Cooling at Speed 2", - ", !- Normalized Cooling Capacity Function of Temperature Curve Name at Speed 2", - ", !- Cooling Energy Input Ratio Function of Temperature Curve Name at Speed 2", - ", !- Cooling Energy Input Ratio Function of PLR Curve Name at Speed 2", - ", !- Rated Cooling Capacity at Speed 3", - ", !- Rated COP for Cooling at Speed 3", - ", !- Normalized Cooling Capacity Function of Temperature Curve Name at Speed 3", - ", !- Cooling Energy Input Ratio Function of Temperature Curve Name at Speed 3", - ", !- Cooling Energy Input Ratio Function of PLR Curve Name at Speed 3", - ", !- Rated Cooling Capacity at Speed 4", - ", !- Rated COP for Cooling at Speed 4", - ", !- Normalized Cooling Capacity Function of Temperature Curve Name at Speed 4", - ", !- Cooling Energy Input Ratio Function of Temperature Curve Name at Speed 4", - ", !- Cooling Energy Input Ratio Function of PLR Curve Name at Speed 4", - ", !- Rated Cooling Capacity at Speed 5", - ", !- Rated COP for Cooling at Speed 5", - ", !- Normalized Cooling Capacity Function of Temperature Curve Name at Speed 5", - ", !- Cooling Energy Input Ratio Function of Temperature Curve Name at Speed 5", - ", !- Cooling Energy Input Ratio Function of PLR Curve Name at Speed 5", - ", !- Booster Mode On Cooling", - ", !- Rated Cooling Capacity in Booster Mode", - ", !- Rated Cooling COP in Booster Mode", - ", !- Normalized Cooling Capacity Function of Temperature Curve Name in Booster Mode", - ", !- Cooling Energy Input Ratio Function of Temperature Curve Name in Booster Mode", - "; !- Cooling Energy Input Ratio Function of PLR Curve Name in Booster Mode", - - "Curve:Quadratic,", - " MinLWTvsOAT, !- Name", - " 0.0, !- Coefficient1 Constant", - " 1.0, !- Coefficient2 x", - " 0.0, !- Coefficient3 x**2", - " -17.77778, !- Minimum Value of x", - " 35.0, !- Maximum Value of x", - " 20.0, !- Minimum Curve Output", - " 35.0, !- Maximum Curve Output", - " Temperature, !- Input Unit Type for X", - " Temperature; !- Output Unit Type", - - "Curve:Quadratic,", - " MaxLWTvsOAT, !- Name", - " 53.1666666666667, !- Coefficient1 Constant", - " 0.85, !- Coefficient2 x", - " 0.0, !- Coefficient3 x**2", - " -17.777778, !- Minimum Value of x", - " 35.0, !- Maximum Value of x", - " 20.0, !- Minimum Curve Output", - " 60.0, !- Maximum Curve Output", - " Temperature, !- Input Unit Type for X", - " Temperature; !- Output Unit Type", - - "Curve:Biquadratic,", - "CapCurveFuncTemp, !- Name", - "1.0, !- Coefficient1 Constant", - "0.0, !- Coefficient2 x", - "0.0, !- Coefficient3 x**2", - "0.0, !- Coefficient4 y", - "0.0, !- Coefficient5 y**2", - "0.0, !- Coefficient6 x*y", - "5.0, !- Minimum Value of x", - "10.0, !- Maximum Value of x", - "24.0, !- Minimum Value of y", - "35.0, !- Maximum Value of y", - ", !- Minimum Curve Output", - ", !- Maximum Curve Output", - "Temperature, !- Input Unit Type for X", - "Temperature, !- Input Unit Type for Y", - "Dimensionless; !- Output Unit Type", - - "Curve:Biquadratic,", - "EIRCurveFuncTemp, !- Name", - "1.0, !- Coefficient1 Constant", - "0.0, !- Coefficient2 x", - "0.0, !- Coefficient3 x**2", - "0.0, !- Coefficient4 y", - "0.0, !- Coefficient5 y**2", - "0.0, !- Coefficient6 x*y", - "5.0, !- Minimum Value of x", - "10.0, !- Maximum Value of x", - "24.0, !- Minimum Value of y", - "35.0, !- Maximum Value of y", - ", !- Minimum Curve Output", - ", !- Maximum Curve Output", - "Temperature, !- Input Unit Type for X", - "Temperature, !- Input Unit Type for Y", - "Dimensionless; !- Output Unit Type", - - "Curve:Quadratic,", - "EIRCurveFuncPLR, !- Name", - "1.0, !- Coefficient1 Constant", - "0.0, !- Coefficient2 x", - "0.0, !- Coefficient3 x**2", - "0.0, !- Minimum Value of x", - "1.0; !- Maximum Value of x", - }); - ASSERT_TRUE(process_idf(idf_objects)); - state->init_state(*state); - - DataPlant::PlantEquipmentType equipType = DataPlant::PlantEquipmentType::HeatPumpAirToWaterHeating; - HeatPumpAirToWater::factory(*state, equipType, "test_AWHP_defaults"); - equipType = DataPlant::PlantEquipmentType::HeatPumpAirToWaterCooling; - HeatPumpAirToWater::factory(*state, equipType, "test_AWHP_defaults"); - // cooling component in the AWHP - EXPECT_ENUM_EQ(state->dataHeatPumpAirToWater->heatPumps[0].EIRHPType, DataPlant::PlantEquipmentType::HeatPumpAirToWaterCooling); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[0].availSchedName, ""); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[0].availSched, Sched::GetScheduleAlwaysOn(*state)); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[0].operationModeControlSche, nullptr); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[0].sourceSideDesignInletTemp, 25); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[0].sourceSideDesignVolFlowRate, 0.1); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[0].ratedLeavingWaterTemperature, 22); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[0].loadSideDesignVolFlowRate, 0.05); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[0].minSourceTempLimit, 18); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[0].maxSourceTempLimit, 40); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[0].minSupplyWaterTempCurveIndex, 3); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[0].maxSupplyWaterTempCurveIndex, 4); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[0].sizingFactor, 0.9); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[0].loadSideNodes.inlet, 1); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[0].loadSideNodes.outlet, 2); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[0].sourceSideNodes.inlet, 3); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[0].sourceSideNodes.outlet, 4); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[0].heatPumpMultiplier, 1); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[0].controlType, HeatPumpAirToWater::CompressorControlType::FixedSpeed); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[0].boosterOn, true); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[0].numSpeeds, 3); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[0].referenceCapacity, 500); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[0].ratedCapacity[0], 120); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[0].ratedCOP[0], 4); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[0].capFuncTempCurveIndex[0], Curve::GetCurveIndex(*state, "CAPCURVEFUNCTEMP")); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[0].powerRatioFuncTempCurveIndex[0], Curve::GetCurveIndex(*state, "EIRCURVEFUNCTEMP")); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[0].powerRatioFuncPLRCurveIndex[0], Curve::GetCurveIndex(*state, "EIRCURVEFUNCPLR")); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[0].ratedCapacity[1], 240); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[0].ratedCOP[1], 3.5); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[0].capFuncTempCurveIndex[1], Curve::GetCurveIndex(*state, "CAPCURVEFUNCTEMP")); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[0].powerRatioFuncTempCurveIndex[1], Curve::GetCurveIndex(*state, "EIRCURVEFUNCTEMP")); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[0].powerRatioFuncPLRCurveIndex[1], Curve::GetCurveIndex(*state, "EIRCURVEFUNCPLR")); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[0].ratedCapacity[2], 500); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[0].ratedCOP[2], 2.0); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[0].capFuncTempCurveIndex[2], Curve::GetCurveIndex(*state, "CAPCURVEFUNCTEMP")); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[0].powerRatioFuncTempCurveIndex[2], Curve::GetCurveIndex(*state, "EIRCURVEFUNCTEMP")); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[0].powerRatioFuncPLRCurveIndex[2], Curve::GetCurveIndex(*state, "EIRCURVEFUNCPLR")); - EXPECT_ENUM_EQ(state->dataHeatPumpAirToWater->heatPumps[0].operatingModeControlMethod, HeatPumpAirToWater::OperatingModeControlMethod::Load); - EXPECT_ENUM_EQ(state->dataHeatPumpAirToWater->heatPumps[0].operatingModeControlOptionMultipleUnit, - HeatPumpAirToWater::OperatingModeControlOptionMultipleUnit::SingleMode); - - EXPECT_ENUM_EQ(state->dataHeatPumpAirToWater->heatPumps[1].EIRHPType, DataPlant::PlantEquipmentType::HeatPumpAirToWaterHeating); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[1].name, "TEST_AWHP"); - EXPECT_ENUM_EQ(state->dataHeatPumpAirToWater->heatPumps[1].operatingModeControlMethod, HeatPumpAirToWater::OperatingModeControlMethod::Load); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[1].availSchedName, ""); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[1].availSched, Sched::GetScheduleAlwaysOn(*state)); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[1].operationModeControlSche, nullptr); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[1].sourceSideDesignInletTemp, 20); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[1].sourceSideDesignVolFlowRate, 0.1); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[1].ratedLeavingWaterTemperature, 50); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[1].loadSideDesignVolFlowRate, 0.02); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[1].minSourceTempLimit, -20); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[1].maxSourceTempLimit, 25); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[1].minSupplyWaterTempCurveIndex, 3); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[1].maxSupplyWaterTempCurveIndex, 4); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[1].sizingFactor, 1.0); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[1].loadSideNodes.inlet, 5); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[1].loadSideNodes.outlet, 6); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[1].sourceSideNodes.inlet, 3); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[1].sourceSideNodes.outlet, 4); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[1].maxOutdoorTemperatureDefrost, 10); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[1].defrostStrategy, DefrostControl::Timed); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[1].defrostTime, 0.2); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[1].defrostResistiveHeaterCap, 150); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[1].defrostEIRFTIndex, 0); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[1].heatPumpMultiplier, 1); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[1].controlType, HeatPumpAirToWater::CompressorControlType::FixedSpeed); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[1].CrankcaseHeaterCapacity, 100); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[1].CrankcaseHeaterCapacityCurveIndex, Curve::GetCurveIndex(*state, "EIRCURVEFUNCPLR")); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[1].MaxOATCrankcaseHeater, 20); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[1].boosterOn, true); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[1].numSpeeds, 3); // with booster mode, one more speed level - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[1].referenceCapacity, 50000); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[1].ratedCapacity[0], 100); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[1].ratedCOP[0], 3); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[1].capFuncTempCurveIndex[0], Curve::GetCurveIndex(*state, "CAPCURVEFUNCTEMP")); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[1].powerRatioFuncTempCurveIndex[0], Curve::GetCurveIndex(*state, "EIRCURVEFUNCTEMP")); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[1].powerRatioFuncPLRCurveIndex[0], Curve::GetCurveIndex(*state, "EIRCURVEFUNCPLR")); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[1].ratedCapacity[1], 200); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[1].ratedCOP[1], 3.5); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[1].capFuncTempCurveIndex[1], Curve::GetCurveIndex(*state, "CAPCURVEFUNCTEMP")); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[1].powerRatioFuncTempCurveIndex[1], Curve::GetCurveIndex(*state, "EIRCURVEFUNCTEMP")); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[1].powerRatioFuncPLRCurveIndex[1], Curve::GetCurveIndex(*state, "EIRCURVEFUNCPLR")); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[1].ratedCapacity[2], 50000); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[1].ratedCOP[2], 2.5); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[1].capFuncTempCurveIndex[2], Curve::GetCurveIndex(*state, "CAPCURVEFUNCTEMP")); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[1].powerRatioFuncTempCurveIndex[2], Curve::GetCurveIndex(*state, "EIRCURVEFUNCTEMP")); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[1].powerRatioFuncPLRCurveIndex[2], Curve::GetCurveIndex(*state, "EIRCURVEFUNCPLR")); - EXPECT_ENUM_EQ(state->dataHeatPumpAirToWater->heatPumps[1].operatingModeControlMethod, HeatPumpAirToWater::OperatingModeControlMethod::Load); - EXPECT_ENUM_EQ(state->dataHeatPumpAirToWater->heatPumps[1].operatingModeControlOptionMultipleUnit, - HeatPumpAirToWater::OperatingModeControlOptionMultipleUnit::SingleMode); - - equipType = DataPlant::PlantEquipmentType::HeatPumpAirToWaterHeating; - HeatPumpAirToWater::factory(*state, equipType, "test_AWHP_defaults"); - equipType = DataPlant::PlantEquipmentType::HeatPumpAirToWaterCooling; - HeatPumpAirToWater::factory(*state, equipType, "test_AWHP_defaults"); - - // cooling component in the AWHP - EXPECT_ENUM_EQ(state->dataHeatPumpAirToWater->heatPumps[2].EIRHPType, DataPlant::PlantEquipmentType::HeatPumpAirToWaterCooling); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[2].name, "TEST_AWHP_DEFAULTS"); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[2].availSchedName, ""); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[2].availSched, Sched::GetScheduleAlwaysOn(*state)); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[2].operationModeControlSche, nullptr); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[2].sourceSideDesignInletTemp, 8); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[2].sourceSideDesignVolFlowRate, -99999); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[2].ratedLeavingWaterTemperature, 40); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[2].loadSideDesignVolFlowRate, -99999); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[2].minSourceTempLimit, -30); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[2].maxSourceTempLimit, 100); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[2].minSupplyWaterTempCurveIndex, 0); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[2].maxSupplyWaterTempCurveIndex, 0); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[2].sizingFactor, 1.0); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[2].heatPumpMultiplier, 1); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[2].controlType, HeatPumpAirToWater::CompressorControlType::VariableSpeed); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[2].boosterOn, false); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[2].boosterMultCap, 1.0); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[2].boosterMultCOP, 1.0); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[2].numSpeeds, 1); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[2].referenceCapacity, -99999); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[2].ratedCapacity[0], -99999); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[2].ratedCOP[0], 3.0); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[2].capFuncTempCurveIndex[0], Curve::GetCurveIndex(*state, "CAPCURVEFUNCTEMP")); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[2].powerRatioFuncTempCurveIndex[0], Curve::GetCurveIndex(*state, "EIRCURVEFUNCTEMP")); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[2].powerRatioFuncPLRCurveIndex[0], Curve::GetCurveIndex(*state, "EIRCURVEFUNCPLR")); - EXPECT_ENUM_EQ(state->dataHeatPumpAirToWater->heatPumps[2].operatingModeControlMethod, HeatPumpAirToWater::OperatingModeControlMethod::Load); - EXPECT_ENUM_EQ(state->dataHeatPumpAirToWater->heatPumps[2].operatingModeControlOptionMultipleUnit, - HeatPumpAirToWater::OperatingModeControlOptionMultipleUnit::SingleMode); - - EXPECT_ENUM_EQ(state->dataHeatPumpAirToWater->heatPumps[3].EIRHPType, DataPlant::PlantEquipmentType::HeatPumpAirToWaterHeating); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[3].name, "TEST_AWHP_DEFAULTS"); - EXPECT_ENUM_EQ(state->dataHeatPumpAirToWater->heatPumps[3].operatingModeControlMethod, HeatPumpAirToWater::OperatingModeControlMethod::Load); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[3].availSchedName, ""); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[3].availSched, Sched::GetScheduleAlwaysOn(*state)); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[3].operationModeControlSche, nullptr); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[3].sourceSideDesignInletTemp, 8); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[3].sourceSideDesignVolFlowRate, -99999); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[3].ratedLeavingWaterTemperature, 40); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[3].loadSideDesignVolFlowRate, -99999); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[3].minSourceTempLimit, -30); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[3].maxSourceTempLimit, 100); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[2].minSupplyWaterTempCurveIndex, 0); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[2].maxSupplyWaterTempCurveIndex, 0); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[3].sizingFactor, 1.0); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[3].maxOutdoorTemperatureDefrost, 10); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[3].defrostStrategy, DefrostControl::None); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[3].defrostTime, 0.0); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[3].defrostResistiveHeaterCap, 0.0); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[3].defrostEIRFTIndex, 0); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[3].heatPumpMultiplier, 1.0); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[3].controlType, HeatPumpAirToWater::CompressorControlType::VariableSpeed); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[3].CrankcaseHeaterCapacity, 0.0); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[3].CrankcaseHeaterCapacityCurveIndex, 0); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[3].MaxOATCrankcaseHeater, 10.0); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[3].boosterOn, false); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[3].boosterMultCap, 1.0); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[3].boosterMultCOP, 1.0); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[3].numSpeeds, 1); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[3].referenceCapacity, -99999); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[3].ratedCapacity[0], -99999); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[3].ratedCOP[0], 3.0); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[3].capFuncTempCurveIndex[0], Curve::GetCurveIndex(*state, "CAPCURVEFUNCTEMP")); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[3].powerRatioFuncTempCurveIndex[0], Curve::GetCurveIndex(*state, "EIRCURVEFUNCTEMP")); - EXPECT_EQ(state->dataHeatPumpAirToWater->heatPumps[3].powerRatioFuncPLRCurveIndex[0], Curve::GetCurveIndex(*state, "EIRCURVEFUNCPLR")); - EXPECT_ENUM_EQ(state->dataHeatPumpAirToWater->heatPumps[3].operatingModeControlMethod, HeatPumpAirToWater::OperatingModeControlMethod::Load); - EXPECT_ENUM_EQ(state->dataHeatPumpAirToWater->heatPumps[3].operatingModeControlOptionMultipleUnit, - HeatPumpAirToWater::OperatingModeControlOptionMultipleUnit::SingleMode); -} - -TEST_F(EnergyPlusFixture, calcLoadSideHeatTransfer_AWHP) -{ - auto thisAWHP = HeatPumpAirToWater(); - - state->dataPlnt->PlantLoop.allocate(1); - state->dataPlnt->PlantLoop(1).FluidName = "WATER"; - state->dataPlnt->PlantLoop(1).glycol = Fluid::GetWater(*state); - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).TotalBranches = 1; - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch.allocate(1); - 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 &PLHPPlantLoadSideComp = state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp(1); - PLHPPlantLoadSideComp.Type = DataPlant::PlantEquipmentType::HeatPumpAirToWaterCooling; - state->dataLoopNodes->Node.allocate(2); - thisAWHP.EIRHPType = EnergyPlus::DataPlant::PlantEquipmentType::HeatPumpAirToWaterCooling; - thisAWHP.loadSidePlantLoc = {1, DataPlant::LoopSideLocation::Supply, 1, 1}; - PlantUtilities::SetPlantLocationLinks(*state, thisAWHP.loadSidePlantLoc); - thisAWHP.loadSideNodes.outlet = 2; - thisAWHP.loadSideNodes.inlet = 1; - thisAWHP.loadSideMassFlowRate = 2; - thisAWHP.loadSideInletTemp = 20; - state->dataLoopNodes->Node(thisAWHP.loadSideNodes.inlet).Temp = thisAWHP.loadSideInletTemp; - Real64 CpLoad = thisAWHP.loadSidePlantLoc.loop->glycol->getSpecificHeat( - *state, state->dataLoopNodes->Node(thisAWHP.loadSideNodes.inlet).Temp, "HeatPumpAirToWater::calcLoadSideHeatTransfer()"); - // if capacity is less than load, heat transfer is just capacity - Real64 capacity = 100; - Real64 load = 120; - thisAWHP.calcLoadOutletTemp = EIRPlantLoopHeatPumps::HeatPumpAirToWater::subtract; - thisAWHP.calcLoadSideHeatTransfer(*state, capacity, load); - EXPECT_EQ(thisAWHP.loadSideHeatTransfer, capacity); - EXPECT_NEAR( - CpLoad * (thisAWHP.loadSideOutletTemp - thisAWHP.loadSideInletTemp) * thisAWHP.loadSideMassFlowRate, -thisAWHP.loadSideHeatTransfer, 1e-6); - - thisAWHP.EIRHPType = EnergyPlus::DataPlant::PlantEquipmentType::HeatPumpAirToWaterHeating; - thisAWHP.calcLoadOutletTemp = EIRPlantLoopHeatPumps::HeatPumpAirToWater::add; - thisAWHP.calcLoadSideHeatTransfer(*state, capacity, load); - EXPECT_EQ(thisAWHP.loadSideHeatTransfer, capacity); - EXPECT_NEAR( - CpLoad * (thisAWHP.loadSideOutletTemp - thisAWHP.loadSideInletTemp) * thisAWHP.loadSideMassFlowRate, thisAWHP.loadSideHeatTransfer, 1e-6); - - // if capacity is less than load, heat transfer is load - capacity = 120; - load = 100; - thisAWHP.EIRHPType = EnergyPlus::DataPlant::PlantEquipmentType::HeatPumpAirToWaterCooling; - thisAWHP.calcLoadOutletTemp = EIRPlantLoopHeatPumps::HeatPumpAirToWater::subtract; - thisAWHP.calcLoadSideHeatTransfer(*state, capacity, load); - EXPECT_EQ(thisAWHP.loadSideHeatTransfer, load); - EXPECT_NEAR( - CpLoad * (thisAWHP.loadSideOutletTemp - thisAWHP.loadSideInletTemp) * thisAWHP.loadSideMassFlowRate, -thisAWHP.loadSideHeatTransfer, 1e-6); - - thisAWHP.EIRHPType = EnergyPlus::DataPlant::PlantEquipmentType::HeatPumpAirToWaterHeating; - thisAWHP.calcLoadOutletTemp = EIRPlantLoopHeatPumps::HeatPumpAirToWater::add; - thisAWHP.calcLoadSideHeatTransfer(*state, capacity, load); - EXPECT_EQ(thisAWHP.loadSideHeatTransfer, load); - EXPECT_NEAR( - CpLoad * (thisAWHP.loadSideOutletTemp - thisAWHP.loadSideInletTemp) * thisAWHP.loadSideMassFlowRate, thisAWHP.loadSideHeatTransfer, 1e-6); - - // if load is smaller than minimum load (minimumPLR * availableCapacity), heat transfer is minimum load - capacity = 120; - load = 10; - thisAWHP.minimumPLR = 0.1; - thisAWHP.referenceCapacity = capacity; - thisAWHP.EIRHPType = EnergyPlus::DataPlant::PlantEquipmentType::HeatPumpAirToWaterCooling; - thisAWHP.calcLoadOutletTemp = EIRPlantLoopHeatPumps::HeatPumpAirToWater::subtract; - thisAWHP.calcLoadSideHeatTransfer(*state, capacity, load); - EXPECT_EQ(thisAWHP.loadSideHeatTransfer, capacity * thisAWHP.minimumPLR); -} - -TEST_F(EnergyPlusFixture, calcPowerUsage_AWHP) -{ - - std::string const idf_objects = delimited_string({ - "Curve:Biquadratic,", - "CapCurveFuncTemp, !- Name", - "1.0, !- Coefficient1 Constant", - "0.0, !- Coefficient2 x", - "0.0, !- Coefficient3 x**2", - "0.0, !- Coefficient4 y", - "0.0, !- Coefficient5 y**2", - "0.0, !- Coefficient6 x*y", - "5.0, !- Minimum Value of x", - "10.0, !- Maximum Value of x", - "24.0, !- Minimum Value of y", - "35.0, !- Maximum Value of y", - ", !- Minimum Curve Output", - ", !- Maximum Curve Output", - "Temperature, !- Input Unit Type for X", - "Temperature, !- Input Unit Type for Y", - "Dimensionless; !- Output Unit Type", - - "Curve:Biquadratic,", - "EIRCurveFuncTemp, !- Name", - "1.0, !- Coefficient1 Constant", - "0.0, !- Coefficient2 x", - "0.0, !- Coefficient3 x**2", - "0.0, !- Coefficient4 y", - "0.0, !- Coefficient5 y**2", - "0.0, !- Coefficient6 x*y", - "5.0, !- Minimum Value of x", - "10.0, !- Maximum Value of x", - "24.0, !- Minimum Value of y", - "35.0, !- Maximum Value of y", - ", !- Minimum Curve Output", - ", !- Maximum Curve Output", - "Temperature, !- Input Unit Type for X", - "Temperature, !- Input Unit Type for Y", - "Dimensionless; !- Output Unit Type", - - "Curve:Quadratic,", - "EIRCurveFuncPLR, !- Name", - "1.0, !- Coefficient1 Constant", - "0.0, !- Coefficient2 x", - "0.0, !- Coefficient3 x**2", - "0.0, !- Minimum Value of x", - "1.0; !- Maximum Value of x", - }); - ASSERT_TRUE(process_idf(idf_objects)); - state->init_state(*state); - - auto thisAWHP = HeatPumpAirToWater(); - state->dataPlnt->PlantLoop.allocate(1); - state->dataPlnt->PlantLoop(1).FluidName = "WATER"; - state->dataPlnt->PlantLoop(1).glycol = Fluid::GetWater(*state); - state->dataPlnt->PlantLoop(1).LoopDemandCalcScheme = DataPlant::LoopDemandCalcScheme::SingleSetPoint; - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).TotalBranches = 1; - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch.allocate(1); - 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 &PLHPPlantLoadSideComp = state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp(1); - PLHPPlantLoadSideComp.Type = DataPlant::PlantEquipmentType::HeatPumpAirToWaterCooling; - thisAWHP.loadSidePlantLoc = {1, DataPlant::LoopSideLocation::Supply, 1, 1}; - PlantUtilities::SetPlantLocationLinks(*state, thisAWHP.loadSidePlantLoc); - thisAWHP.loadSidePlantLoc.comp->CurOpSchemeType = DataPlant::OpScheme::CompSetPtBased; - thisAWHP.EIRHPType = EnergyPlus::DataPlant::PlantEquipmentType::HeatPumpAirToWaterCooling; - - thisAWHP.loadSideNodes.inlet = 1; - thisAWHP.loadSideNodes.outlet = 2; - thisAWHP.setPointNodeNum = thisAWHP.loadSideNodes.outlet; - state->dataLoopNodes->Node.allocate(2); - state->dataLoopNodes->Node(thisAWHP.setPointNodeNum).TempSetPoint = 20; - thisAWHP.sourceSideHeatTransfer = 100; - thisAWHP.loadSideHeatTransfer = 90; - thisAWHP.numSpeeds = 2; - thisAWHP.ratedCapacity[0] = 600; - thisAWHP.ratedCOP[0] = 1; - thisAWHP.capFuncTempCurveIndex[0] = Curve::GetCurveIndex(*state, "CAPCURVEFUNCTEMP"); - thisAWHP.powerRatioFuncTempCurveIndex[0] = Curve::GetCurveIndex(*state, "EIRCURVEFUNCTEMP"); - thisAWHP.powerRatioFuncPLRCurveIndex[0] = Curve::GetCurveIndex(*state, "EIRCURVEFUNCPLR"); - thisAWHP.ratedCapacity[1] = 1200; - thisAWHP.ratedCOP[1] = 1; - thisAWHP.capFuncTempCurveIndex[1] = Curve::GetCurveIndex(*state, "CAPCURVEFUNCTEMP"); - thisAWHP.powerRatioFuncTempCurveIndex[1] = Curve::GetCurveIndex(*state, "EIRCURVEFUNCTEMP"); - thisAWHP.powerRatioFuncPLRCurveIndex[1] = Curve::GetCurveIndex(*state, "EIRCURVEFUNCPLR"); - thisAWHP.heatPumpMultiplier = 2; - thisAWHP.cyclingRatio = 1.0; - Real64 availableCapacityBeforeMultiplier = thisAWHP.ratedCapacity[1]; - // when COP = 1, power usage should equal heat transfer - thisAWHP.loadSideHeatTransfer = 500; - thisAWHP.calcPowerUsage(*state, availableCapacityBeforeMultiplier); - EXPECT_EQ(thisAWHP.speedLevel, 1); - EXPECT_EQ(thisAWHP.powerUsage, 500); - EXPECT_EQ(thisAWHP.cyclingRatio, 500.0 / 600.0); - EXPECT_EQ(thisAWHP.numUnitUsed, 1); - thisAWHP.loadSideHeatTransfer = 1000; - thisAWHP.cyclingRatio = 1.0; // reset cycling ratio back to 1 - thisAWHP.calcPowerUsage(*state, availableCapacityBeforeMultiplier); - EXPECT_EQ(thisAWHP.speedLevel, 2); - EXPECT_EQ(thisAWHP.powerUsage, 1000); - EXPECT_EQ(thisAWHP.cyclingRatio, 1.0); - EXPECT_EQ(thisAWHP.numUnitUsed, 1); - thisAWHP.loadSideHeatTransfer = 1500; - thisAWHP.cyclingRatio = 1.0; // reset cycling ratio back to 1 - thisAWHP.calcPowerUsage(*state, availableCapacityBeforeMultiplier); - EXPECT_EQ(thisAWHP.speedLevel, 1); - EXPECT_EQ(thisAWHP.powerUsage, 1500); - EXPECT_EQ(thisAWHP.numUnitUsed, 2); - EXPECT_EQ(thisAWHP.cyclingRatio, 300.0 / 600.0); - thisAWHP.loadSideHeatTransfer = 2000; - thisAWHP.cyclingRatio = 1.0; // reset cycling ratio back to 1 - thisAWHP.calcPowerUsage(*state, availableCapacityBeforeMultiplier); - EXPECT_EQ(thisAWHP.speedLevel, 2); - EXPECT_EQ(thisAWHP.powerUsage, 2000); - EXPECT_EQ(thisAWHP.numUnitUsed, 2); - EXPECT_EQ(thisAWHP.cyclingRatio, 1.0); -} - -TEST_F(EnergyPlusFixture, crankcaseHeater_AWHP) -{ - std::string const idf_objects = delimited_string({ - "Curve:Linear,", - "heaterCapCurve, !- Name", - "10.0, !- Coefficient1 Constant", - "2., !- Coefficient2 x", - "-10.0, !- Minimum Value of x", - "70; !- Maximum Value of x", - }); - ASSERT_TRUE(process_idf(idf_objects)); - state->init_state(*state); - auto thisAWHP = HeatPumpAirToWater(); - thisAWHP.CrankcaseHeaterCapacity = 100; - thisAWHP.CrankcaseHeaterCapacityCurveIndex = Curve::GetCurveIndex(*state, "HEATERCAPCURVE"); - // when outdoor temperature is higher than temperature threshold, crankcase heater is off - state->dataEnvrn->OutDryBulbTemp = 15; - thisAWHP.MaxOATCrankcaseHeater = 10; - thisAWHP.CrankcaseHeaterPower = thisAWHP.calcCrankcaseHeaterPower(*state); - ASSERT_EQ(thisAWHP.CrankcaseHeaterPower, 0.0); - state->dataEnvrn->OutDryBulbTemp = 9; - thisAWHP.CrankcaseHeaterPower = thisAWHP.calcCrankcaseHeaterPower(*state); - ASSERT_EQ(thisAWHP.CrankcaseHeaterPower, 100 * (10 + 2 * 9)); -} - -TEST_F(EnergyPlusFixture, calcOpMode_AWHP) -{ - std::string const idf_objects = delimited_string({ - "Curve:Biquadratic," - "CapCurveFuncTemp, !- Name", - "1.0, !- Coefficient1 Constant", - "0.0, !- Coefficient2 x", - "0.0, !- Coefficient3 x**2", - "0.0, !- Coefficient4 y", - "0.0, !- Coefficient5 y**2", - "0.0, !- Coefficient6 x*y", - "5.0, !- Minimum Value of x", - "10.0, !- Maximum Value of x", - "24.0, !- Minimum Value of y", - "35.0, !- Maximum Value of y", - ", !- Minimum Curve Output", - ", !- Maximum Curve Output", - "Temperature, !- Input Unit Type for X", - "Temperature, !- Input Unit Type for Y", - "Dimensionless; !- Output Unit Type", - }); - ASSERT_TRUE(process_idf(idf_objects)); - state->init_state(*state); - auto thisAWHP = HeatPumpAirToWater(); - auto companionAWHP = HeatPumpAirToWater(); - thisAWHP.companionHeatPumpCoil = &companionAWHP; - companionAWHP.companionHeatPumpCoil = &thisAWHP; - thisAWHP.heatPumpMultiplier = 6; - companionAWHP.heatPumpMultiplier = 6; - thisAWHP.referenceCapacityOneUnit = 100; - companionAWHP.referenceCapacityOneUnit = 150; - thisAWHP.EIRHPType = EnergyPlus::DataPlant::PlantEquipmentType::HeatPumpAirToWaterCooling; - companionAWHP.EIRHPType = EnergyPlus::DataPlant::PlantEquipmentType::HeatPumpAirToWaterHeating; - - state->dataPlnt->PlantLoop.allocate(2); - state->dataPlnt->PlantLoop(1).FluidName = "WATER"; - state->dataPlnt->PlantLoop(1).glycol = Fluid::GetWater(*state); - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).TotalBranches = 1; - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch.allocate(1); - 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 &PLHPPlantLoadSideComp = state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp(1); - PLHPPlantLoadSideComp.Type = DataPlant::PlantEquipmentType::HeatPumpAirToWaterCooling; - thisAWHP.loadSidePlantLoc = {1, EnergyPlus::DataPlant::LoopSideLocation::Supply, 1, 1}; - PlantUtilities::SetPlantLocationLinks(*state, thisAWHP.loadSidePlantLoc); - thisAWHP.OperationModeEMSOverrideOn = false; - - state->dataPlnt->PlantLoop(2).FluidName = "WATER"; - state->dataPlnt->PlantLoop(2).glycol = Fluid::GetWater(*state); - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Supply).TotalBranches = 1; - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Supply).Branch.allocate(1); - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).TotalComponents = 1; - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp.allocate(1); - auto &companionPLHPPlantLoadSideComp = state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp(1); - companionPLHPPlantLoadSideComp.Type = DataPlant::PlantEquipmentType::HeatPumpAirToWaterHeating; - companionAWHP.loadSidePlantLoc = {2, EnergyPlus::DataPlant::LoopSideLocation::Supply, 1, 1}; - PlantUtilities::SetPlantLocationLinks(*state, companionAWHP.loadSidePlantLoc); - companionAWHP.OperationModeEMSOverrideOn = false; - - thisAWHP.capFuncTempCurveIndex[0] = 1; - thisAWHP.loadSideOutletTemp = 65; - thisAWHP.sourceSideInletTemp = 20; - companionAWHP.capFuncTempCurveIndex[0] = 1; - companionAWHP.loadSideOutletTemp = 65; - companionAWHP.sourceSideInletTemp = 20; - - Real64 currentLoad = -300; - companionPLHPPlantLoadSideComp.MyLoad = 400; - thisAWHP.calcOpMode(*state, currentLoad, HeatPumpAirToWater::OperatingModeControlOptionMultipleUnit::SingleMode); - EXPECT_EQ(thisAWHP.operatingMode, 0); - EXPECT_EQ(companionAWHP.operatingMode, 3); - - currentLoad = -500; - companionPLHPPlantLoadSideComp.MyLoad = 300; - thisAWHP.calcOpMode(*state, currentLoad, HeatPumpAirToWater::OperatingModeControlOptionMultipleUnit::SingleMode); - EXPECT_EQ(thisAWHP.operatingMode, 5); - EXPECT_EQ(companionAWHP.operatingMode, 0); - - currentLoad = -300; - companionPLHPPlantLoadSideComp.MyLoad = 400; - thisAWHP.calcOpMode(*state, currentLoad, HeatPumpAirToWater::OperatingModeControlOptionMultipleUnit::CoolingPriority); - EXPECT_EQ(thisAWHP.operatingMode, 3); - EXPECT_EQ(companionAWHP.operatingMode, 3); - - currentLoad = -500; - companionPLHPPlantLoadSideComp.MyLoad = 300; - thisAWHP.calcOpMode(*state, currentLoad, HeatPumpAirToWater::OperatingModeControlOptionMultipleUnit::CoolingPriority); - EXPECT_EQ(thisAWHP.operatingMode, 5); - EXPECT_EQ(companionAWHP.operatingMode, 1); - - currentLoad = -250; - companionPLHPPlantLoadSideComp.MyLoad = 250; - thisAWHP.calcOpMode(*state, currentLoad, HeatPumpAirToWater::OperatingModeControlOptionMultipleUnit::CoolingPriority); - EXPECT_EQ(thisAWHP.operatingMode, 3); - EXPECT_EQ(companionAWHP.operatingMode, 2); - - currentLoad = -300; - companionPLHPPlantLoadSideComp.MyLoad = 400; - thisAWHP.calcOpMode(*state, currentLoad, HeatPumpAirToWater::OperatingModeControlOptionMultipleUnit::HeatingPriority); - EXPECT_EQ(thisAWHP.operatingMode, 3); - EXPECT_EQ(companionAWHP.operatingMode, 3); - - currentLoad = -500; - companionPLHPPlantLoadSideComp.MyLoad = 300; - thisAWHP.calcOpMode(*state, currentLoad, HeatPumpAirToWater::OperatingModeControlOptionMultipleUnit::HeatingPriority); - EXPECT_EQ(thisAWHP.operatingMode, 4); - EXPECT_EQ(companionAWHP.operatingMode, 2); - - currentLoad = -250; - companionPLHPPlantLoadSideComp.MyLoad = 250; - thisAWHP.calcOpMode(*state, currentLoad, HeatPumpAirToWater::OperatingModeControlOptionMultipleUnit::HeatingPriority); - EXPECT_EQ(thisAWHP.operatingMode, 3); - EXPECT_EQ(companionAWHP.operatingMode, 2); - - currentLoad = -200; - companionPLHPPlantLoadSideComp.MyLoad = 1000; - thisAWHP.calcOpMode(*state, currentLoad, HeatPumpAirToWater::OperatingModeControlOptionMultipleUnit::HeatingPriority); - EXPECT_EQ(thisAWHP.operatingMode, 0); - EXPECT_EQ(companionAWHP.operatingMode, 6); - - currentLoad = 0; - companionPLHPPlantLoadSideComp.MyLoad = 400; - thisAWHP.calcOpMode(*state, currentLoad, HeatPumpAirToWater::OperatingModeControlOptionMultipleUnit::Balanced); - EXPECT_EQ(thisAWHP.operatingMode, 0); - EXPECT_EQ(companionAWHP.operatingMode, 3); - - currentLoad = 0; - companionPLHPPlantLoadSideComp.MyLoad = 4000; - thisAWHP.calcOpMode(*state, currentLoad, HeatPumpAirToWater::OperatingModeControlOptionMultipleUnit::Balanced); - EXPECT_EQ(thisAWHP.operatingMode, 0); - EXPECT_EQ(companionAWHP.operatingMode, 6); - - currentLoad = -400; - companionPLHPPlantLoadSideComp.MyLoad = 0; - thisAWHP.calcOpMode(*state, currentLoad, HeatPumpAirToWater::OperatingModeControlOptionMultipleUnit::Balanced); - EXPECT_EQ(thisAWHP.operatingMode, 4); - EXPECT_EQ(companionAWHP.operatingMode, 0); - - currentLoad = -4000; - companionPLHPPlantLoadSideComp.MyLoad = 0; - thisAWHP.calcOpMode(*state, currentLoad, HeatPumpAirToWater::OperatingModeControlOptionMultipleUnit::Balanced); - EXPECT_EQ(thisAWHP.operatingMode, 6); - EXPECT_EQ(companionAWHP.operatingMode, 0); - - currentLoad = -300; - companionPLHPPlantLoadSideComp.MyLoad = 400; - thisAWHP.calcOpMode(*state, currentLoad, HeatPumpAirToWater::OperatingModeControlOptionMultipleUnit::Balanced); - EXPECT_EQ(thisAWHP.operatingMode, 3); - EXPECT_EQ(companionAWHP.operatingMode, 3); - - currentLoad = -500; - companionPLHPPlantLoadSideComp.MyLoad = 300; - thisAWHP.calcOpMode(*state, currentLoad, HeatPumpAirToWater::OperatingModeControlOptionMultipleUnit::Balanced); - EXPECT_EQ(thisAWHP.operatingMode, 4); - EXPECT_EQ(companionAWHP.operatingMode, 2); - - currentLoad = -1000; - companionPLHPPlantLoadSideComp.MyLoad = 1500; - thisAWHP.calcOpMode(*state, currentLoad, HeatPumpAirToWater::OperatingModeControlOptionMultipleUnit::Balanced); - EXPECT_EQ(thisAWHP.operatingMode, 3); - EXPECT_EQ(companionAWHP.operatingMode, 3); -} - -TEST_F(EnergyPlusFixture, processInputForEIRPLHP_TestAirSourceDuplicateNodes) -{ - std::string const idf_objects = delimited_string({"HeatPump:PlantLoop:EIR:Cooling,", - " hp cooling side,", - " node 1,", - " node 2,", - " AirSource,", - " node 3,", - " node 3,", - " ,", - " ,", - " ,", - " 0.001,", - " 0.001,", - " ,", - " 1000,", - " 3.14,", - " ,", - " dummyCurve,", - " dummyCurve,", - " dummyCurve;", - "Curve:Linear,", - " dummyCurve,", - " 1,", - " 0,", - " 1,", - " 1;"}); - ASSERT_TRUE(process_idf(idf_objects)); - state->init_state(*state); - - // set up the plant loops - // first the load side - state->dataPlnt->TotNumLoops = 2; - state->dataPlnt->PlantLoop.allocate(2); - - state->dataPlnt->PlantLoop(1).FluidName = "WATER"; - state->dataPlnt->PlantLoop(1).glycol = Fluid::GetWater(*state); - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).TotalBranches = 1; - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch.allocate(1); - 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 &PLHPPlantLoadSideComp = state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp(1); - PLHPPlantLoadSideComp.Type = DataPlant::PlantEquipmentType::HeatPumpEIRCooling; - // then the source side - - state->dataPlnt->PlantLoop(2).FluidName = "WATER"; - state->dataPlnt->PlantLoop(2).glycol = Fluid::GetWater(*state); - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).TotalBranches = 1; - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch.allocate(1); - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).TotalComponents = 1; - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp.allocate(1); - auto &PLHPPlantLoadSourceComp = state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp(1); - PLHPPlantLoadSourceComp.Type = DataPlant::PlantEquipmentType::HeatPumpEIRCooling; - - // the init call expects a "from" calling point - PlantLocation myLocation = PlantLocation(1, DataPlant::LoopSideLocation::Supply, 1, 1); - - // call the factory with a valid name to trigger reading inputs - // expects fatal error due to same node names - EXPECT_THROW(EIRPlantLoopHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpEIRCooling, "HP COOLING SIDE");, std::runtime_error); - // expect error related to same node names - std::string error_msg = delimited_string({ - " ** Severe ** PlantLoopHeatPump hp cooling side has the same inlet and outlet node.", - " ** ~~~ ** Node Name: NODE 3", - " ** Fatal ** Previous EIR PLHP errors cause program termination", - " ...Summary of Errors that led to program termination:", - " ..... Reference severe error count=1", - " ..... Last severe error=PlantLoopHeatPump hp cooling side has the same inlet and outlet node.", - }); - EXPECT_TRUE(compare_err_stream(error_msg)); -} - -TEST_F(EnergyPlusFixture, processInputForEIRPLHP_TestAirSourceOANode) -{ - std::string const idf_objects = delimited_string({"HeatPump:PlantLoop:EIR:Cooling,", - " hp cooling side,", - " node 1,", - " node 2,", - " AirSource,", - " node 3,", - " node 4,", - " ,", - " ,", - " ,", - " Autosize,", - " Autosize,", - " ,", - " Autosize,", - " 1.0,", - " 1,", - " dummyCurve,", - " dummyCurve,", - " dummyCurve;", - "Curve:Linear,", - " dummyCurve,", - " 1,", - " 0,", - " 1,", - " 1;", - "OutdoorAir:NodeList,", - " node 3;"}); - ASSERT_TRUE(process_idf(idf_objects)); - state->init_state(*state); - - // set up the plant loops - // first the load side - state->dataPlnt->TotNumLoops = 2; - state->dataPlnt->PlantLoop.allocate(2); - - state->dataPlnt->PlantLoop(1).FluidName = "WATER"; - state->dataPlnt->PlantLoop(1).glycol = Fluid::GetWater(*state); - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).TotalBranches = 1; - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch.allocate(1); - 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 &PLHPPlantLoadSideComp = state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp(1); - PLHPPlantLoadSideComp.Type = DataPlant::PlantEquipmentType::HeatPumpEIRCooling; - // then the source side - - state->dataPlnt->PlantLoop(2).FluidName = "WATER"; - state->dataPlnt->PlantLoop(2).glycol = Fluid::GetWater(*state); - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).TotalBranches = 1; - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch.allocate(1); - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).TotalComponents = 1; - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp.allocate(1); - auto &PLHPPlantLoadSourceComp = state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp(1); - PLHPPlantLoadSourceComp.Type = DataPlant::PlantEquipmentType::HeatPumpEIRCooling; - - // the init call expects a "from" calling point - PlantLocation myLocation = PlantLocation(1, DataPlant::LoopSideLocation::Supply, 1, 1); - // setup the outdoor air nodes - OutAirNodeManager::SetOutAirNodes(*state); - // call the factory with a valid name to trigger reading inputs - // expects fatal error due to same node names - EIRPlantLoopHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpEIRCooling, "HP COOLING SIDE"); - EXPECT_TRUE(compare_err_stream("")); -} - -TEST_F(EnergyPlusFixture, processInputForEIRPLHP_TestAirSourceNoOANode) -{ - std::string const idf_objects = delimited_string({"HeatPump:PlantLoop:EIR:Cooling,", - " hp cooling side,", - " node 1,", - " node 2,", - " AirSource,", - " node 3,", - " node 4,", - " ,", - " ,", - " ,", - " Autosize,", - " Autosize,", - " ,", - " Autosize,", - " 1.0,", - " 1,", - " dummyCurve,", - " dummyCurve,", - " dummyCurve;", - "Curve:Linear,", - " dummyCurve,", - " 1,", - " 0,", - " 1,", - " 1;"}); - ASSERT_TRUE(process_idf(idf_objects)); - state->init_state(*state); - - // set up the plant loops - // first the load side - state->dataPlnt->TotNumLoops = 2; - state->dataPlnt->PlantLoop.allocate(2); - - state->dataPlnt->PlantLoop(1).FluidName = "WATER"; - state->dataPlnt->PlantLoop(1).glycol = Fluid::GetWater(*state); - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).TotalBranches = 1; - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch.allocate(1); - 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 &PLHPPlantLoadSideComp = state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp(1); - PLHPPlantLoadSideComp.Type = DataPlant::PlantEquipmentType::HeatPumpEIRCooling; - // then the source side - - state->dataPlnt->PlantLoop(2).FluidName = "WATER"; - state->dataPlnt->PlantLoop(2).glycol = Fluid::GetWater(*state); - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).TotalBranches = 1; - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch.allocate(1); - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).TotalComponents = 1; - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp.allocate(1); - auto &PLHPPlantLoadSourceComp = state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp(1); - PLHPPlantLoadSourceComp.Type = DataPlant::PlantEquipmentType::HeatPumpEIRCooling; - - // the init call expects a "from" calling point - PlantLocation myLocation = PlantLocation(1, DataPlant::LoopSideLocation::Supply, 1, 1); - - // call the factory with a valid name to trigger reading inputs - // expects fatal error due to same node names - EIRPlantLoopHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpEIRCooling, "HP COOLING SIDE"); - bool ErrFound = false; - Node::CheckNodeConnections(*state, ErrFound); - // expect error related to OA node not being an OutdoorAirNode - std::string error_msg = delimited_string({ - " ** Severe ** Node Connection Error, Node=\"NODE 1\", Inlet node did not find an appropriate matching \"outlet\" node.", - " ** ~~~ ** If this is an outdoor air inlet node, it must be listed in an OutdoorAir:Node or OutdoorAir:NodeList object.", - " ** ~~~ ** Reference Object=HeatPump:PlantLoop:EIR:Cooling, Name=HP COOLING SIDE", - " ** Severe ** Node Connection Error, Node=\"NODE 3\", Inlet node did not find an appropriate matching \"outlet\" node.", - " ** ~~~ ** If this is an outdoor air inlet node, it must be listed in an OutdoorAir:Node or OutdoorAir:NodeList object.", - " ** ~~~ ** Reference Object=HeatPump:PlantLoop:EIR:Cooling, Name=HP COOLING SIDE", - - }); - EXPECT_TRUE(compare_err_stream(error_msg)); -} - -TEST_F(EnergyPlusFixture, Initialization) -{ - std::string const idf_objects = delimited_string({"HeatPump:PlantLoop:EIR:Cooling,", - " hp cooling side,", - " node 1,", - " node 2,", - " WaterSource,", - " node 3,", - " node 4,", - " ,", - " ,", - " ,", - " 0.001,", - " 0.001,", - " ,", - " 1000,", - " 3.14,", - " ,", - " dummyCurve,", - " dummyCurve,", - " dummyCurve;", - "Curve:Linear,", - " dummyCurve,", - " 1,", - " 0,", - " 1,", - " 1;"}); - ASSERT_TRUE(process_idf(idf_objects)); - state->init_state(*state); - - // set up the plant loops - // first the load side - state->dataPlnt->TotNumLoops = 2; - state->dataPlnt->PlantLoop.allocate(2); - - state->dataPlnt->PlantLoop(1).FluidName = "WATER"; - state->dataPlnt->PlantLoop(1).glycol = Fluid::GetWater(*state); - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).TotalBranches = 1; - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch.allocate(1); - 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 &PLHPPlantLoadSideComp = state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp(1); - PLHPPlantLoadSideComp.Type = DataPlant::PlantEquipmentType::HeatPumpEIRCooling; - // then the source side - - state->dataPlnt->PlantLoop(2).FluidName = "WATER"; - state->dataPlnt->PlantLoop(2).glycol = Fluid::GetWater(*state); - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).TotalBranches = 1; - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch.allocate(1); - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).TotalComponents = 1; - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp.allocate(1); - auto &PLHPPlantLoadSourceComp = state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp(1); - PLHPPlantLoadSourceComp.Type = DataPlant::PlantEquipmentType::HeatPumpEIRCooling; - - // the init call expects a "from" calling point - PlantLocation myLocation = PlantLocation(1, DataPlant::LoopSideLocation::Supply, 1, 1); - - // call the factory with a valid name to trigger reading inputs - EIRPlantLoopHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpEIRCooling, "HP COOLING SIDE"); - - // verify the size of the vector and the processed condition - EXPECT_EQ(1u, state->dataEIRPlantLoopHeatPump->heatPumps.size()); - - // for now we know the order is maintained, so get each heat pump object - EIRPlantLoopHeatPump *thisCoolingPLHP = &state->dataEIRPlantLoopHeatPump->heatPumps[0]; - - // do a bit of extra wiring up to the plant - PLHPPlantLoadSideComp.Name = thisCoolingPLHP->name; - PLHPPlantLoadSideComp.NodeNumIn = thisCoolingPLHP->loadSideNodes.inlet; - PLHPPlantLoadSourceComp.Name = thisCoolingPLHP->name; - PLHPPlantLoadSourceComp.NodeNumIn = thisCoolingPLHP->sourceSideNodes.inlet; - - // call for initialization, oneTimeInit only first - state->dataGlobal->BeginEnvrnFlag = false; - thisCoolingPLHP->onInitLoopEquip(*state, myLocation); - - // validate that location work got done correctly - EXPECT_EQ(1, thisCoolingPLHP->loadSidePlantLoc.loopNum); - EXPECT_ENUM_EQ(DataPlant::LoopSideLocation::Supply, thisCoolingPLHP->loadSidePlantLoc.loopSideNum); - EXPECT_EQ(1, thisCoolingPLHP->loadSidePlantLoc.branchNum); - EXPECT_EQ(1, thisCoolingPLHP->loadSidePlantLoc.compNum); - EXPECT_EQ(2, thisCoolingPLHP->sourceSidePlantLoc.loopNum); - EXPECT_ENUM_EQ(DataPlant::LoopSideLocation::Demand, thisCoolingPLHP->sourceSidePlantLoc.loopSideNum); - EXPECT_EQ(1, thisCoolingPLHP->sourceSidePlantLoc.branchNum); - EXPECT_EQ(1, thisCoolingPLHP->sourceSidePlantLoc.compNum); - - // now call for initialization again, for begin environment - state->dataGlobal->BeginEnvrnFlag = true; - state->dataPlnt->PlantFirstSizesOkayToFinalize = true; - thisCoolingPLHP->onInitLoopEquip(*state, myLocation); - - // validate that plant sizing went ok - Real64 constexpr flowTol = 0.001; - Real64 constexpr rho = 999.89; // easy to edit here if the expected density gets adjusted in E+ - Real64 const expectedLoadSideMassFlow = rho * thisCoolingPLHP->loadSideDesignVolFlowRate; - Real64 const expectedSourceSideMassFlow = rho * thisCoolingPLHP->sourceSideDesignVolFlowRate; - EXPECT_NEAR(expectedLoadSideMassFlow, thisCoolingPLHP->loadSideDesignMassFlowRate, flowTol); - EXPECT_NEAR(expectedSourceSideMassFlow, thisCoolingPLHP->sourceSideDesignMassFlowRate, flowTol); - EXPECT_NEAR(0.0, state->dataLoopNodes->Node(thisCoolingPLHP->loadSideNodes.inlet).MassFlowRateMin, flowTol); - EXPECT_NEAR(0.0, state->dataLoopNodes->Node(thisCoolingPLHP->loadSideNodes.inlet).MassFlowRateMinAvail, flowTol); - EXPECT_NEAR(expectedLoadSideMassFlow, state->dataLoopNodes->Node(thisCoolingPLHP->loadSideNodes.inlet).MassFlowRateMax, flowTol); - EXPECT_NEAR(expectedLoadSideMassFlow, state->dataLoopNodes->Node(thisCoolingPLHP->loadSideNodes.inlet).MassFlowRateMaxAvail, flowTol); - EXPECT_NEAR(0.0, state->dataLoopNodes->Node(thisCoolingPLHP->sourceSideNodes.inlet).MassFlowRateMin, flowTol); - EXPECT_NEAR(0.0, state->dataLoopNodes->Node(thisCoolingPLHP->sourceSideNodes.inlet).MassFlowRateMinAvail, flowTol); - EXPECT_NEAR(expectedSourceSideMassFlow, state->dataLoopNodes->Node(thisCoolingPLHP->sourceSideNodes.inlet).MassFlowRateMax, flowTol); - EXPECT_NEAR(expectedSourceSideMassFlow, state->dataLoopNodes->Node(thisCoolingPLHP->sourceSideNodes.inlet).MassFlowRateMaxAvail, flowTol); -} - -TEST_F(EnergyPlusFixture, EIRPLHP_Initialization_SetpointMissing) -{ - std::string const idf_objects = delimited_string({ - "HeatPump:PlantLoop:EIR:Heating,", - " Heating Coil, !- Name", - " Heating Coil Load Loop Intermediate Node, !- Load Side Inlet Node Name", - " Heating Coil Load Loop Supply Side Heating Coil Outlet Node, !- Load Side Outlet Node Name", - " AirSource, !- Condenser Type", - " Outdoor Air Inlet Node, !- Source Side Inlet Node Name", - " Outdoor Air Outlet Node, !- Source Side Outlet Node Name", - " , !- Heat Recovery Inlet Node Name", - " , !- Heat Recovery Outlet Node Name", - " , !- Companion Heat Pump Name", - " 0.005, !- Load Side Reference Flow Rate {m3/s}", - " 2, !- Source Side Reference Flow Rate {m3/s}", - " autosize, !- Heat Recovery Reference Flow Rate {m3/s}", - " 80000, !- Reference Capacity {W}", - " 3.5, !- Reference Coefficient of Performance {W/W}", - " 1.0, !- Sizing Factor", - " CapCurveFuncTemp, !- Capacity Modifier Function of Temperature Curve Name", - " EIRCurveFuncTemp, !- Electric Input to Output Ratio Modifier Function of Temperature Curve Name", - " EIRCurveFuncPLR, !- Electric Input to Output Ratio Modifier Function of Part Load Ratio Curve Name", - " 1.0, !- Heating To Cooling Capacity Sizing Ratio", - " CoolingCapacity, !- Heat Pump Sizing Method", - " Setpoint, !- Control Type", - " ConstantFlow, !- Flow Mode", - " 0.0, !- Minimum Part Load Ratio", - " -100.0, !- Minimum Source Inlet Temperature {C}", - " 100.0, !- Maximum Source Inlet Temperature {C}", - " , !- Minimum Supply Water Temperature Curve Name", - " , !- Maximum Supply Water Temperature Curve Name", - " , !- Dry Outdoor Correction Factor Curve Name", - " 10.0, !- Maximum Outdoor Dry Bulb Temperature For Defrost Operation", - " , !- Heat Pump Defrost Control", - " 0.058333, !- Heat Pump Defrost Time Period Fraction", - " , !- Defrost Energy Input Ratio Function of Temperature Curve Name", - " , !- Timed Empirical Defrost Frequency Curve Name", - " , !- Timed Empirical Defrost Heat Load Penalty Curve Name", - " , !- Timed Empirical Defrost Heat Input Energy Fraction Curve Name", - " 4.5, !- Minimum Heat Recovery Outlet Temperature {C}", - " , !- Heat Recovery Capacity Modifier Function of Temperature Curve Name", - " ; !- Heat Recovery Electric Input to Output Ratio Modifier Function of Temperature Curve Name", - - "Curve:Biquadratic,", - " CapCurveFuncTemp, !- Name", - " 1.0, !- Coefficient1 Constant", - " 0.0, !- Coefficient2 x", - " 0.0, !- Coefficient3 x**2", - " 0.0, !- Coefficient4 y", - " 0.0, !- Coefficient5 y**2", - " 0.0, !- Coefficient6 x*y", - " 5.0, !- Minimum Value of x", - " 10.0, !- Maximum Value of x", - " 24.0, !- Minimum Value of y", - " 35.0, !- Maximum Value of y", - " , !- Minimum Curve Output", - " , !- Maximum Curve Output", - " Temperature, !- Input Unit Type for X", - " Temperature, !- Input Unit Type for Y", - " Dimensionless; !- Output Unit Type", - - "Curve:Biquadratic,", - " EIRCurveFuncTemp, !- Name", - " 1.0, !- Coefficient1 Constant", - " 0.0, !- Coefficient2 x", - " 0.0, !- Coefficient3 x**2", - " 0.0, !- Coefficient4 y", - " 0.0, !- Coefficient5 y**2", - " 0.0, !- Coefficient6 x*y", - " 5.0, !- Minimum Value of x", - " 10.0, !- Maximum Value of x", - " 24.0, !- Minimum Value of y", - " 35.0, !- Maximum Value of y", - " , !- Minimum Curve Output", - " , !- Maximum Curve Output", - " Temperature, !- Input Unit Type for X", - " Temperature, !- Input Unit Type for Y", - " Dimensionless; !- Output Unit Type", - - "Curve:Quadratic,", - " EIRCurveFuncPLR, !- Name", - " 1.0, !- Coefficient1 Constant", - " 0.0, !- Coefficient2 x", - " 0.0, !- Coefficient3 x**2", - " 0.0, !- Minimum Value of x", - " 1.0; !- Maximum Value of x", - }); - - ASSERT_TRUE(process_idf(idf_objects)); - state->init_state(*state); - - // call the factory with a valid name to trigger reading inputs - EIRPlantLoopHeatPump *thisHeatingPLHP = - static_cast(EIRPlantLoopHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpEIRHeating, "HEATING COIL")); - - // the factory would've called GetOnlySingleNode for the in/out pairs on the PLHP, add another one for the loop - // outlet setpoint node - EXPECT_EQ(4, state->dataLoopNodes->NumOfNodes); - EXPECT_EQ(4, state->dataLoopNodes->Node.size()); - state->dataLoopNodes->Node.redimension(5); - state->dataLoopNodes->NodeID.redimension(5); - int constexpr loadSidePlantOutletNodeIndex = 5; - state->dataLoopNodes->NodeID(loadSidePlantOutletNodeIndex) = "HEATING LOOP SUPPLY OUTLET NODE"; - - // set up the plant loops - // first the load side - state->dataPlnt->TotNumLoops = 1; - state->dataPlnt->PlantLoop.allocate(1); - - int constexpr loadSidePlantIndex = 1; - - auto &loadSideLoop = state->dataPlnt->PlantLoop(loadSidePlantIndex); - loadSideLoop.FluidName = "WATER"; - loadSideLoop.glycol = Fluid::GetWater(*state); - loadSideLoop.LoopSide(DataPlant::LoopSideLocation::Supply).TotalBranches = 1; - loadSideLoop.LoopSide(DataPlant::LoopSideLocation::Supply).Branch.allocate(1); - loadSideLoop.LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).TotalComponents = 1; - loadSideLoop.LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp.allocate(1); - loadSideLoop.TempSetPointNodeNum = loadSidePlantOutletNodeIndex; - - auto &loadSideLoopComp = loadSideLoop.LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp(1); - loadSideLoopComp.Name = thisHeatingPLHP->name; - loadSideLoopComp.NodeNumIn = thisHeatingPLHP->loadSideNodes.inlet; - loadSideLoopComp.Type = DataPlant::PlantEquipmentType::HeatPumpEIRHeating; - loadSideLoopComp.CurOpSchemeType = DataPlant::OpScheme::CompSetPtBased; - - // do a little setup here - thisHeatingPLHP->loadSidePlantLoc.loopNum = loadSidePlantIndex; - thisHeatingPLHP->loadSidePlantLoc.loopSideNum = DataPlant::LoopSideLocation::Supply; - thisHeatingPLHP->loadSidePlantLoc.branchNum = 1; - thisHeatingPLHP->loadSidePlantLoc.compNum = 1; - - // This sets the loop, side,branch and comp on the PlantLoc - PlantUtilities::SetPlantLocationLinks(*state, thisHeatingPLHP->loadSidePlantLoc); - - // Test SingleSetPoint operation first - loadSideLoop.LoopDemandCalcScheme = DataPlant::LoopDemandCalcScheme::SingleSetPoint; - state->dataLoopNodes->Node(loadSidePlantOutletNodeIndex).TempSetPoint = 30.0; - state->dataLoopNodes->Node(loadSidePlantOutletNodeIndex).TempSetPointHi = Node::SensedNodeFlagValue; - state->dataLoopNodes->Node(loadSidePlantOutletNodeIndex).TempSetPointLo = Node::SensedNodeFlagValue; - // This is already the case, but I'm being explicit - state->dataLoopNodes->Node(thisHeatingPLHP->loadSideNodes.outlet).TempSetPoint = Node::SensedNodeFlagValue; - state->dataLoopNodes->Node(thisHeatingPLHP->loadSideNodes.outlet).TempSetPointHi = Node::SensedNodeFlagValue; - state->dataLoopNodes->Node(thisHeatingPLHP->loadSideNodes.outlet).TempSetPointLo = Node::SensedNodeFlagValue; - - // the init call expects a "from" calling point - PlantLocation myLocation = PlantLocation(loadSidePlantIndex, DataPlant::LoopSideLocation::Supply, 1, 1); - - // call for initialization, oneTimeInit only first - state->dataGlobal->BeginEnvrnFlag = false; - thisHeatingPLHP->onInitLoopEquip(*state, myLocation); - - compare_err_stream(delimited_string({ - " ** Warning ** EIRPlantLoopHeatPump : oneTimeInit: Missing temperature setpoint for Setpoint Controlled HeatPump:PlantLoop:EIR:Heating " - "name = \"HEATING COIL\"", - " ** ~~~ ** A temperature setpoint is needed at the load side outlet node, use a SetpointManager", - " ** ~~~ ** The overall loop setpoint will be assumed for the Heat Pump. The simulation continues ... ", - })); - EXPECT_TRUE(thisHeatingPLHP->SetpointSetToLoopErrDone); - EXPECT_EQ(loadSidePlantOutletNodeIndex, thisHeatingPLHP->setPointNodeNum); - - EXPECT_NEAR(30.0, thisHeatingPLHP->getLoadSideOutletSetPointTemp(*state), 0.001); - EXPECT_NEAR(30.0, state->dataLoopNodes->Node(thisHeatingPLHP->setPointNodeNum).TempSetPoint, 0.001); - EXPECT_NEAR(Node::SensedNodeFlagValue, state->dataLoopNodes->Node(thisHeatingPLHP->setPointNodeNum).TempSetPointHi, 0.001); - EXPECT_NEAR(Node::SensedNodeFlagValue, state->dataLoopNodes->Node(thisHeatingPLHP->setPointNodeNum).TempSetPointLo, 0.001); - - // test for dual setpoint operation - loadSideLoop.LoopDemandCalcScheme = DataPlant::LoopDemandCalcScheme::DualSetPointDeadBand; - state->dataLoopNodes->Node(loadSidePlantOutletNodeIndex).TempSetPoint = Node::SensedNodeFlagValue; - state->dataLoopNodes->Node(loadSidePlantOutletNodeIndex).TempSetPointHi = 30.0; - state->dataLoopNodes->Node(loadSidePlantOutletNodeIndex).TempSetPointLo = 10.0; - state->dataLoopNodes->Node(thisHeatingPLHP->loadSideNodes.outlet).TempSetPoint = Node::SensedNodeFlagValue; - state->dataLoopNodes->Node(thisHeatingPLHP->loadSideNodes.outlet).TempSetPointHi = Node::SensedNodeFlagValue; - state->dataLoopNodes->Node(thisHeatingPLHP->loadSideNodes.outlet).TempSetPointLo = Node::SensedNodeFlagValue; - - // reset the flag to force re-running oneTimeInit - thisHeatingPLHP->oneTimeInitFlag = true; - thisHeatingPLHP->SetpointSetToLoopErrDone = false; - thisHeatingPLHP->onInitLoopEquip(*state, myLocation); - - compare_err_stream(delimited_string({ - " ** Warning ** EIRPlantLoopHeatPump : oneTimeInit: Missing temperature setpoint for Setpoint Controlled HeatPump:PlantLoop:EIR:Heating " - "name = \"HEATING COIL\"", - " ** ~~~ ** A temperature setpoint is needed at the load side outlet node, use a SetpointManager", - " ** ~~~ ** The overall loop setpoint will be assumed for the Heat Pump. The simulation continues ... ", - })); - EXPECT_TRUE(thisHeatingPLHP->SetpointSetToLoopErrDone); - EXPECT_EQ(loadSidePlantOutletNodeIndex, thisHeatingPLHP->setPointNodeNum); - - EXPECT_NEAR(10.0, thisHeatingPLHP->getLoadSideOutletSetPointTemp(*state), 0.001); - EXPECT_NEAR(Node::SensedNodeFlagValue, state->dataLoopNodes->Node(thisHeatingPLHP->setPointNodeNum).TempSetPoint, 0.001); - EXPECT_NEAR(30.0, state->dataLoopNodes->Node(thisHeatingPLHP->setPointNodeNum).TempSetPointHi, 0.001); - EXPECT_NEAR(10.0, state->dataLoopNodes->Node(thisHeatingPLHP->setPointNodeNum).TempSetPointLo, 0.001); - - // validate that location work got done correctly - EXPECT_EQ(1, thisHeatingPLHP->loadSidePlantLoc.loopNum); - EXPECT_ENUM_EQ(DataPlant::LoopSideLocation::Supply, thisHeatingPLHP->loadSidePlantLoc.loopSideNum); - EXPECT_EQ(1, thisHeatingPLHP->loadSidePlantLoc.branchNum); - EXPECT_EQ(1, thisHeatingPLHP->loadSidePlantLoc.compNum); - - // now call for initialization again, for begin environment - state->dataGlobal->BeginEnvrnFlag = true; - state->dataPlnt->PlantFirstSizesOkayToFinalize = true; - thisHeatingPLHP->onInitLoopEquip(*state, myLocation); -} - -TEST_F(EnergyPlusFixture, TestSizing_FullyAutosizedCoolingWithCompanion_WaterSource) -{ - std::string const idf_objects = delimited_string({"HeatPump:PlantLoop:EIR:Cooling,", - " hp cooling side,", - " node 1,", - " node 2,", - " WaterSource,", - " node 3,", - " node 4,", - " ,", - " ,", - " hp heating side,", - " Autosize,", - " Autosize,", - ",", - " Autosize,", - " 1.0,", - " 1,", - " dummyCurve,", - " dummyCurve,", - " dummyCurve;", - "HeatPump:PlantLoop:EIR:Heating,", - " hp heating side,", - " node 5,", - " node 6,", - " WaterSource,", - " node 7,", - " node 8,", - " ,", - " ,", - " hp cooling side,", - " Autosize,", - " Autosize,", - ",", - " Autosize,", - " 1.0,", - " 1,", - " dummyCurve,", - " dummyCurve,", - " dummyCurve;", - "Curve:Linear,", - " dummyCurve,", - " 1,", - " 0,", - " 1,", - " 1;"}); - ASSERT_TRUE(process_idf(idf_objects)); - state->init_state(*state); - - // call the factory with a valid name to trigger reading inputs - EIRPlantLoopHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpEIRCooling, "HP COOLING SIDE"); - - // verify the size of the vector and the processed condition - EXPECT_EQ(2u, state->dataEIRPlantLoopHeatPump->heatPumps.size()); - - // for now we know the order is maintained, so get each heat pump object - EIRPlantLoopHeatPump *thisCoolingPLHP = &state->dataEIRPlantLoopHeatPump->heatPumps[0]; - EIRPlantLoopHeatPump *thisHeatingPLHP = &state->dataEIRPlantLoopHeatPump->heatPumps[1]; - - // validate that we have the right ones - EXPECT_EQ("HP COOLING SIDE", thisCoolingPLHP->name); - EXPECT_EQ("HP HEATING SIDE", thisHeatingPLHP->name); - - // We'll set up two plant loops: a load and a source loop - state->dataPlnt->TotNumLoops = 2; - state->dataPlnt->PlantLoop.allocate(state->dataPlnt->TotNumLoops); - - state->dataPlnt->PlantLoop(1).FluidName = "WATER"; - state->dataPlnt->PlantLoop(1).glycol = Fluid::GetWater(*state); - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Demand).TotalBranches = 1; - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Demand).Branch.allocate(1); - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).TotalComponents = 1; - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp.allocate(1); - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).TotalBranches = 1; - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch.allocate(1); - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).TotalComponents = 2; - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp.allocate(2); - - state->dataPlnt->PlantLoop(2).FluidName = "WATER"; - state->dataPlnt->PlantLoop(2).glycol = Fluid::GetWater(*state); - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).TotalBranches = 1; - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch.allocate(1); - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).TotalComponents = 2; - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp.allocate(2); - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Supply).TotalBranches = 1; - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Supply).Branch.allocate(1); - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).TotalComponents = 1; - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp.allocate(1); - - Real64 constexpr plantSizingLoadVolFlow = 0.01; - Real64 constexpr plantSizingLoadDeltaT = 1.0; - - state->dataSize->PlantSizData.allocate(2); - state->dataSize->PlantSizData(1).DesVolFlowRate = 0.010; - state->dataSize->PlantSizData(1).DeltaT = 1.0; - state->dataSize->PlantSizData(2).DesVolFlowRate = 0.030; - state->dataSize->PlantSizData(2).DeltaT = 1.0; - - auto &loop1supplyComponent1 = state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp(1); - auto &loop2demandComponent1 = state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp(1); - auto &loop1supplyComponent2 = state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp(2); - auto &loop2demandComponent2 = state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp(2); - - loop1supplyComponent1.Type = DataPlant::PlantEquipmentType::HeatPumpEIRHeating; - loop2demandComponent1.Type = DataPlant::PlantEquipmentType::HeatPumpEIRHeating; - loop1supplyComponent2.Type = DataPlant::PlantEquipmentType::HeatPumpEIRCooling; - loop2demandComponent2.Type = DataPlant::PlantEquipmentType::HeatPumpEIRCooling; - - loop1supplyComponent1.Name = thisHeatingPLHP->name; - loop2demandComponent1.Name = thisHeatingPLHP->name; - loop1supplyComponent2.Name = thisCoolingPLHP->name; - loop2demandComponent2.Name = thisCoolingPLHP->name; - - loop1supplyComponent1.NodeNumIn = thisHeatingPLHP->loadSideNodes.inlet; - loop2demandComponent1.NodeNumIn = thisHeatingPLHP->sourceSideNodes.inlet; - loop1supplyComponent2.NodeNumIn = thisCoolingPLHP->loadSideNodes.inlet; - loop2demandComponent2.NodeNumIn = thisCoolingPLHP->sourceSideNodes.inlet; - - // the init call expects a "from" calling point - PlantLocation myCoolingLoadLocation = PlantLocation(1, DataPlant::LoopSideLocation::Supply, 1, 1); - PlantLocation myCoolingSourceLocation = PlantLocation(2, DataPlant::LoopSideLocation::Demand, 1, 1); - PlantLocation myHeatingLoadLocation = PlantLocation(1, DataPlant::LoopSideLocation::Supply, 1, 2); - - // set a couple global flags - state->dataGlobal->BeginEnvrnFlag = true; - - // initialize so the components can find themselves on the plant - thisCoolingPLHP->onInitLoopEquip(*state, myCoolingLoadLocation); - thisHeatingPLHP->onInitLoopEquip(*state, myHeatingLoadLocation); - - state->dataPlnt->PlantFinalSizesOkayToReport = true; - state->dataPlnt->PlantFirstSizesOkayToReport = true; - state->dataPlnt->PlantFirstSizesOkayToFinalize = true; - - // assign the plant sizing data - state->dataPlnt->PlantLoop(1).PlantSizNum = 1; - state->dataPlnt->PlantLoop(2).PlantSizNum = 2; - - // The load side should be what is imposed by the plant sizing data for the design flow rate. - // The source side should be calculated based on the COP, which was set at 1.0 for convenience. - // This works out to a multiplier of 2 on the source flow rate. With the same deltaT on the design of the source - // loop, the flow rate must be twice as high. - // The source side has a slightly different set of thermal properties so the expected flow is scaled by those. - Real64 constexpr expectedLoadCp = 4197.93; - Real64 constexpr expectedLoadRho = 999.898; - Real64 constexpr expectedSourceCp = 4185.0; - Real64 constexpr expectedSourceRho = 983.2; - Real64 const expectedLoadFlow = plantSizingLoadVolFlow; - Real64 expectedCapacity = expectedLoadRho * expectedLoadFlow * expectedLoadCp * plantSizingLoadDeltaT; - Real64 expectedSourceFlow = plantSizingLoadVolFlow * 2.0; - thisCoolingPLHP->sizeLoadSide(*state); - thisCoolingPLHP->sizeSrcSideWSHP(*state); - EXPECT_NEAR(expectedLoadFlow, thisCoolingPLHP->loadSideDesignVolFlowRate, 0.0001); - EXPECT_NEAR(expectedSourceFlow, thisCoolingPLHP->sourceSideDesignVolFlowRate, 0.0001); - EXPECT_NEAR(expectedCapacity, thisCoolingPLHP->referenceCapacity, 0.0001); - - // with a sizing run complete, we can also go ahead and get the design capacities... - // they should be nonzero for the load side of things - Real64 tmpMin = -1.0, tmpMax = -1.0, tmpOpt = -1.0; - thisCoolingPLHP->getDesignCapacities(*state, myCoolingLoadLocation, tmpMax, tmpMin, tmpOpt); - EXPECT_NEAR(0.0, tmpMin, 0.001); - EXPECT_NEAR(expectedCapacity, tmpMax, 0.001); - EXPECT_NEAR(expectedCapacity, tmpOpt, 0.001); - // but always zero for the source side of things - tmpMin = -1.0, tmpMax = -1.0, tmpOpt = -1.0; - thisCoolingPLHP->getDesignCapacities(*state, myCoolingSourceLocation, tmpMax, tmpMin, tmpOpt); - EXPECT_NEAR(0.0, tmpMin, 0.001); - EXPECT_NEAR(0.0, tmpMax, 0.001); - EXPECT_NEAR(0.0, tmpOpt, 0.001); - - // we can reset things and do a few more corner cases here - - // lets just try it again but with the plant sizing data set to zero flow, it should try to use the companion - // but the companion isn't sized yet, so it should get zero conditions - thisCoolingPLHP->loadSideDesignVolFlowRate = DataSizing::AutoSize; - thisCoolingPLHP->sourceSideDesignVolFlowRate = DataSizing::AutoSize; - thisCoolingPLHP->referenceCapacity = DataSizing::AutoSize; - state->dataSize->PlantSizData(1).DesVolFlowRate = 0.0; - thisCoolingPLHP->sizeLoadSide(*state); - thisCoolingPLHP->sizeSrcSideWSHP(*state); - EXPECT_NEAR(0.0, thisCoolingPLHP->loadSideDesignVolFlowRate, 0.0001); - EXPECT_NEAR(0.0, thisCoolingPLHP->sourceSideDesignVolFlowRate, 0.0001); - EXPECT_NEAR(0.0, thisCoolingPLHP->referenceCapacity, 0.0001); - - // but now let's try to size the heating coil, which will try to use the cooling coil's sized data - thisCoolingPLHP->loadSideDesignVolFlowRate = expectedLoadFlow; - thisCoolingPLHP->sourceSideDesignVolFlowRate = expectedSourceFlow; - thisCoolingPLHP->referenceCapacity = expectedCapacity; - Real64 const designSourceSideHeatTransfer = expectedCapacity * (1 - 1 / thisHeatingPLHP->referenceCOP); - expectedSourceFlow = designSourceSideHeatTransfer / (state->dataSize->PlantSizData(2).DeltaT * expectedSourceCp * expectedSourceRho); - expectedCapacity = expectedLoadRho * expectedLoadFlow * expectedLoadCp * plantSizingLoadDeltaT; - thisHeatingPLHP->sizeLoadSide(*state); - thisHeatingPLHP->sizeSrcSideWSHP(*state); - EXPECT_NEAR(expectedLoadFlow, thisHeatingPLHP->loadSideDesignVolFlowRate, 0.0001); - EXPECT_NEAR(expectedSourceFlow, thisHeatingPLHP->sourceSideDesignVolFlowRate, 0.0001); - EXPECT_NEAR(expectedCapacity, thisHeatingPLHP->referenceCapacity, 0.0001); -} - -TEST_F(EnergyPlusFixture, TestSizing_FullyHardsizedHeatingWithCompanion) -{ - std::string const idf_objects = delimited_string({"HeatPump:PlantLoop:EIR:Cooling,", - " hp cooling side,", - " node 1,", - " node 2,", - " WaterSource,", - " node 3,", - " node 4,", - " ,", - " ,", - " hp heating side,", - " 0.01,", - " 0.02,", - " ,", - " 1200,", - " 1.0,", - " 1,", - " dummyCurve,", - " dummyCurve,", - " dummyCurve;", - "HeatPump:PlantLoop:EIR:Heating,", - " hp heating side,", - " node 5,", - " node 6,", - " WaterSource,", - " node 7,", - " node 8,", - " ,", - " ,", - " hp cooling side,", - " 0.01,", - " 0.02,", - " ,", - " 1200,", - " 1.0,", - " 1,", - " dummyCurve,", - " dummyCurve,", - " dummyCurve;", - "Curve:Linear,", - " dummyCurve,", - " 1,", - " 0,", - " 1,", - " 1;"}); - ASSERT_TRUE(process_idf(idf_objects)); - state->init_state(*state); - - // call the factory with a valid name to trigger reading inputs - EIRPlantLoopHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpEIRCooling, "HP COOLING SIDE"); - - // verify the size of the vector and the processed condition - EXPECT_EQ(2u, state->dataEIRPlantLoopHeatPump->heatPumps.size()); - - // for now we know the order is maintained, so get each heat pump object - EIRPlantLoopHeatPump *thisCoolingPLHP = &state->dataEIRPlantLoopHeatPump->heatPumps[0]; - EIRPlantLoopHeatPump *thisHeatingPLHP = &state->dataEIRPlantLoopHeatPump->heatPumps[1]; - - // validate that we have the right ones - EXPECT_EQ("HP COOLING SIDE", thisCoolingPLHP->name); - EXPECT_EQ("HP HEATING SIDE", thisHeatingPLHP->name); - - // We'll set up two plant loops: a load and a source loop - state->dataPlnt->TotNumLoops = 2; - state->dataPlnt->PlantLoop.allocate(state->dataPlnt->TotNumLoops); - - state->dataPlnt->PlantLoop(1).FluidName = "WATER"; - state->dataPlnt->PlantLoop(1).glycol = Fluid::GetWater(*state); - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Demand).TotalBranches = 1; - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Demand).Branch.allocate(1); - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).TotalComponents = 1; - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp.allocate(1); - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).TotalBranches = 1; - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch.allocate(1); - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).TotalComponents = 2; - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp.allocate(2); - state->dataPlnt->PlantLoop(1).PlantSizNum = 1; - - state->dataPlnt->PlantLoop(2).FluidName = "WATER"; - state->dataPlnt->PlantLoop(2).glycol = Fluid::GetWater(*state); - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).TotalBranches = 1; - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch.allocate(1); - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).TotalComponents = 2; - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp.allocate(2); - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Supply).TotalBranches = 1; - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Supply).Branch.allocate(1); - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).TotalComponents = 1; - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp.allocate(1); - state->dataPlnt->PlantLoop(2).PlantSizNum = 2; - - state->dataSize->PlantSizData.allocate(2); - state->dataSize->PlantSizData(1).DesVolFlowRate = 0.020; - state->dataSize->PlantSizData(1).DeltaT = 1.0; - state->dataSize->PlantSizData(2).DesVolFlowRate = 0.030; - state->dataSize->PlantSizData(2).DeltaT = 1.0; - - auto &loop1supplyComponent1 = state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp(1); - auto &loop2demandComponent1 = state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp(1); - auto &loop1supplyComponent2 = state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp(2); - auto &loop2demandComponent2 = state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp(2); - - loop1supplyComponent1.Type = DataPlant::PlantEquipmentType::HeatPumpEIRHeating; - loop2demandComponent1.Type = DataPlant::PlantEquipmentType::HeatPumpEIRHeating; - loop1supplyComponent2.Type = DataPlant::PlantEquipmentType::HeatPumpEIRCooling; - loop2demandComponent2.Type = DataPlant::PlantEquipmentType::HeatPumpEIRCooling; - - loop1supplyComponent1.Name = thisHeatingPLHP->name; - loop2demandComponent1.Name = thisHeatingPLHP->name; - loop1supplyComponent2.Name = thisCoolingPLHP->name; - loop2demandComponent2.Name = thisCoolingPLHP->name; - - loop1supplyComponent1.NodeNumIn = thisHeatingPLHP->loadSideNodes.inlet; - loop2demandComponent1.NodeNumIn = thisHeatingPLHP->sourceSideNodes.inlet; - loop1supplyComponent2.NodeNumIn = thisCoolingPLHP->loadSideNodes.inlet; - loop2demandComponent2.NodeNumIn = thisCoolingPLHP->sourceSideNodes.inlet; - - // the init call expects a "from" calling point - PlantLocation myLoadLocation = PlantLocation(1, DataPlant::LoopSideLocation::Supply, 1, 1); - - // set a couple global flags - state->dataGlobal->BeginEnvrnFlag = true; - state->dataGlobal->DisplayExtraWarnings = true; - - // initialize so the components can find themselves on the plant - thisHeatingPLHP->onInitLoopEquip(*state, myLoadLocation); - - state->dataPlnt->PlantFinalSizesOkayToReport = true; - state->dataPlnt->PlantFirstSizesOkayToReport = true; - state->dataPlnt->PlantFirstSizesOkayToFinalize = true; - - // The values really should just come out all as the hard-sized values, this just makes sure that function didn't - // botch something up. - thisHeatingPLHP->sizeLoadSide(*state); - thisHeatingPLHP->sizeSrcSideWSHP(*state); - EXPECT_NEAR(0.01, thisHeatingPLHP->loadSideDesignVolFlowRate, 0.0001); - EXPECT_NEAR(0.02, thisHeatingPLHP->sourceSideDesignVolFlowRate, 0.0001); - EXPECT_NEAR(1200, thisHeatingPLHP->referenceCapacity, 0.0001); - - // Call it again, but this time with PlantSizing on, it should come out the same again - state->dataGlobal->DoPlantSizing = true; - thisHeatingPLHP->sizeLoadSide(*state); - thisHeatingPLHP->sizeSrcSideWSHP(*state); - EXPECT_NEAR(0.01, thisHeatingPLHP->loadSideDesignVolFlowRate, 0.0001); - EXPECT_NEAR(0.02, thisHeatingPLHP->sourceSideDesignVolFlowRate, 0.0001); - EXPECT_NEAR(1200, thisHeatingPLHP->referenceCapacity, 0.0001); -} - -TEST_F(EnergyPlusFixture, TestSizing_WithCompanionNoPlantSizing) -{ - std::string const idf_objects = delimited_string({"HeatPump:PlantLoop:EIR:Cooling,", - " hp cooling side,", - " node 1,", - " node 2,", - " WaterSource,", - " node 3,", - " node 4,", - " ,", - " ,", - " hp heating side,", - " Autosize,", - " Autosize,", - " ,", - " Autosize,", - " 1.0,", - " 1,", - " dummyCurve,", - " dummyCurve,", - " dummyCurve;", - "HeatPump:PlantLoop:EIR:Heating,", - " hp heating side,", - " node 5,", - " node 6,", - " WaterSource,", - " node 7,", - " node 8,", - " ,", - " ,", - " hp cooling side,", - " Autosize,", - " Autosize,", - " ,", - " Autosize,", - " 1.0,", - " 1,", - " dummyCurve,", - " dummyCurve,", - " dummyCurve;", - "Curve:Linear,", - " dummyCurve,", - " 1,", - " 0,", - " 1,", - " 1;"}); - ASSERT_TRUE(process_idf(idf_objects)); - state->init_state(*state); - - // call the factory with a valid name to trigger reading inputs - EIRPlantLoopHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpEIRCooling, "HP COOLING SIDE"); - - // verify the size of the vector and the processed condition - EXPECT_EQ(2u, state->dataEIRPlantLoopHeatPump->heatPumps.size()); - - // for now we know the order is maintained, so get each heat pump object - EIRPlantLoopHeatPump *thisCoolingPLHP = &state->dataEIRPlantLoopHeatPump->heatPumps[0]; - EIRPlantLoopHeatPump *thisHeatingPLHP = &state->dataEIRPlantLoopHeatPump->heatPumps[1]; - - // validate that we have the right ones - EXPECT_EQ("HP COOLING SIDE", thisCoolingPLHP->name); - EXPECT_EQ("HP HEATING SIDE", thisHeatingPLHP->name); - - // We'll set up two plant loops: a load and a source loop - state->dataPlnt->TotNumLoops = 2; - state->dataPlnt->PlantLoop.allocate(state->dataPlnt->TotNumLoops); - - state->dataPlnt->PlantLoop(1).FluidName = "WATER"; - state->dataPlnt->PlantLoop(1).glycol = Fluid::GetWater(*state); - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Demand).TotalBranches = 1; - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Demand).Branch.allocate(1); - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).TotalComponents = 1; - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp.allocate(1); - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).TotalBranches = 1; - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch.allocate(1); - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).TotalComponents = 2; - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp.allocate(2); - - state->dataPlnt->PlantLoop(2).FluidName = "WATER"; - state->dataPlnt->PlantLoop(2).glycol = Fluid::GetWater(*state); - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).TotalBranches = 1; - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch.allocate(1); - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).TotalComponents = 2; - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp.allocate(2); - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Supply).TotalBranches = 1; - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Supply).Branch.allocate(1); - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).TotalComponents = 1; - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp.allocate(1); - - auto &loop1supplyComponent1 = state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp(1); - auto &loop2demandComponent1 = state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp(1); - auto &loop1supplyComponent2 = state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp(2); - auto &loop2demandComponent2 = state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp(2); - - loop1supplyComponent1.Type = DataPlant::PlantEquipmentType::HeatPumpEIRHeating; - loop2demandComponent1.Type = DataPlant::PlantEquipmentType::HeatPumpEIRHeating; - loop1supplyComponent2.Type = DataPlant::PlantEquipmentType::HeatPumpEIRCooling; - loop2demandComponent2.Type = DataPlant::PlantEquipmentType::HeatPumpEIRCooling; - - loop1supplyComponent1.Name = thisHeatingPLHP->name; - loop2demandComponent1.Name = thisHeatingPLHP->name; - loop1supplyComponent2.Name = thisCoolingPLHP->name; - loop2demandComponent2.Name = thisCoolingPLHP->name; - - loop1supplyComponent1.NodeNumIn = thisHeatingPLHP->loadSideNodes.inlet; - loop2demandComponent1.NodeNumIn = thisHeatingPLHP->sourceSideNodes.inlet; - loop1supplyComponent2.NodeNumIn = thisCoolingPLHP->loadSideNodes.inlet; - loop2demandComponent2.NodeNumIn = thisCoolingPLHP->sourceSideNodes.inlet; - - // the init call expects a "from" calling point - PlantLocation myCoolingLoadLocation = PlantLocation(1, DataPlant::LoopSideLocation::Supply, 1, 1); - PlantLocation myHeatingLoadLocation = PlantLocation(1, DataPlant::LoopSideLocation::Supply, 1, 2); - - // set a couple global flags - state->dataGlobal->BeginEnvrnFlag = true; - - // initialize so the components can find themselves on the plant - thisCoolingPLHP->onInitLoopEquip(*state, myCoolingLoadLocation); - thisHeatingPLHP->onInitLoopEquip(*state, myHeatingLoadLocation); - - state->dataPlnt->PlantFinalSizesOkayToReport = true; - state->dataPlnt->PlantFirstSizesOkayToReport = true; - state->dataPlnt->PlantFirstSizesOkayToFinalize = true; - - // let's just fake that the companion coil already got autosized properly - thisHeatingPLHP->loadSideDesignVolFlowRate = 0.1; - thisHeatingPLHP->sourceSideDesignVolFlowRate = 0.2; - thisHeatingPLHP->referenceCapacity = 1000.0; - - // With no plant sizing, it will try to use the autosized companion instead - // the load flow should be the companion load flow - // with no source plant sizing, the source flow will actually work out to be the same as the load flow (not the source flow) - // the capacity will be the companion capacity - thisCoolingPLHP->sizeLoadSide(*state); - thisCoolingPLHP->sizeSrcSideWSHP(*state); - EXPECT_NEAR(0.1, thisCoolingPLHP->loadSideDesignVolFlowRate, 0.0001); - EXPECT_NEAR(0.1, thisCoolingPLHP->sourceSideDesignVolFlowRate, 0.0001); - EXPECT_NEAR(1000.0, thisCoolingPLHP->referenceCapacity, 0.0001); -} - -TEST_F(EnergyPlusFixture, TestSizing_NoCompanionNoPlantSizingError) -{ - std::string const idf_objects = delimited_string({"HeatPump:PlantLoop:EIR:Heating,", - " hp heating side,", - " node 5,", - " node 6,", - " WaterSource,", - " node 7,", - " node 8,", - " ,", - " ,", - " ,", - " Autosize,", - " Autosize,", - " ,", - " Autosize,", - " 1.0,", - " 1,", - " dummyCurve,", - " dummyCurve,", - " dummyCurve;", - "Curve:Linear,", - " dummyCurve,", - " 1,", - " 0,", - " 1,", - " 1;"}); - ASSERT_TRUE(process_idf(idf_objects)); - state->init_state(*state); - - // call the factory with a valid name to trigger reading inputs - EIRPlantLoopHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpEIRHeating, "HP HEATING SIDE"); - - // verify the size of the vector and the processed condition - EXPECT_EQ(1u, state->dataEIRPlantLoopHeatPump->heatPumps.size()); - - // for now we know the order is maintained, so get each heat pump object - EIRPlantLoopHeatPump *thisHeatingPLHP = &state->dataEIRPlantLoopHeatPump->heatPumps[0]; - - // validate that we have the right ones - EXPECT_EQ("HP HEATING SIDE", thisHeatingPLHP->name); - - // We'll set up two plant loops: a load and a source loop - state->dataPlnt->TotNumLoops = 2; - state->dataPlnt->PlantLoop.allocate(state->dataPlnt->TotNumLoops); - - state->dataPlnt->PlantLoop(1).FluidName = "WATER"; - state->dataPlnt->PlantLoop(1).glycol = Fluid::GetWater(*state); - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Demand).TotalBranches = 1; - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Demand).Branch.allocate(1); - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).TotalComponents = 1; - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp.allocate(1); - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).TotalBranches = 1; - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch.allocate(1); - 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); - - state->dataPlnt->PlantLoop(2).FluidName = "WATER"; - state->dataPlnt->PlantLoop(2).glycol = Fluid::GetWater(*state); - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).TotalBranches = 1; - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch.allocate(1); - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).TotalComponents = 1; - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp.allocate(1); - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Supply).TotalBranches = 1; - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Supply).Branch.allocate(1); - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).TotalComponents = 1; - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp.allocate(1); - - auto &loop1supplyComponent1 = state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp(1); - auto &loop2demandComponent1 = state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp(1); - - loop1supplyComponent1.Type = DataPlant::PlantEquipmentType::HeatPumpEIRHeating; - loop2demandComponent1.Type = DataPlant::PlantEquipmentType::HeatPumpEIRHeating; - - loop1supplyComponent1.Name = thisHeatingPLHP->name; - loop2demandComponent1.Name = thisHeatingPLHP->name; - - loop1supplyComponent1.NodeNumIn = thisHeatingPLHP->loadSideNodes.inlet; - loop2demandComponent1.NodeNumIn = thisHeatingPLHP->sourceSideNodes.inlet; - - // the init call expects a "from" calling point - PlantLocation myHeatingLoadLocation = PlantLocation(1, DataPlant::LoopSideLocation::Supply, 1, 1); - - // set a couple global flags - state->dataGlobal->BeginEnvrnFlag = true; - - // initialize so the components can find themselves on the plant - thisHeatingPLHP->onInitLoopEquip(*state, myHeatingLoadLocation); - - state->dataPlnt->PlantFinalSizesOkayToReport = true; - state->dataPlnt->PlantFirstSizesOkayToReport = true; - state->dataPlnt->PlantFirstSizesOkayToFinalize = true; - - // with no plant sizing available and no companion coil to size from, it should throw a fatal - EXPECT_THROW(thisHeatingPLHP->sizeLoadSide(*state), std::runtime_error); -} - -TEST_F(EnergyPlusFixture, TestSizing_NoCompanionNoPlantSizingHardSized) -{ - std::string const idf_objects = delimited_string({"HeatPump:PlantLoop:EIR:Heating,", - " hp heating side,", - " node 5,", - " node 6,", - " WaterSource,", - " node 7,", - " node 8,", - " ,", - " ,", - " ,", - " 0.1,", - " 0.1,", - " ,", - " 1000,", - " 1.0,", - " 1,", - " dummyCurve,", - " dummyCurve,", - " dummyCurve;", - "Curve:Linear,", - " dummyCurve,", - " 1,", - " 0,", - " 1,", - " 1;"}); - ASSERT_TRUE(process_idf(idf_objects)); - state->init_state(*state); - - // call the factory with a valid name to trigger reading inputs - EIRPlantLoopHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpEIRHeating, "HP HEATING SIDE"); - - // verify the size of the vector and the processed condition - EXPECT_EQ(1u, state->dataEIRPlantLoopHeatPump->heatPumps.size()); - - // for now we know the order is maintained, so get each heat pump object - EIRPlantLoopHeatPump *thisHeatingPLHP = &state->dataEIRPlantLoopHeatPump->heatPumps[0]; - - // validate that we have the right ones - EXPECT_EQ("HP HEATING SIDE", thisHeatingPLHP->name); - - // We'll set up two plant loops: a load and a source loop - state->dataPlnt->TotNumLoops = 2; - state->dataPlnt->PlantLoop.allocate(state->dataPlnt->TotNumLoops); - - state->dataPlnt->PlantLoop(1).FluidName = "WATER"; - state->dataPlnt->PlantLoop(1).glycol = Fluid::GetWater(*state); - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Demand).TotalBranches = 1; - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Demand).Branch.allocate(1); - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).TotalComponents = 1; - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp.allocate(1); - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).TotalBranches = 1; - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch.allocate(1); - 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); - - state->dataPlnt->PlantLoop(2).FluidName = "WATER"; - state->dataPlnt->PlantLoop(2).glycol = Fluid::GetWater(*state); - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).TotalBranches = 1; - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch.allocate(1); - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).TotalComponents = 1; - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp.allocate(1); - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Supply).TotalBranches = 1; - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Supply).Branch.allocate(1); - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).TotalComponents = 1; - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp.allocate(1); - - auto &loop1supplyComponent1 = state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp(1); - auto &loop2demandComponent1 = state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp(1); - - loop1supplyComponent1.Type = DataPlant::PlantEquipmentType::HeatPumpEIRHeating; - loop2demandComponent1.Type = DataPlant::PlantEquipmentType::HeatPumpEIRHeating; - - loop1supplyComponent1.Name = thisHeatingPLHP->name; - loop2demandComponent1.Name = thisHeatingPLHP->name; - - loop1supplyComponent1.NodeNumIn = thisHeatingPLHP->loadSideNodes.inlet; - loop2demandComponent1.NodeNumIn = thisHeatingPLHP->sourceSideNodes.inlet; - - // the init call expects a "from" calling point - PlantLocation myHeatingLoadLocation = PlantLocation(1, DataPlant::LoopSideLocation::Supply, 1, 1); - - // set a couple global flags - state->dataGlobal->BeginEnvrnFlag = true; - - // initialize so the components can find themselves on the plant - thisHeatingPLHP->onInitLoopEquip(*state, myHeatingLoadLocation); - - state->dataPlnt->PlantFinalSizesOkayToReport = true; - state->dataPlnt->PlantFirstSizesOkayToReport = true; - state->dataPlnt->PlantFirstSizesOkayToFinalize = true; - - // this should report out to the sizing output, but just the user defined stuff - thisHeatingPLHP->sizeLoadSide(*state); - thisHeatingPLHP->sizeSrcSideWSHP(*state); - EXPECT_NEAR(0.1, thisHeatingPLHP->loadSideDesignVolFlowRate, 0.0001); - EXPECT_NEAR(0.1, thisHeatingPLHP->sourceSideDesignVolFlowRate, 0.0001); - EXPECT_NEAR(1000, thisHeatingPLHP->referenceCapacity, 0.0001); -} - -TEST_F(EnergyPlusFixture, CoolingOutletSetpointWorker) -{ - std::string const idf_objects = delimited_string({"HeatPump:PlantLoop:EIR:Cooling,", - " hp cooling side,", - " node 1,", - " node 2,", - " WaterSource,", - " node 3,", - " node 4,", - " ,", - " ,", - " ,", - " 0.001,", - " 0.001,", - " ,", - " 1000,", - " 3.14,", - " ,", - " dummyCurve,", - " dummyCurve,", - " dummyCurve;", - "Curve:Linear,", - " dummyCurve,", - " 1,", - " 0,", - " 1,", - " 1;"}); - ASSERT_TRUE(process_idf(idf_objects)); - state->init_state(*state); - - // set up the plant loops - // first the load side - state->dataPlnt->TotNumLoops = 1; - state->dataPlnt->PlantLoop.allocate(1); - auto &PLHPPlantLoadSideLoop = state->dataPlnt->PlantLoop(1); - - state->dataPlnt->PlantLoop(1).FluidName = "WATER"; - state->dataPlnt->PlantLoop(1).glycol = Fluid::GetWater(*state); - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).TotalBranches = 1; - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch.allocate(1); - 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 &PLHPPlantLoadSideComp = state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp(1); - PLHPPlantLoadSideComp.Type = DataPlant::PlantEquipmentType::HeatPumpEIRCooling; - - // call the factory with a valid name to trigger reading inputs - EIRPlantLoopHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpEIRCooling, "HP COOLING SIDE"); - - // verify the size of the vector and the processed condition - EXPECT_EQ(1u, state->dataEIRPlantLoopHeatPump->heatPumps.size()); - - // for now we know the order is maintained, so get each heat pump object - EIRPlantLoopHeatPump *thisCoolingPLHP = &state->dataEIRPlantLoopHeatPump->heatPumps[0]; - - // do a little setup here - thisCoolingPLHP->loadSidePlantLoc.loopNum = 1; - thisCoolingPLHP->loadSidePlantLoc.loopSideNum = DataPlant::LoopSideLocation::Supply; - thisCoolingPLHP->loadSidePlantLoc.branchNum = 1; - thisCoolingPLHP->loadSidePlantLoc.compNum = 1; - PlantUtilities::SetPlantLocationLinks(*state, thisCoolingPLHP->loadSidePlantLoc); - thisCoolingPLHP->loadSideNodes.outlet = 1; - - // the factory would've called GetOnlySingleNode for the in/out pairs on the PLHP, add another one for the loop - // outlet setpoint node - state->dataLoopNodes->Node.allocate(5); - PLHPPlantLoadSideLoop.TempSetPointNodeNum = 5; - - // set up the plant setpoint conditions and test for single setpoint operation - PLHPPlantLoadSideLoop.LoopDemandCalcScheme = DataPlant::LoopDemandCalcScheme::SingleSetPoint; - PLHPPlantLoadSideComp.CurOpSchemeType = DataPlant::OpScheme::CompSetPtBased; - // Normally done by oneTimeInit - thisCoolingPLHP->setPointNodeNum = thisCoolingPLHP->loadSideNodes.outlet; - state->dataLoopNodes->Node(thisCoolingPLHP->setPointNodeNum).TempSetPoint = 3.141; - state->dataLoopNodes->Node(5).TempSetPoint = 2.718; - EXPECT_NEAR(3.141, thisCoolingPLHP->getLoadSideOutletSetPointTemp(*state), 0.001); - PLHPPlantLoadSideComp.CurOpSchemeType = DataPlant::OpScheme::CoolingRB; - EXPECT_NEAR(2.718, thisCoolingPLHP->getLoadSideOutletSetPointTemp(*state), 0.001); - - // test for dual setpoint operation - PLHPPlantLoadSideLoop.LoopDemandCalcScheme = DataPlant::LoopDemandCalcScheme::DualSetPointDeadBand; - PLHPPlantLoadSideComp.CurOpSchemeType = DataPlant::OpScheme::CompSetPtBased; - state->dataLoopNodes->Node(thisCoolingPLHP->loadSideNodes.outlet).TempSetPointHi = 6.282; - state->dataLoopNodes->Node(5).TempSetPointHi = 5.436; - EXPECT_NEAR(6.282, thisCoolingPLHP->getLoadSideOutletSetPointTemp(*state), 0.001); - PLHPPlantLoadSideComp.CurOpSchemeType = DataPlant::OpScheme::CoolingRB; - EXPECT_NEAR(5.436, thisCoolingPLHP->getLoadSideOutletSetPointTemp(*state), 0.001); -} - -TEST_F(EnergyPlusFixture, HeatingOutletSetpointWorker) -{ - std::string const idf_objects = delimited_string({"HeatPump:PlantLoop:EIR:Heating,", - " hp heating side,", - " node 1,", - " node 2,", - " WaterSource,", - " node 3,", - " node 3,", - " ,", - " ,", - " ,", - " 0.001,", - " 0.001,", - " ,", - " 1000,", - " 3.14,", - " ,", - " dummyCurve,", - " dummyCurve,", - " dummyCurve;", - "Curve:Linear,", - " dummyCurve,", - " 1,", - " 0,", - " 1,", - " 1;"}); - ASSERT_TRUE(process_idf(idf_objects)); - state->init_state(*state); - - // set up the plant loops - // first the load side - state->dataPlnt->TotNumLoops = 1; - state->dataPlnt->PlantLoop.allocate(1); - auto &PLHPPlantLoadSideLoop = state->dataPlnt->PlantLoop(1); - - state->dataPlnt->PlantLoop(1).FluidName = "WATER"; - state->dataPlnt->PlantLoop(1).glycol = Fluid::GetWater(*state); - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).TotalBranches = 1; - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch.allocate(1); - 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 &PLHPPlantLoadSideComp = state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp(1); - PLHPPlantLoadSideComp.Type = DataPlant::PlantEquipmentType::HeatPumpEIRHeating; - - // call the factory with a valid name to trigger reading inputs - EIRPlantLoopHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpEIRHeating, "HP HEATING SIDE"); - - // for now we know the order is maintained, so get each heat pump object - EIRPlantLoopHeatPump *thisHeatingPLHP = &state->dataEIRPlantLoopHeatPump->heatPumps[0]; - - // do a little setup here - thisHeatingPLHP->loadSidePlantLoc.loopNum = 1; - thisHeatingPLHP->loadSidePlantLoc.loopSideNum = DataPlant::LoopSideLocation::Supply; - thisHeatingPLHP->loadSidePlantLoc.branchNum = 1; - thisHeatingPLHP->loadSidePlantLoc.compNum = 1; - PlantUtilities::SetPlantLocationLinks(*state, thisHeatingPLHP->loadSidePlantLoc); - - thisHeatingPLHP->loadSideNodes.outlet = 1; - - // the factory would've called GetOnlySingleNode for the in/out pairs on the PLHP, add another one for the loop - // outlet setpoint node - state->dataLoopNodes->Node.allocate(5); - PLHPPlantLoadSideLoop.TempSetPointNodeNum = 5; - - // test for dual setpoint operation - PLHPPlantLoadSideLoop.LoopDemandCalcScheme = DataPlant::LoopDemandCalcScheme::DualSetPointDeadBand; - PLHPPlantLoadSideComp.CurOpSchemeType = DataPlant::OpScheme::CompSetPtBased; - // Normally done by oneTimeInit - thisHeatingPLHP->setPointNodeNum = thisHeatingPLHP->loadSideNodes.outlet; - state->dataLoopNodes->Node(thisHeatingPLHP->setPointNodeNum).TempSetPointHi = 30.0; - state->dataLoopNodes->Node(thisHeatingPLHP->setPointNodeNum).TempSetPointLo = 10.0; - state->dataLoopNodes->Node(5).TempSetPointHi = 30.0; - state->dataLoopNodes->Node(5).TempSetPointLo = 12.0; - EXPECT_NEAR(10.0, thisHeatingPLHP->getLoadSideOutletSetPointTemp(*state), 0.001); - PLHPPlantLoadSideComp.CurOpSchemeType = DataPlant::OpScheme::HeatingRB; - EXPECT_NEAR(12.0, thisHeatingPLHP->getLoadSideOutletSetPointTemp(*state), 0.001); -} - -TEST_F(EnergyPlusFixture, Initialization2_WaterSource) -{ - std::string const idf_objects = delimited_string({"HeatPump:PlantLoop:EIR:Cooling,", - " hp cooling side,", - " node 1,", - " node 2,", - " WaterSource,", - " node 3,", - " node 4,", - " ,", - " ,", - " ,", - " 0.001,", - " 0.001,", - " ,", - " 1000,", - " 3.14,", - " ,", - " dummyCurve,", - " dummyCurve,", - " dummyCurve;", - "Curve:Linear,", - " dummyCurve,", - " 1,", - " 0,", - " 1,", - " 1;"}); - ASSERT_TRUE(process_idf(idf_objects)); - state->init_state(*state); - - bool firstHVACIteration = true; - // set up the plant loops - // first the load side - state->dataPlnt->TotNumLoops = 2; - state->dataPlnt->PlantLoop.allocate(2); - - state->dataPlnt->PlantLoop(1).FluidName = "WATER"; - state->dataPlnt->PlantLoop(1).glycol = Fluid::GetWater(*state); - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).TotalBranches = 1; - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch.allocate(1); - 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 &PLHPPlantLoadSideComp = state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp(1); - PLHPPlantLoadSideComp.Type = DataPlant::PlantEquipmentType::HeatPumpEIRCooling; - // then the source side - - state->dataPlnt->PlantLoop(2).FluidName = "WATER"; - state->dataPlnt->PlantLoop(2).glycol = Fluid::GetWater(*state); - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).TotalBranches = 1; - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch.allocate(1); - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).TotalComponents = 1; - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp.allocate(1); - auto &PLHPPlantLoadSourceComp = state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp(1); - PLHPPlantLoadSourceComp.Type = DataPlant::PlantEquipmentType::HeatPumpEIRCooling; - - // the init call expects a "from" calling point - PlantLocation myLocation = PlantLocation(1, DataPlant::LoopSideLocation::Supply, 1, 1); - - // call the factory with a valid name to trigger reading inputs - EIRPlantLoopHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpEIRCooling, "HP COOLING SIDE"); - - // verify the size of the vector and the processed condition - EXPECT_EQ(1u, state->dataEIRPlantLoopHeatPump->heatPumps.size()); - - // for now we know the order is maintained, so get each heat pump object - EIRPlantLoopHeatPump *thisCoolingPLHP = &state->dataEIRPlantLoopHeatPump->heatPumps[0]; - - // do a bit of extra wiring up to the plant - PLHPPlantLoadSideComp.Name = thisCoolingPLHP->name; - PLHPPlantLoadSideComp.NodeNumIn = thisCoolingPLHP->loadSideNodes.inlet; - PLHPPlantLoadSourceComp.Name = thisCoolingPLHP->name; - PLHPPlantLoadSourceComp.NodeNumIn = thisCoolingPLHP->sourceSideNodes.inlet; - - // call for all initialization - state->dataGlobal->BeginEnvrnFlag = true; - state->dataPlnt->PlantFirstSizesOkayToFinalize = true; - thisCoolingPLHP->onInitLoopEquip(*state, myLocation); - - // call with run flag off, loose limits on node min/max - thisCoolingPLHP->running = false; - thisCoolingPLHP->setOperatingFlowRatesWSHP(*state, firstHVACIteration); - EXPECT_NEAR(0.0, thisCoolingPLHP->loadSideMassFlowRate, 0.001); - EXPECT_NEAR(0.0, thisCoolingPLHP->sourceSideMassFlowRate, 0.001); - - // call with run flag off, nonzero minimums - state->dataLoopNodes->Node(thisCoolingPLHP->loadSideNodes.inlet).MassFlowRateMinAvail = 0.1; - state->dataLoopNodes->Node(thisCoolingPLHP->sourceSideNodes.inlet).MassFlowRateMinAvail = 0.2; - thisCoolingPLHP->running = false; - thisCoolingPLHP->setOperatingFlowRatesWSHP(*state, firstHVACIteration); - EXPECT_NEAR(0.1, thisCoolingPLHP->loadSideMassFlowRate, 0.001); - EXPECT_NEAR(0.2, thisCoolingPLHP->sourceSideMassFlowRate, 0.001); - - // call with run flag off, load side flow locked - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).FlowLock = DataPlant::FlowLock::Locked; - state->dataLoopNodes->Node(thisCoolingPLHP->loadSideNodes.inlet).MassFlowRate = 0.24; - state->dataLoopNodes->Node(thisCoolingPLHP->sourceSideNodes.inlet).MassFlowRateMinAvail = 0.0; - thisCoolingPLHP->running = false; - thisCoolingPLHP->setOperatingFlowRatesWSHP(*state, firstHVACIteration); - EXPECT_NEAR(0.24, thisCoolingPLHP->loadSideMassFlowRate, 0.001); - EXPECT_NEAR(0.0, thisCoolingPLHP->sourceSideMassFlowRate, 0.001); - - // call with run flag ON, flow locked at zero on load side - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).FlowLock = DataPlant::FlowLock::Locked; - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).FlowLock = DataPlant::FlowLock::Locked; - state->dataLoopNodes->Node(thisCoolingPLHP->loadSideNodes.inlet).MassFlowRate = 0.0; - state->dataLoopNodes->Node(thisCoolingPLHP->sourceSideNodes.inlet).MassFlowRate = 0.2; - thisCoolingPLHP->running = true; - thisCoolingPLHP->setOperatingFlowRatesWSHP(*state, firstHVACIteration); - EXPECT_NEAR(0.0, thisCoolingPLHP->loadSideMassFlowRate, 0.001); - EXPECT_NEAR(0.2, thisCoolingPLHP->sourceSideMassFlowRate, 0.001); - - // call with run flag ON, flow locked at zero on source side - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).FlowLock = DataPlant::FlowLock::Locked; - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).FlowLock = DataPlant::FlowLock::Locked; - state->dataLoopNodes->Node(thisCoolingPLHP->loadSideNodes.inlet).MassFlowRate = 0.2; - state->dataLoopNodes->Node(thisCoolingPLHP->sourceSideNodes.inlet).MassFlowRate = 0.0; - thisCoolingPLHP->running = true; - thisCoolingPLHP->setOperatingFlowRatesWSHP(*state, firstHVACIteration); - EXPECT_NEAR(0.2, thisCoolingPLHP->loadSideMassFlowRate, 0.001); - EXPECT_NEAR(0.0, thisCoolingPLHP->sourceSideMassFlowRate, 0.001); - - // call with run flag ON, flow locked at zero on both sides - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).FlowLock = DataPlant::FlowLock::Locked; - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).FlowLock = DataPlant::FlowLock::Locked; - state->dataLoopNodes->Node(thisCoolingPLHP->loadSideNodes.inlet).MassFlowRate = 0.0; - state->dataLoopNodes->Node(thisCoolingPLHP->sourceSideNodes.inlet).MassFlowRate = 0.0; - thisCoolingPLHP->running = true; - thisCoolingPLHP->setOperatingFlowRatesWSHP(*state, firstHVACIteration); - EXPECT_NEAR(0.0, thisCoolingPLHP->loadSideMassFlowRate, 0.001); - EXPECT_NEAR(0.0, thisCoolingPLHP->sourceSideMassFlowRate, 0.001); - - // call with run flag ON, flow locked at nonzero both - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).FlowLock = DataPlant::FlowLock::Locked; - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).FlowLock = DataPlant::FlowLock::Locked; - state->dataLoopNodes->Node(thisCoolingPLHP->loadSideNodes.inlet).MassFlowRate = 0.14; - state->dataLoopNodes->Node(thisCoolingPLHP->sourceSideNodes.inlet).MassFlowRate = 0.13; - thisCoolingPLHP->running = true; - thisCoolingPLHP->setOperatingFlowRatesWSHP(*state, firstHVACIteration); - EXPECT_NEAR(0.14, thisCoolingPLHP->loadSideMassFlowRate, 0.001); - EXPECT_NEAR(0.13, thisCoolingPLHP->sourceSideMassFlowRate, 0.001); -} - -TEST_F(EnergyPlusFixture, OnInitLoopEquipTopologyErrorCases) -{ - std::string const idf_objects = delimited_string({"HeatPump:PlantLoop:EIR:Cooling,", - " hp cooling side,", - " node 1,", - " node 2,", - " WaterSource,", - " node 3,", - " node 4,", - " ,", - " ,", - " ,", - " 0.0001,", - " 0.0001,", - " ,", - " 1000,", - " 3.14,", - " ,", - " dummyCurve,", - " dummyCurve,", - " dummyCurve;", - "Curve:Linear,", - " dummyCurve,", - " 0.95,", - " 0,", - " 1,", - " 1;"}); - ASSERT_TRUE(process_idf(idf_objects)); - state->init_state(*state); - - // set up a couple simple plant loops with one branch per loop-side and one component per branch - state->dataPlnt->TotNumLoops = 2; - state->dataPlnt->PlantLoop.allocate(state->dataPlnt->TotNumLoops); - - state->dataPlnt->PlantLoop(1).FluidName = "WATER"; - state->dataPlnt->PlantLoop(1).glycol = Fluid::GetWater(*state); - state->dataPlnt->PlantLoop(1).LoopDemandCalcScheme = DataPlant::LoopDemandCalcScheme::SingleSetPoint; - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Demand).TotalBranches = 1; - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Demand).Branch.allocate(1); - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).TotalComponents = 1; - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp.allocate(1); - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).TotalBranches = 1; - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch.allocate(1); - 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); - - state->dataPlnt->PlantLoop(2).FluidName = "WATER"; - state->dataPlnt->PlantLoop(2).glycol = Fluid::GetWater(*state); - state->dataPlnt->PlantLoop(2).LoopDemandCalcScheme = DataPlant::LoopDemandCalcScheme::SingleSetPoint; - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).TotalBranches = 1; - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch.allocate(1); - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).TotalComponents = 1; - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp.allocate(1); - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Supply).TotalBranches = 1; - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Supply).Branch.allocate(1); - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).TotalComponents = 1; - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp.allocate(1); - auto &PLHPPlantSupplySideComp = state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp(1); - auto &PLHPPlantDemandSideComp = state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp(1); - auto &extraPLHPPlantSupplySideComp = state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp(1); - auto &extraPLHPPlantDemandSideComp = state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp(1); - PLHPPlantSupplySideComp.Type = DataPlant::PlantEquipmentType::HeatPumpEIRCooling; - PLHPPlantDemandSideComp.Type = DataPlant::PlantEquipmentType::HeatPumpEIRCooling; - extraPLHPPlantSupplySideComp.Type = DataPlant::PlantEquipmentType::HeatPumpEIRCooling; - extraPLHPPlantDemandSideComp.Type = DataPlant::PlantEquipmentType::HeatPumpEIRCooling; - - // call the factory with a valid name to trigger reading inputs - EIRPlantLoopHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpEIRCooling, "HP COOLING SIDE"); - EXPECT_EQ(1u, state->dataEIRPlantLoopHeatPump->heatPumps.size()); - EIRPlantLoopHeatPump *thisCoolingPLHP = &state->dataEIRPlantLoopHeatPump->heatPumps[0]; - - // init the plant component data with the name we have now from the factory call - PLHPPlantSupplySideComp.Name = thisCoolingPLHP->name; - PLHPPlantDemandSideComp.Name = thisCoolingPLHP->name; - - // the init call expects a "from" calling point - PlantLocation myLoadLocation = PlantLocation(1, DataPlant::LoopSideLocation::Supply, 1, 1); - - // set a couple global flags - state->dataGlobal->BeginEnvrnFlag = true; - state->dataPlnt->PlantFirstSizesOkayToFinalize = true; - - // test the case where the heat pump is connected to both the supply and demand sides of the same loop - PLHPPlantSupplySideComp.NodeNumIn = thisCoolingPLHP->loadSideNodes.inlet; - PLHPPlantDemandSideComp.NodeNumIn = thisCoolingPLHP->sourceSideNodes.inlet; - extraPLHPPlantSupplySideComp.NodeNumIn = -1; - extraPLHPPlantDemandSideComp.NodeNumIn = -1; - // call for all initialization, it should abort because the coil load and supply sides were on the same loop - EXPECT_THROW(thisCoolingPLHP->onInitLoopEquip(*state, myLoadLocation), std::runtime_error); - - // test the case where the heat pump source side cannot be found - PLHPPlantSupplySideComp.NodeNumIn = thisCoolingPLHP->loadSideNodes.inlet; - PLHPPlantDemandSideComp.NodeNumIn = -1; - extraPLHPPlantSupplySideComp.NodeNumIn = -1; - extraPLHPPlantDemandSideComp.NodeNumIn = -1; - // call for all initialization, it should abort because the coil source side inlet node is not found on plant - EXPECT_THROW(thisCoolingPLHP->onInitLoopEquip(*state, myLoadLocation), std::runtime_error); - - // test the case where the heat pump load side cannot be found - PLHPPlantSupplySideComp.NodeNumIn = -1; - PLHPPlantDemandSideComp.NodeNumIn = thisCoolingPLHP->sourceSideNodes.inlet; - extraPLHPPlantSupplySideComp.NodeNumIn = -1; - extraPLHPPlantDemandSideComp.NodeNumIn = -1; - // call for all initialization, it should abort because the coil load side inlet node is not found on plant - EXPECT_THROW(thisCoolingPLHP->onInitLoopEquip(*state, myLoadLocation), std::runtime_error); - - // test the case where the heat pump source side is found, but it's on the supply side of a loop - // still need to drop the load side onto a (extra) plant supply to trigger this condition - PLHPPlantSupplySideComp.NodeNumIn = thisCoolingPLHP->sourceSideNodes.inlet; - PLHPPlantDemandSideComp.NodeNumIn = -1; - extraPLHPPlantSupplySideComp.NodeNumIn = thisCoolingPLHP->loadSideNodes.inlet; - extraPLHPPlantDemandSideComp.NodeNumIn = -1; - // call for all initialization, it should abort because the coil source was found on a supply side - EXPECT_THROW(thisCoolingPLHP->onInitLoopEquip(*state, myLoadLocation), std::runtime_error); - - // test the case where the heat pump load side is found, but it's on the demand side of a loop - // still need to drop the source side onto a (extra) plant demand to trigger this condition - PLHPPlantSupplySideComp.NodeNumIn = -1; - PLHPPlantDemandSideComp.NodeNumIn = thisCoolingPLHP->loadSideNodes.inlet; - extraPLHPPlantSupplySideComp.NodeNumIn = -1; - extraPLHPPlantDemandSideComp.NodeNumIn = thisCoolingPLHP->sourceSideNodes.inlet; - // call for all initialization, it should abort because the coil load was found on a demand side - EXPECT_THROW(thisCoolingPLHP->onInitLoopEquip(*state, myLoadLocation), std::runtime_error); -} - -TEST_F(EnergyPlusFixture, CoolingSimulate_WaterSource) -{ - std::string const idf_objects = delimited_string({"HeatPump:PlantLoop:EIR:Cooling,", - " hp cooling side,", - " node 1,", - " node 2,", - " WaterSource,", - " node 3,", - " node 4,", - " ,", - " ,", - " ,", - " 0.0001,", - " 0.0001,", - " ,", - " 1000,", - " 3.14,", - " ,", - " dummyCurve,", - " dummyCurve,", - " dummyCurve;", - "Curve:Linear,", - " dummyCurve,", - " 0.95,", - " 0,", - " 1,", - " 1;"}); - ASSERT_TRUE(process_idf(idf_objects)); - state->init_state(*state); - - // set up the plant loops - // first the load side - state->dataPlnt->TotNumLoops = 2; - state->dataPlnt->PlantLoop.allocate(2); - - state->dataPlnt->PlantLoop(1).FluidName = "WATER"; - state->dataPlnt->PlantLoop(1).glycol = Fluid::GetWater(*state); - state->dataPlnt->PlantLoop(1).LoopDemandCalcScheme = DataPlant::LoopDemandCalcScheme::SingleSetPoint; - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).TotalBranches = 1; - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch.allocate(1); - 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 &PLHPPlantLoadSideComp = state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp(1); - PLHPPlantLoadSideComp.Type = DataPlant::PlantEquipmentType::HeatPumpEIRCooling; - PLHPPlantLoadSideComp.CurOpSchemeType = DataPlant::OpScheme::CompSetPtBased; - // then the source side - - state->dataPlnt->PlantLoop(2).FluidName = "WATER"; - state->dataPlnt->PlantLoop(2).glycol = Fluid::GetWater(*state); - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).TotalBranches = 1; - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch.allocate(1); - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).TotalComponents = 1; - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp.allocate(1); - auto &PLHPPlantLoadSourceComp = state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp(1); - PLHPPlantLoadSourceComp.Type = DataPlant::PlantEquipmentType::HeatPumpEIRCooling; - - // the init call expects a "from" calling point - PlantLocation myLoadLocation = PlantLocation(1, DataPlant::LoopSideLocation::Supply, 1, 1); - PlantLocation mySourceLocation = PlantLocation(2, DataPlant::LoopSideLocation::Demand, 1, 1); - - // call the factory with a valid name to trigger reading inputs - EIRPlantLoopHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpEIRCooling, "HP COOLING SIDE"); - - // verify the size of the vector and the processed condition - EXPECT_EQ(1u, state->dataEIRPlantLoopHeatPump->heatPumps.size()); - - // for now we know the order is maintained, so get each heat pump object - EIRPlantLoopHeatPump *thisCoolingPLHP = &state->dataEIRPlantLoopHeatPump->heatPumps[0]; - - // do a bit of extra wiring up to the plant - PLHPPlantLoadSideComp.Name = thisCoolingPLHP->name; - PLHPPlantLoadSideComp.NodeNumIn = thisCoolingPLHP->loadSideNodes.inlet; - PLHPPlantLoadSourceComp.Name = thisCoolingPLHP->name; - PLHPPlantLoadSourceComp.NodeNumIn = thisCoolingPLHP->sourceSideNodes.inlet; - - // call for all initialization - state->dataGlobal->BeginEnvrnFlag = true; - state->dataPlnt->PlantFirstSizesOkayToFinalize = true; - thisCoolingPLHP->onInitLoopEquip(*state, myLoadLocation); - - // call from load side location, firsthvac, no load, not running, verify the unit doesn't have any values lingering - thisCoolingPLHP->loadSideHeatTransfer = 1000; - thisCoolingPLHP->loadSideInletTemp = 23.0; - thisCoolingPLHP->loadSideOutletTemp = 42.0; - thisCoolingPLHP->powerUsage = 4.0; - thisCoolingPLHP->sourceSideHeatTransfer = 60.0; - thisCoolingPLHP->sourceSideInletTemp = 43.0; - thisCoolingPLHP->sourceSideOutletTemp = 83.0; - bool firstHVAC = true; - Real64 curLoad = 0.0; - bool runFlag = false; - thisCoolingPLHP->simulate(*state, myLoadLocation, firstHVAC, curLoad, runFlag); - EXPECT_NEAR(0.0, thisCoolingPLHP->loadSideHeatTransfer, 0.001); - EXPECT_NEAR(0.0, thisCoolingPLHP->sourceSideHeatTransfer, 0.001); - EXPECT_NEAR(0.0, thisCoolingPLHP->powerUsage, 0.001); - EXPECT_NEAR(thisCoolingPLHP->loadSideInletTemp, thisCoolingPLHP->loadSideOutletTemp, 0.001); - EXPECT_NEAR(thisCoolingPLHP->sourceSideInletTemp, thisCoolingPLHP->sourceSideOutletTemp, 0.001); - - // call from source side location, firsthvac, no load, not running, connected loop should be triggered to resimulate - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).SimLoopSideNeeded = false; - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).SimLoopSideNeeded = false; - thisCoolingPLHP->simulate(*state, mySourceLocation, firstHVAC, curLoad, runFlag); - EXPECT_TRUE(state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).SimLoopSideNeeded); - - thisCoolingPLHP->setPointNodeNum = thisCoolingPLHP->loadSideNodes.outlet; - - // now we can call it again from the load side, but this time there is load (still firsthvac, unit can meet load) - { - firstHVAC = true; - curLoad = -800; - runFlag = true; - Real64 constexpr expectedLoadMassFlowRate = 0.09999; - Real64 constexpr expectedCp = 4183; - Real64 constexpr specifiedLoadSetpoint = 15; - Real64 const calculatedLoadInletTemp = specifiedLoadSetpoint - curLoad / (expectedLoadMassFlowRate * expectedCp); - state->dataLoopNodes->Node(thisCoolingPLHP->setPointNodeNum).TempSetPoint = specifiedLoadSetpoint; - state->dataLoopNodes->Node(thisCoolingPLHP->loadSideNodes.inlet).Temp = calculatedLoadInletTemp; - state->dataLoopNodes->Node(thisCoolingPLHP->sourceSideNodes.inlet).Temp = 30; - thisCoolingPLHP->simulate(*state, myLoadLocation, firstHVAC, curLoad, runFlag); - // expect it to meet setpoint and have some pre-evaluated conditions - EXPECT_NEAR(specifiedLoadSetpoint, thisCoolingPLHP->loadSideOutletTemp, 0.001); - EXPECT_NEAR(-curLoad, thisCoolingPLHP->loadSideHeatTransfer, 0.001); - } - - // now we can call it again from the load side, but this time there is load (still firsthvac, unit cannot meet load) - { - firstHVAC = true; - curLoad = -1200; - Real64 availableCapacity = 950.0; - runFlag = true; - Real64 constexpr expectedLoadMassFlowRate = 0.09999; - Real64 constexpr expectedCp = 4183; - Real64 constexpr specifiedLoadSetpoint = 15; - Real64 const calculatedLoadInletTemp = specifiedLoadSetpoint - curLoad / (expectedLoadMassFlowRate * expectedCp); - state->dataLoopNodes->Node(thisCoolingPLHP->setPointNodeNum).TempSetPoint = specifiedLoadSetpoint; - state->dataLoopNodes->Node(thisCoolingPLHP->loadSideNodes.inlet).Temp = calculatedLoadInletTemp; - state->dataLoopNodes->Node(thisCoolingPLHP->sourceSideNodes.inlet).Temp = 30; - thisCoolingPLHP->simulate(*state, myLoadLocation, firstHVAC, curLoad, runFlag); - // expect it to miss setpoint and be at max capacity - EXPECT_NEAR(15.597, thisCoolingPLHP->loadSideOutletTemp, 0.001); - EXPECT_NEAR(availableCapacity, thisCoolingPLHP->loadSideHeatTransfer, 0.001); - } -} - -TEST_F(EnergyPlusFixture, HeatingSimulate_WaterSource) -{ - std::string const idf_objects = delimited_string({"HeatPump:PlantLoop:EIR:Heating,", - " hp heating side,", - " node 1,", - " node 2,", - " WaterSource,", - " node 3,", - " node 4,", - " ,", - " ,", - " ,", - " 0.0001,", - " 0.0001,", - " ,", - " 1000,", - " 3.14,", - " ,", - " dummyCurve,", - " dummyCurve,", - " dummyCurve;", - "Curve:Linear,", - " dummyCurve,", - " 0.95,", - " 0,", - " 1,", - " 1;"}); - ASSERT_TRUE(process_idf(idf_objects)); - state->init_state(*state); - - // set up the plant loops - // first the load side - state->dataPlnt->TotNumLoops = 2; - state->dataPlnt->PlantLoop.allocate(2); - - state->dataPlnt->PlantLoop(1).FluidName = "WATER"; - state->dataPlnt->PlantLoop(1).glycol = Fluid::GetWater(*state); - state->dataPlnt->PlantLoop(1).LoopDemandCalcScheme = DataPlant::LoopDemandCalcScheme::SingleSetPoint; - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).TotalBranches = 1; - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch.allocate(1); - 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 &PLHPPlantLoadSideComp = state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp(1); - PLHPPlantLoadSideComp.Type = DataPlant::PlantEquipmentType::HeatPumpEIRHeating; - PLHPPlantLoadSideComp.CurOpSchemeType = DataPlant::OpScheme::CompSetPtBased; - // then the source side - - state->dataPlnt->PlantLoop(2).FluidName = "WATER"; - state->dataPlnt->PlantLoop(2).glycol = Fluid::GetWater(*state); - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).TotalBranches = 1; - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch.allocate(1); - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).TotalComponents = 1; - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp.allocate(1); - auto &PLHPPlantLoadSourceComp = state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp(1); - PLHPPlantLoadSourceComp.Type = DataPlant::PlantEquipmentType::HeatPumpEIRHeating; - - // the init call expects a "from" calling point - PlantLocation myLoadLocation = PlantLocation(1, DataPlant::LoopSideLocation::Supply, 1, 1); - - // call the factory with a valid name to trigger reading inputs - EIRPlantLoopHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpEIRHeating, "HP HEATING SIDE"); - - // verify the size of the vector and the processed condition - EXPECT_EQ(1u, state->dataEIRPlantLoopHeatPump->heatPumps.size()); - - // for now we know the order is maintained, so get each heat pump object - EIRPlantLoopHeatPump *thisHeatingPLHP = &state->dataEIRPlantLoopHeatPump->heatPumps[0]; - - // do a bit of extra wiring up to the plant - PLHPPlantLoadSideComp.Name = thisHeatingPLHP->name; - PLHPPlantLoadSideComp.NodeNumIn = thisHeatingPLHP->loadSideNodes.inlet; - PLHPPlantLoadSourceComp.Name = thisHeatingPLHP->name; - PLHPPlantLoadSourceComp.NodeNumIn = thisHeatingPLHP->sourceSideNodes.inlet; - - // call for all initialization - state->dataGlobal->BeginEnvrnFlag = true; - state->dataPlnt->PlantFirstSizesOkayToFinalize = true; - thisHeatingPLHP->onInitLoopEquip(*state, myLoadLocation); - - thisHeatingPLHP->setPointNodeNum = thisHeatingPLHP->loadSideNodes.outlet; - - // call it from the load side, but this time there is a negative (cooling) load - shouldn't try to run - { - bool firstHVAC = true; - Real64 curLoad = -900; - bool runFlag = true; // plant actually shouldn't do this but the component can be smart enough to handle it - Real64 constexpr specifiedLoadSetpoint = 45; - Real64 constexpr loadInletTemp = 46; - state->dataLoopNodes->Node(thisHeatingPLHP->setPointNodeNum).TempSetPoint = specifiedLoadSetpoint; - state->dataLoopNodes->Node(thisHeatingPLHP->loadSideNodes.inlet).Temp = loadInletTemp; - state->dataLoopNodes->Node(thisHeatingPLHP->sourceSideNodes.inlet).Temp = 30; - thisHeatingPLHP->simulate(*state, myLoadLocation, firstHVAC, curLoad, runFlag); - // expect it to meet setpoint and have some pre-evaluated conditions - EXPECT_NEAR(loadInletTemp, thisHeatingPLHP->loadSideOutletTemp, 0.001); - EXPECT_NEAR(0.0, thisHeatingPLHP->loadSideHeatTransfer, 0.001); - } - - // call it from the load side, but this time there is load (still firsthvac, unit can meet load) - { - bool firstHVAC = true; - Real64 curLoad = 800; - bool runFlag = true; - Real64 constexpr expectedLoadMassFlowRate = 0.09999; - Real64 constexpr expectedCp = 4180; - Real64 constexpr specifiedLoadSetpoint = 45; - Real64 const calculatedLoadInletTemp = specifiedLoadSetpoint - curLoad / (expectedLoadMassFlowRate * expectedCp); - state->dataLoopNodes->Node(thisHeatingPLHP->setPointNodeNum).TempSetPoint = specifiedLoadSetpoint; - state->dataLoopNodes->Node(thisHeatingPLHP->loadSideNodes.inlet).Temp = calculatedLoadInletTemp; - state->dataLoopNodes->Node(thisHeatingPLHP->sourceSideNodes.inlet).Temp = 30; - thisHeatingPLHP->simulate(*state, myLoadLocation, 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); - } - - // now we can call it again from the load side, but this time there is load (still firsthvac, unit cannot meet load) - { - bool firstHVAC = true; - Real64 curLoad = 1200; - Real64 availableCapacity = 950.0; - bool runFlag = true; - Real64 constexpr expectedLoadMassFlowRate = 0.09999; - Real64 constexpr expectedCp = 4180; - Real64 constexpr specifiedLoadSetpoint = 45; - Real64 const calculatedLoadInletTemp = specifiedLoadSetpoint - curLoad / (expectedLoadMassFlowRate * expectedCp); - state->dataLoopNodes->Node(thisHeatingPLHP->setPointNodeNum).TempSetPoint = specifiedLoadSetpoint; - state->dataLoopNodes->Node(thisHeatingPLHP->loadSideNodes.inlet).Temp = calculatedLoadInletTemp; - state->dataLoopNodes->Node(thisHeatingPLHP->sourceSideNodes.inlet).Temp = 30; - thisHeatingPLHP->simulate(*state, myLoadLocation, firstHVAC, curLoad, runFlag); - // expect it to miss setpoint and be at max capacity - EXPECT_NEAR(44.402, thisHeatingPLHP->loadSideOutletTemp, 0.001); - EXPECT_NEAR(availableCapacity, thisHeatingPLHP->loadSideHeatTransfer, 0.001); - } -} - -TEST_F(EnergyPlusFixture, TestConcurrentOperationChecking) -{ - state->dataEIRPlantLoopHeatPump->heatPumps.resize(4); - EIRPlantLoopHeatPump *coil1 = &state->dataEIRPlantLoopHeatPump->heatPumps[0]; - EIRPlantLoopHeatPump *coil2 = &state->dataEIRPlantLoopHeatPump->heatPumps[1]; - EIRPlantLoopHeatPump *coil3 = &state->dataEIRPlantLoopHeatPump->heatPumps[2]; - EIRPlantLoopHeatPump *coil4 = &state->dataEIRPlantLoopHeatPump->heatPumps[3]; - - // pair up the last two - coil3->companionHeatPumpCoil = coil4; - coil4->companionHeatPumpCoil = coil3; - - // set all of them to running - coil1->running = true; - coil2->running = true; - coil3->running = true; - coil4->running = true; - - // check to warn about concurrent operation - EIRPlantLoopHeatPump::checkConcurrentOperation(*state); - - // that will just add a recurring warning to the end, so to check whether - // a warning was actually made, I'll just check the warning index values - ASSERT_EQ(0, coil1->recurringConcurrentOperationWarningIndex); - ASSERT_EQ(0, coil2->recurringConcurrentOperationWarningIndex); - ASSERT_EQ(1, coil3->recurringConcurrentOperationWarningIndex); - ASSERT_EQ(1, coil4->recurringConcurrentOperationWarningIndex); -} - -TEST_F(EnergyPlusFixture, ConstructionFullObjectsHeatingAndCooling_AirSource) -{ - std::string const idf_objects = delimited_string({"HeatPump:PlantLoop:EIR:Heating,", - " hp heating side,", - " node 1,", - " node 2,", - " AirSource,", - " node 3,", - " node 4,", - " ,", - " ,", - " hp cooling side,", - " 0.001,", - " 0.001,", - " ,", - " 1000,", - " 3.14,", - " 2,", - " dummyCurve,", - " dummyCurve,", - " dummyCurve;", - "HeatPump:PlantLoop:EIR:Cooling,", - " hp cooling side,", - " node 1,", - " node 2,", - " AirSource,", - " node 3,", - " node 4,", - " ,", - " ,", - " hp heating side,", - " 0.001,", - " 0.001,", - " ,", - " 1000,", - " 3.14,", - " 2,", - " dummyCurve,", - " dummyCurve,", - " dummyCurve;", - "Curve:Linear,", - " dummyCurve,", - " 1,", - " 0,", - " 1,", - " 1;"}); - ASSERT_TRUE(process_idf(idf_objects)); - state->init_state(*state); - - // call the factory with a valid name to trigger reading inputs - EIRPlantLoopHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpEIRHeating, "HP HEATING SIDE"); - - // verify the size of the vector and the processed condition - EXPECT_EQ(2u, state->dataEIRPlantLoopHeatPump->heatPumps.size()); - - // for now we know the order is maintained, so get each heat pump object - EIRPlantLoopHeatPump *thisHeatingPLHP = &state->dataEIRPlantLoopHeatPump->heatPumps[1]; - EIRPlantLoopHeatPump *thisCoolingPLHP = &state->dataEIRPlantLoopHeatPump->heatPumps[0]; - - // validate the heating side - EXPECT_EQ("HP HEATING SIDE", thisHeatingPLHP->name); - EXPECT_ENUM_EQ(DataPlant::PlantEquipmentType::HeatPumpEIRHeating, thisHeatingPLHP->EIRHPType); - EXPECT_EQ(thisCoolingPLHP, thisHeatingPLHP->companionHeatPumpCoil); - EXPECT_EQ(1, thisHeatingPLHP->capFuncTempCurveIndex); - EXPECT_EQ(1, thisHeatingPLHP->powerRatioFuncTempCurveIndex); - EXPECT_EQ(1, thisHeatingPLHP->powerRatioFuncPLRCurveIndex); - - // validate the cooling side - EXPECT_EQ("HP COOLING SIDE", thisCoolingPLHP->name); - EXPECT_ENUM_EQ(DataPlant::PlantEquipmentType::HeatPumpEIRCooling, thisCoolingPLHP->EIRHPType); - EXPECT_EQ(thisHeatingPLHP, thisCoolingPLHP->companionHeatPumpCoil); - EXPECT_EQ(1, thisCoolingPLHP->capFuncTempCurveIndex); - EXPECT_EQ(1, thisCoolingPLHP->powerRatioFuncTempCurveIndex); - EXPECT_EQ(1, thisCoolingPLHP->powerRatioFuncPLRCurveIndex); - - // calling the factory with an invalid name or type will call ShowFatalError, which will trigger a runtime exception - EXPECT_THROW(EIRPlantLoopHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpEIRHeating, "fake"), std::runtime_error); - EXPECT_THROW(EIRPlantLoopHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpEIRCooling, "HP HEATING SIDE"), std::runtime_error); - EXPECT_THROW(EIRPlantLoopHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpEIRCooling, "fake"), std::runtime_error); - EXPECT_THROW(EIRPlantLoopHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpEIRHeating, "HP COOLING SIDE"), std::runtime_error); -} - -TEST_F(EnergyPlusFixture, CoolingSimulate_AirSource) -{ - std::string const idf_objects = delimited_string({"HeatPump:PlantLoop:EIR:Cooling,", - " hp cooling side,", - " node 1,", - " node 2,", - " AirSource,", - " node 3,", - " node 4,", - " ,", - " ,", - " ,", - " 0.0001,", - " 1,", - " ,", - " 1000,", - " 3.14,", - " ,", - " dummyCurve,", - " dummyCurve,", - " dummyCurve;", - "Curve:Linear,", - " dummyCurve,", - " 0.95,", - " 0,", - " 1,", - " 1;"}); - ASSERT_TRUE(process_idf(idf_objects)); - state->init_state(*state); - - // set up the plant loops - // first the load side - state->dataPlnt->TotNumLoops = 1; - state->dataPlnt->PlantLoop.allocate(1); - - state->dataPlnt->PlantLoop(1).FluidName = "WATER"; - state->dataPlnt->PlantLoop(1).glycol = Fluid::GetWater(*state); - state->dataPlnt->PlantLoop(1).LoopDemandCalcScheme = DataPlant::LoopDemandCalcScheme::SingleSetPoint; - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).TotalBranches = 1; - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch.allocate(1); - 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 &PLHPPlantLoadSideComp = state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp(1); - PLHPPlantLoadSideComp.Type = DataPlant::PlantEquipmentType::HeatPumpEIRCooling; - PLHPPlantLoadSideComp.CurOpSchemeType = DataPlant::OpScheme::CompSetPtBased; - - // the init call expects a "from" calling point - PlantLocation myLoadLocation = PlantLocation(1, DataPlant::LoopSideLocation::Supply, 1, 1); - - // call the factory with a valid name to trigger reading inputs - EIRPlantLoopHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpEIRCooling, "HP COOLING SIDE"); - - // verify the size of the vector and the processed condition - EXPECT_EQ(1u, state->dataEIRPlantLoopHeatPump->heatPumps.size()); - - // for now we know the order is maintained, so get each heat pump object - EIRPlantLoopHeatPump *thisCoolingPLHP = &state->dataEIRPlantLoopHeatPump->heatPumps[0]; - - // do a bit of extra wiring up to the plant - PLHPPlantLoadSideComp.Name = thisCoolingPLHP->name; - PLHPPlantLoadSideComp.NodeNumIn = thisCoolingPLHP->loadSideNodes.inlet; - - // call for all initialization - state->dataGlobal->BeginEnvrnFlag = true; - state->dataPlnt->PlantFirstSizesOkayToFinalize = true; - thisCoolingPLHP->onInitLoopEquip(*state, myLoadLocation); - thisCoolingPLHP->setPointNodeNum = thisCoolingPLHP->loadSideNodes.outlet; - - // call from load side location, firsthvac, no load, not running, verify the unit doesn't have any values lingering - thisCoolingPLHP->loadSideHeatTransfer = 1000; - thisCoolingPLHP->loadSideInletTemp = 23.0; - thisCoolingPLHP->loadSideOutletTemp = 42.0; - thisCoolingPLHP->powerUsage = 4.0; - thisCoolingPLHP->sourceSideHeatTransfer = 60.0; - thisCoolingPLHP->sourceSideInletTemp = 43.0; - thisCoolingPLHP->sourceSideOutletTemp = 83.0; - bool firstHVAC = true; - Real64 curLoad = 0.0; - bool runFlag = false; - thisCoolingPLHP->simulate(*state, myLoadLocation, firstHVAC, curLoad, runFlag); - EXPECT_NEAR(0.0, thisCoolingPLHP->loadSideHeatTransfer, 0.001); - EXPECT_NEAR(0.0, thisCoolingPLHP->sourceSideHeatTransfer, 0.001); - EXPECT_NEAR(0.0, thisCoolingPLHP->powerUsage, 0.001); - EXPECT_NEAR(thisCoolingPLHP->loadSideInletTemp, thisCoolingPLHP->loadSideOutletTemp, 0.001); - EXPECT_NEAR(thisCoolingPLHP->sourceSideInletTemp, thisCoolingPLHP->sourceSideOutletTemp, 0.001); - - // now we can call it again from the load side, but this time there is load (still firsthvac, unit can meet load) - { - firstHVAC = true; - curLoad = -800; - runFlag = true; - Real64 constexpr expectedLoadMassFlowRate = 0.09999; - Real64 constexpr expectedCp = 4183; - Real64 constexpr specifiedLoadSetpoint = 15; - Real64 const calculatedLoadInletTemp = specifiedLoadSetpoint - curLoad / (expectedLoadMassFlowRate * expectedCp); - state->dataLoopNodes->Node(thisCoolingPLHP->setPointNodeNum).TempSetPoint = specifiedLoadSetpoint; - state->dataLoopNodes->Node(thisCoolingPLHP->loadSideNodes.inlet).Temp = calculatedLoadInletTemp; - state->dataLoopNodes->Node(thisCoolingPLHP->sourceSideNodes.inlet).Temp = 30; - thisCoolingPLHP->simulate(*state, myLoadLocation, firstHVAC, curLoad, runFlag); - // expect it to meet setpoint and have some pre-evaluated conditions - EXPECT_NEAR(specifiedLoadSetpoint, thisCoolingPLHP->loadSideOutletTemp, 0.001); - EXPECT_NEAR(-curLoad, thisCoolingPLHP->loadSideHeatTransfer, 0.001); - } - - // now we can call it again from the load side, but this time there is load (still firsthvac, unit cannot meet load) - { - firstHVAC = true; - curLoad = -1200; - Real64 availableCapacity = 950.0; - runFlag = true; - Real64 constexpr expectedLoadMassFlowRate = 0.09999; - Real64 constexpr expectedCp = 4183; - Real64 constexpr specifiedLoadSetpoint = 15; - Real64 const calculatedLoadInletTemp = specifiedLoadSetpoint - curLoad / (expectedLoadMassFlowRate * expectedCp); - state->dataLoopNodes->Node(thisCoolingPLHP->setPointNodeNum).TempSetPoint = specifiedLoadSetpoint; - state->dataLoopNodes->Node(thisCoolingPLHP->loadSideNodes.inlet).Temp = calculatedLoadInletTemp; - state->dataLoopNodes->Node(thisCoolingPLHP->sourceSideNodes.inlet).Temp = 30; - thisCoolingPLHP->simulate(*state, myLoadLocation, firstHVAC, curLoad, runFlag); - // expect it to miss setpoint and be at max capacity - EXPECT_NEAR(15.597, thisCoolingPLHP->loadSideOutletTemp, 0.001); - EXPECT_NEAR(availableCapacity, thisCoolingPLHP->loadSideHeatTransfer, 0.001); - } -} - -TEST_F(EnergyPlusFixture, HeatingSimulate_AirSource) -{ - std::string const idf_objects = delimited_string({"HeatPump:PlantLoop:EIR:Heating,", - " hp heating side,", - " node 1,", - " node 2,", - " AirSource,", - " node 3,", - " node 4,", - " ,", - " ,", - " ,", - " 0.0001,", - " 1,", - " ,", - " 1000,", - " 3.14,", - " ,", - " dummyCurve,", - " dummyCurve,", - " dummyCurve,", - " 1.0,", - " HeatingCapacity,", - " Load,", - " ConstantFlow,", - " 0.5;", - "Curve:Linear,", - " dummyCurve,", - " 0.95,", - " 0,", - " 1,", - " 1;"}); - ASSERT_TRUE(process_idf(idf_objects)); - state->init_state(*state); - - // set up the plant loops - // first the load side - state->dataPlnt->TotNumLoops = 1; - state->dataPlnt->PlantLoop.allocate(1); - - state->dataPlnt->PlantLoop(1).FluidName = "WATER"; - state->dataPlnt->PlantLoop(1).glycol = Fluid::GetWater(*state); - state->dataPlnt->PlantLoop(1).LoopDemandCalcScheme = DataPlant::LoopDemandCalcScheme::SingleSetPoint; - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).TotalBranches = 1; - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch.allocate(1); - 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 &PLHPPlantLoadSideComp = state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp(1); - PLHPPlantLoadSideComp.Type = DataPlant::PlantEquipmentType::HeatPumpEIRHeating; - PLHPPlantLoadSideComp.CurOpSchemeType = DataPlant::OpScheme::CompSetPtBased; - - // the init call expects a "from" calling point - PlantLocation myLoadLocation = PlantLocation(1, DataPlant::LoopSideLocation::Supply, 1, 1); - - // call the factory with a valid name to trigger reading inputs - EIRPlantLoopHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpEIRHeating, "HP HEATING SIDE"); - - // verify the size of the vector and the processed condition - EXPECT_EQ(1u, state->dataEIRPlantLoopHeatPump->heatPumps.size()); - - // for now we know the order is maintained, so get each heat pump object - EIRPlantLoopHeatPump *thisHeatingPLHP = &state->dataEIRPlantLoopHeatPump->heatPumps[0]; - - // do a bit of extra wiring up to the plant - PLHPPlantLoadSideComp.Name = thisHeatingPLHP->name; - PLHPPlantLoadSideComp.NodeNumIn = thisHeatingPLHP->loadSideNodes.inlet; - - // call for all initialization - state->dataGlobal->BeginEnvrnFlag = true; - state->dataPlnt->PlantFirstSizesOkayToFinalize = true; - thisHeatingPLHP->onInitLoopEquip(*state, myLoadLocation); - thisHeatingPLHP->setPointNodeNum = thisHeatingPLHP->loadSideNodes.outlet; - - // call it from the load side, but this time there is a negative (cooling) load - shouldn't try to run - { - bool firstHVAC = true; - Real64 curLoad = -900; - bool runFlag = true; // plant actually shouldn't do this but the component can be smart enough to handle it - Real64 constexpr specifiedLoadSetpoint = 45; - Real64 constexpr loadInletTemp = 46; - state->dataLoopNodes->Node(thisHeatingPLHP->setPointNodeNum).TempSetPoint = specifiedLoadSetpoint; - state->dataLoopNodes->Node(thisHeatingPLHP->loadSideNodes.inlet).Temp = loadInletTemp; - state->dataLoopNodes->Node(thisHeatingPLHP->sourceSideNodes.inlet).Temp = 30; - thisHeatingPLHP->simulate(*state, myLoadLocation, firstHVAC, curLoad, runFlag); - // expect it to meet setpoint and have some pre-evaluated conditions - EXPECT_NEAR(loadInletTemp, thisHeatingPLHP->loadSideOutletTemp, 0.001); - EXPECT_NEAR(0.0, thisHeatingPLHP->loadSideHeatTransfer, 0.001); - } - - // call it from the load side, but this time there is load (still firsthvac, unit can meet load) - { - bool firstHVAC = true; - Real64 curLoad = 800; - bool runFlag = true; - Real64 constexpr expectedLoadMassFlowRate = 0.09999; - Real64 constexpr expectedCp = 4180; - Real64 constexpr specifiedLoadSetpoint = 45; - Real64 const calculatedLoadInletTemp = specifiedLoadSetpoint - curLoad / (expectedLoadMassFlowRate * expectedCp); - state->dataLoopNodes->Node(thisHeatingPLHP->setPointNodeNum).TempSetPoint = specifiedLoadSetpoint; - state->dataLoopNodes->Node(thisHeatingPLHP->loadSideNodes.inlet).Temp = calculatedLoadInletTemp; - state->dataLoopNodes->Node(thisHeatingPLHP->sourceSideNodes.inlet).Temp = 30; - thisHeatingPLHP->simulate(*state, myLoadLocation, 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); - } - - // now we can call it again from the load side, but this time there is load (still firsthvac, unit cannot meet load) - { - bool firstHVAC = true; - Real64 curLoad = 1200; - Real64 availableCapacity = 950.0; - bool runFlag = true; - Real64 constexpr expectedLoadMassFlowRate = 0.09999; - Real64 constexpr expectedCp = 4180; - Real64 constexpr specifiedLoadSetpoint = 45; - Real64 const calculatedLoadInletTemp = specifiedLoadSetpoint - curLoad / (expectedLoadMassFlowRate * expectedCp); - state->dataLoopNodes->Node(thisHeatingPLHP->setPointNodeNum).TempSetPoint = specifiedLoadSetpoint; - state->dataLoopNodes->Node(thisHeatingPLHP->loadSideNodes.inlet).Temp = calculatedLoadInletTemp; - state->dataLoopNodes->Node(thisHeatingPLHP->sourceSideNodes.inlet).Temp = 30; - thisHeatingPLHP->simulate(*state, myLoadLocation, firstHVAC, curLoad, runFlag); - // expect it to miss setpoint and be at max capacity - EXPECT_NEAR(44.402, thisHeatingPLHP->loadSideOutletTemp, 0.001); - EXPECT_NEAR(availableCapacity, thisHeatingPLHP->loadSideHeatTransfer, 0.001); - } - - // now we can call it again from the load side, but this time there is no load (still firsthvac) - { - bool firstHVAC = true; - Real64 curLoad = 0.0; - bool runFlag = true; - Real64 constexpr expectedLoadMassFlowRate = 0.09999; - Real64 constexpr expectedCp = 4180; - Real64 constexpr specifiedLoadSetpoint = 45; - Real64 const calculatedLoadInletTemp = specifiedLoadSetpoint - curLoad / (expectedLoadMassFlowRate * expectedCp); - state->dataLoopNodes->Node(thisHeatingPLHP->setPointNodeNum).TempSetPoint = specifiedLoadSetpoint; - state->dataLoopNodes->Node(thisHeatingPLHP->loadSideNodes.inlet).Temp = calculatedLoadInletTemp; - state->dataLoopNodes->Node(thisHeatingPLHP->sourceSideNodes.inlet).Temp = 30; - thisHeatingPLHP->simulate(*state, myLoadLocation, firstHVAC, curLoad, runFlag); - // expect it to miss setpoint and be at max capacity - EXPECT_NEAR(45.0, thisHeatingPLHP->loadSideOutletTemp, 0.001); - EXPECT_NEAR(30.0, thisHeatingPLHP->sourceSideOutletTemp, 0.001); - } - - // now we can call it again from the load side, but this time there is a very low load (still firsthvac) - { - bool firstHVAC = true; - Real64 curLoad = 100.0; - bool runFlag = true; - Real64 constexpr expectedLoadMassFlowRate = 0.09999; - Real64 constexpr expectedCp = 4180; - Real64 constexpr specifiedLoadSetpoint = 45; - Real64 const calculatedLoadInletTemp = specifiedLoadSetpoint - curLoad / (expectedLoadMassFlowRate * expectedCp); - state->dataLoopNodes->Node(thisHeatingPLHP->setPointNodeNum).TempSetPoint = specifiedLoadSetpoint; - state->dataLoopNodes->Node(thisHeatingPLHP->loadSideNodes.inlet).Temp = calculatedLoadInletTemp; - state->dataLoopNodes->Node(thisHeatingPLHP->sourceSideNodes.inlet).Temp = 30; - thisHeatingPLHP->simulate(*state, myLoadLocation, firstHVAC, curLoad, runFlag); - // expect it to miss setpoint and be at max capacity - EXPECT_NEAR((curLoad / thisHeatingPLHP->referenceCOP) * 0.95 * 0.95, thisHeatingPLHP->powerUsage, 0.001); - } -} - -TEST_F(EnergyPlusFixture, CoolingConstructionFullyAutoSized_AirSource) -{ - std::string const idf_objects = delimited_string({"HeatPump:PlantLoop:EIR:Cooling,", - " hp cooling side,", - " node 1,", - " node 2,", - " AirSource,", - " node 3,", - " node 4,", - " ,", - " ,", - " ,", - " Autosize,", - " Autosize,", - " ,", - " Autosize,", - " ,", - " 1,", - " dummyCurve,", - " dummyCurve,", - " dummyCurve;", - "Curve:Linear,", - " dummyCurve,", - " 1,", - " 0,", - " 1,", - " 1;"}); - ASSERT_TRUE(process_idf(idf_objects)); - state->init_state(*state); - - // call the factory with a valid name to trigger reading inputs - EIRPlantLoopHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpEIRCooling, "HP COOLING SIDE"); - - // verify the size of the vector and the processed condition - EXPECT_EQ(1u, state->dataEIRPlantLoopHeatPump->heatPumps.size()); - - // for now we know the order is maintained, so get each heat pump object - EIRPlantLoopHeatPump *thisCoolingPLHP = &state->dataEIRPlantLoopHeatPump->heatPumps[0]; - - // validate the cooling side - EXPECT_EQ("HP COOLING SIDE", thisCoolingPLHP->name); - EXPECT_ENUM_EQ(DataPlant::PlantEquipmentType::HeatPumpEIRCooling, thisCoolingPLHP->EIRHPType); - EXPECT_EQ(nullptr, thisCoolingPLHP->companionHeatPumpCoil); - EXPECT_EQ(1, thisCoolingPLHP->capFuncTempCurveIndex); - EXPECT_EQ(1, thisCoolingPLHP->powerRatioFuncTempCurveIndex); - EXPECT_EQ(1, thisCoolingPLHP->powerRatioFuncPLRCurveIndex); - - // calling the factory with an invalid name or type will call ShowFatalError, which will trigger a runtime exception - EXPECT_THROW(EIRPlantLoopHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpEIRCooling, "fake"), std::runtime_error); - EXPECT_THROW(EIRPlantLoopHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpEIRHeating, "HP COOLING SIDE"), std::runtime_error); -} - -TEST_F(EnergyPlusFixture, ClearState) -{ - std::string const idf_objects = delimited_string({"HeatPump:PlantLoop:EIR:Cooling,", - " hp cooling side,", - " node 1,", - " node 2,", - " AirSource,", - " node 3,", - " node 4,", - " ,", - " ,", - " ,", - " Autosize,", - " Autosize,", - " ,", - " Autosize,", - " ,", - " 1,", - " dummyCurve,", - " dummyCurve,", - " dummyCurve;", - "Curve:Linear,", - " dummyCurve,", - " 1,", - " 0,", - " 1,", - " 1;"}); - ASSERT_TRUE(process_idf(idf_objects)); - state->init_state(*state); - - // call the factory with a valid name to trigger reading inputs - EIRPlantLoopHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpEIRCooling, "HP COOLING SIDE"); - EXPECT_EQ(state->dataEIRPlantLoopHeatPump->heatPumps.size(), 1u); - - // test that vector is cleared - state->dataEIRPlantLoopHeatPump->clear_state(); - EXPECT_EQ(state->dataEIRPlantLoopHeatPump->heatPumps.size(), 0u); -} - -TEST_F(EnergyPlusFixture, Initialization2_AirSource) -{ - std::string const idf_objects = delimited_string({"HeatPump:PlantLoop:EIR:Cooling,", - " hp cooling side,", - " node 1,", - " node 2,", - " AirSource,", - " node 3,", - " node 4,", - " ,", - " ,", - " ,", - " 0.001,", - " 1,", - " ,", - " 1000,", - " 3.14,", - " ,", - " dummyCurve,", - " dummyCurve,", - " dummyCurve;", - "Curve:Linear,", - " dummyCurve,", - " 1,", - " 0,", - " 1,", - " 1;"}); - ASSERT_TRUE(process_idf(idf_objects)); - state->init_state(*state); - - bool firstHVACIteration = true; - // set up the plant loops - // first the load side - state->dataPlnt->TotNumLoops = 1; - state->dataPlnt->PlantLoop.allocate(1); - - state->dataPlnt->PlantLoop(1).FluidName = "WATER"; - state->dataPlnt->PlantLoop(1).glycol = Fluid::GetWater(*state); - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).TotalBranches = 1; - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch.allocate(1); - 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 &PLHPPlantLoadSideComp = state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp(1); - PLHPPlantLoadSideComp.Type = DataPlant::PlantEquipmentType::HeatPumpEIRCooling; - - // the init call expects a "from" calling point - PlantLocation myLocation = PlantLocation(1, DataPlant::LoopSideLocation::Supply, 1, 1); - - // call the factory with a valid name to trigger reading inputs - EIRPlantLoopHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpEIRCooling, "HP COOLING SIDE"); - - // verify the size of the vector and the processed condition - EXPECT_EQ(1u, state->dataEIRPlantLoopHeatPump->heatPumps.size()); - - // for now we know the order is maintained, so get each heat pump object - EIRPlantLoopHeatPump *thisCoolingPLHP = &state->dataEIRPlantLoopHeatPump->heatPumps[0]; - - // do a bit of extra wiring up to the plant - PLHPPlantLoadSideComp.Name = thisCoolingPLHP->name; - PLHPPlantLoadSideComp.NodeNumIn = thisCoolingPLHP->loadSideNodes.inlet; - - // call for all initialization - state->dataGlobal->BeginEnvrnFlag = true; - state->dataPlnt->PlantFirstSizesOkayToFinalize = true; - thisCoolingPLHP->onInitLoopEquip(*state, myLocation); - - // Componnent flow control type - auto &comp = DataPlant::CompData::getPlantComponent(*state, thisCoolingPLHP->loadSidePlantLoc); - comp.FlowCtrl = DataBranchAirLoopPlant::ControlType::Active; - - // call with run flag off, loose limits on node min/max - thisCoolingPLHP->running = false; - Real64 constexpr currentLoad = 0.0; - thisCoolingPLHP->setOperatingFlowRatesASHP(*state, firstHVACIteration, currentLoad); - EXPECT_NEAR(0.0, thisCoolingPLHP->loadSideMassFlowRate, 0.001); - EXPECT_NEAR(0.0, thisCoolingPLHP->sourceSideMassFlowRate, 0.001); - - // call with run flag off, nonzero minimums - state->dataLoopNodes->Node(thisCoolingPLHP->loadSideNodes.inlet).MassFlowRateMinAvail = 0.1; - thisCoolingPLHP->running = false; - thisCoolingPLHP->setOperatingFlowRatesASHP(*state, firstHVACIteration, currentLoad); - EXPECT_NEAR(0.1, thisCoolingPLHP->loadSideMassFlowRate, 0.001); - EXPECT_NEAR(0, thisCoolingPLHP->sourceSideMassFlowRate, 0.001); - - // call with run flag off, load side flow locked - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).FlowLock = DataPlant::FlowLock::Locked; - state->dataLoopNodes->Node(thisCoolingPLHP->loadSideNodes.inlet).MassFlowRate = 0.24; - thisCoolingPLHP->running = false; - thisCoolingPLHP->setOperatingFlowRatesASHP(*state, firstHVACIteration, currentLoad); - EXPECT_NEAR(0.24, thisCoolingPLHP->loadSideMassFlowRate, 0.001); - EXPECT_NEAR(0.0, thisCoolingPLHP->sourceSideMassFlowRate, 0.001); - - // call with run flag ON, flow locked at zero on load side - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).FlowLock = DataPlant::FlowLock::Locked; - state->dataLoopNodes->Node(thisCoolingPLHP->loadSideNodes.inlet).MassFlowRate = 0.0; - thisCoolingPLHP->running = true; - thisCoolingPLHP->setOperatingFlowRatesASHP(*state, firstHVACIteration, currentLoad); - EXPECT_NEAR(0.0, thisCoolingPLHP->loadSideMassFlowRate, 0.001); - EXPECT_NEAR(0, thisCoolingPLHP->sourceSideMassFlowRate, 0.001); - - // call with run flag ON, flow locked at zero on source side since there's no load - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).FlowLock = DataPlant::FlowLock::Locked; - state->dataLoopNodes->Node(thisCoolingPLHP->loadSideNodes.inlet).MassFlowRate = 0.2; - thisCoolingPLHP->running = true; - thisCoolingPLHP->setOperatingFlowRatesASHP(*state, firstHVACIteration, currentLoad); - EXPECT_NEAR(0.2, thisCoolingPLHP->loadSideMassFlowRate, 0.001); - EXPECT_NEAR(0.0, thisCoolingPLHP->sourceSideMassFlowRate, 0.001); - - comp.FlowCtrl = DataBranchAirLoopPlant::ControlType::SeriesActive; - thisCoolingPLHP->setOperatingFlowRatesASHP(*state, firstHVACIteration, currentLoad); - EXPECT_NEAR(1.29, thisCoolingPLHP->sourceSideMassFlowRate, 0.01); - - // call with run flag ON, flow locked at zero on both sides - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).FlowLock = DataPlant::FlowLock::Locked; - state->dataLoopNodes->Node(thisCoolingPLHP->loadSideNodes.inlet).MassFlowRate = 0.0; - thisCoolingPLHP->running = true; - thisCoolingPLHP->setOperatingFlowRatesASHP(*state, firstHVACIteration, currentLoad); - EXPECT_NEAR(0.0, thisCoolingPLHP->loadSideMassFlowRate, 0.001); - EXPECT_NEAR(0.0, thisCoolingPLHP->sourceSideMassFlowRate, 0.001); - - // call with run flag ON, flow locked at nonzero both - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).FlowLock = DataPlant::FlowLock::Locked; - state->dataLoopNodes->Node(thisCoolingPLHP->loadSideNodes.inlet).MassFlowRate = 0.14; - thisCoolingPLHP->running = true; - thisCoolingPLHP->setOperatingFlowRatesASHP(*state, firstHVACIteration, currentLoad); - EXPECT_NEAR(0.14, thisCoolingPLHP->loadSideMassFlowRate, 0.001); - EXPECT_NEAR(1.29, thisCoolingPLHP->sourceSideMassFlowRate, 0.01); -} - -TEST_F(EnergyPlusFixture, TestSizing_FullyAutosizedCoolingWithCompanion_AirSource) -{ - std::string const idf_objects = delimited_string({"HeatPump:PlantLoop:EIR:Cooling,", - " hp cooling side,", - " node 1,", - " node 2,", - " AirSource,", - " node 3,", - " node 4,", - " ,", - " ,", - " hp heating side,", - " Autosize,", - " Autosize,", - " ,", - " Autosize,", - " 1.0,", - " 1,", - " dummyCurve,", - " dummyCurve,", - " dummyCurve;", - "HeatPump:PlantLoop:EIR:Heating,", - " hp heating side,", - " node 5,", - " node 6,", - " AirSource,", - " node 7,", - " node 8,", - " ,", - " ,", - " hp cooling side,", - " Autosize,", - " Autosize,", - " ,", - " Autosize,", - " 2.0,", - " 1,", - " dummyCurve,", - " dummyCurve,", - " dummyCurve;", - "Curve:Linear,", - " dummyCurve,", - " 1,", - " 0,", - " 1,", - " 1;"}); - ASSERT_TRUE(process_idf(idf_objects)); - state->init_state(*state); - - // call the factory with a valid name to trigger reading inputs - EIRPlantLoopHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpEIRCooling, "HP COOLING SIDE"); - - // verify the size of the vector and the processed condition - EXPECT_EQ(2u, state->dataEIRPlantLoopHeatPump->heatPumps.size()); - - // for now we know the order is maintained, so get each heat pump object - EIRPlantLoopHeatPump *thisCoolingPLHP = &state->dataEIRPlantLoopHeatPump->heatPumps[0]; - EIRPlantLoopHeatPump *thisHeatingPLHP = &state->dataEIRPlantLoopHeatPump->heatPumps[1]; - - // validate that we have the right ones - EXPECT_EQ("HP COOLING SIDE", thisCoolingPLHP->name); - EXPECT_EQ("HP HEATING SIDE", thisHeatingPLHP->name); - - // We'll set up two plant loops: a load and a source loop - state->dataPlnt->TotNumLoops = 1; - state->dataPlnt->PlantLoop.allocate(state->dataPlnt->TotNumLoops); - - state->dataPlnt->PlantLoop(1).FluidName = "WATER"; - state->dataPlnt->PlantLoop(1).glycol = Fluid::GetWater(*state); - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Demand).TotalBranches = 1; - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Demand).Branch.allocate(1); - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).TotalComponents = 1; - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp.allocate(1); - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).TotalBranches = 1; - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch.allocate(1); - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).TotalComponents = 2; - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp.allocate(2); - - Real64 constexpr plantSizingLoadVolFlow = 0.01; - Real64 constexpr plantSizingLoadDeltaT = 1.0; - - Real64 constexpr plantSizingSrcDeltaT = 10.0; - - state->dataSize->PlantSizData.allocate(2); - state->dataSize->PlantSizData(1).DesVolFlowRate = 0.01; - state->dataSize->PlantSizData(1).DeltaT = 1.0; - state->dataSize->PlantSizData(2).DesVolFlowRate = 0.03; - state->dataSize->PlantSizData(2).DeltaT = 1.0; - - auto &loop1supplyComponent1 = state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp(1); - auto &loop1supplyComponent2 = state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp(2); - - loop1supplyComponent1.Type = DataPlant::PlantEquipmentType::HeatPumpEIRHeating; - loop1supplyComponent2.Type = DataPlant::PlantEquipmentType::HeatPumpEIRCooling; - - loop1supplyComponent1.Name = thisHeatingPLHP->name; - loop1supplyComponent2.Name = thisCoolingPLHP->name; - - loop1supplyComponent1.NodeNumIn = thisHeatingPLHP->loadSideNodes.inlet; - loop1supplyComponent2.NodeNumIn = thisCoolingPLHP->loadSideNodes.inlet; - - // the init call expects a "from" calling point - PlantLocation myCoolingLoadLocation = PlantLocation(1, DataPlant::LoopSideLocation::Supply, 1, 1); - PlantLocation myHeatingLoadLocation = PlantLocation(1, DataPlant::LoopSideLocation::Supply, 1, 2); - - // set a couple global flags - state->dataGlobal->BeginEnvrnFlag = true; - - // initialize so the components can find themselves on the plant - thisCoolingPLHP->onInitLoopEquip(*state, myCoolingLoadLocation); - thisHeatingPLHP->onInitLoopEquip(*state, myHeatingLoadLocation); - - state->dataPlnt->PlantFinalSizesOkayToReport = true; - state->dataPlnt->PlantFirstSizesOkayToReport = true; - state->dataPlnt->PlantFirstSizesOkayToFinalize = true; - - // assign the plant sizing data - state->dataPlnt->PlantLoop(1).PlantSizNum = 1; - - Real64 constexpr sourceSideInitTemp = 20.0; - Real64 constexpr sourceSideHumRat = 0.0; - Real64 expectedLoadCp = 4197.93; - Real64 expectedLoadRho = 999.898; - Real64 expectedSourceCp = Psychrometrics::PsyCpAirFnW(sourceSideHumRat); - Real64 expectedSourceRho = Psychrometrics::PsyRhoAirFnPbTdbW(*state, state->dataEnvrn->StdBaroPress, sourceSideInitTemp, sourceSideHumRat); - Real64 expectedLoadFlow = plantSizingLoadVolFlow; - Real64 expectedCapacity = expectedLoadRho * expectedLoadFlow * expectedLoadCp * plantSizingLoadDeltaT; - Real64 expectedSourceLoad = 0.0; - expectedSourceLoad = expectedCapacity * (1 + 1 / thisCoolingPLHP->referenceCOP); - Real64 expectedSourceFlow = expectedSourceLoad / (expectedSourceCp * expectedSourceRho * plantSizingSrcDeltaT); - thisCoolingPLHP->sizeLoadSide(*state); - thisCoolingPLHP->sizeSrcSideASHP(*state); - EXPECT_NEAR(expectedLoadFlow, thisCoolingPLHP->loadSideDesignVolFlowRate, 0.0001); - EXPECT_NEAR(expectedSourceFlow, thisCoolingPLHP->sourceSideDesignVolFlowRate, 0.1); - EXPECT_NEAR(expectedCapacity, thisCoolingPLHP->referenceCapacity, 0.0001); - - // with a sizing run complete, we can also go ahead and get the design capacities... - // they should be nonzero for the load side of things - Real64 tmpMin = -1.0, tmpMax = -1.0, tmpOpt = -1.0; - thisCoolingPLHP->getDesignCapacities(*state, myCoolingLoadLocation, tmpMax, tmpMin, tmpOpt); - EXPECT_NEAR(0.0, tmpMin, 0.001); - EXPECT_NEAR(expectedCapacity, tmpMax, 0.001); - EXPECT_NEAR(expectedCapacity, tmpOpt, 0.001); - - // we can reset things and do a few more corner cases here - - // lets just try it again but with the plant sizing data set to zero flow, it should try to use the companion - // but the companion isn't sized yet, so it should get zero conditions - thisCoolingPLHP->loadSideDesignVolFlowRate = DataSizing::AutoSize; - thisCoolingPLHP->sourceSideDesignVolFlowRate = DataSizing::AutoSize; - thisCoolingPLHP->referenceCapacity = DataSizing::AutoSize; - state->dataSize->PlantSizData(1).DesVolFlowRate = 0.0; - thisCoolingPLHP->sizeLoadSide(*state); - thisCoolingPLHP->sizeSrcSideASHP(*state); - EXPECT_NEAR(0.0, thisCoolingPLHP->loadSideDesignVolFlowRate, 0.0001); - EXPECT_NEAR(0.0, thisCoolingPLHP->sourceSideDesignVolFlowRate, 0.0001); - EXPECT_NEAR(0.0, thisCoolingPLHP->referenceCapacity, 0.0001); - - // but now let's try to size the heating coil, which will try to use the cooling coil's sized data - thisCoolingPLHP->loadSideDesignVolFlowRate = expectedLoadFlow; - thisCoolingPLHP->sourceSideDesignVolFlowRate = expectedSourceFlow; - thisCoolingPLHP->referenceCapacity = expectedCapacity; - expectedCapacity = expectedLoadRho * expectedLoadFlow * expectedLoadCp * plantSizingLoadDeltaT; - expectedSourceLoad = expectedCapacity * (1 - 1 / thisHeatingPLHP->referenceCOP); - expectedSourceFlow = expectedSourceLoad / (expectedSourceCp * expectedSourceRho * plantSizingSrcDeltaT); - thisHeatingPLHP->sizeLoadSide(*state); - thisHeatingPLHP->sizeSrcSideASHP(*state); - EXPECT_NEAR(expectedLoadFlow, thisHeatingPLHP->loadSideDesignVolFlowRate, 0.0001); - EXPECT_NEAR(expectedSourceFlow, thisHeatingPLHP->sourceSideDesignVolFlowRate, 0.1); - EXPECT_NEAR(expectedCapacity, thisHeatingPLHP->referenceCapacity, 0.0001); -} - -TEST_F(EnergyPlusFixture, TestSizing_HardsizedFlowAutosizedCoolingWithCompanion_AirSource) -{ - std::string const idf_objects = delimited_string({"HeatPump:PlantLoop:EIR:Cooling,", - " hp cooling side,", - " node 1,", - " node 2,", - " AirSource,", - " node 3,", - " node 4,", - " ,", - " ,", - " hp heating side,", - " Autosize,", - " 2.0,", - " ,", - " Autosize,", - " 1.0,", - " 1,", - " dummyCurve,", - " dummyCurve,", - " dummyCurve;", - "HeatPump:PlantLoop:EIR:Heating,", - " hp heating side,", - " node 5,", - " node 6,", - " AirSource,", - " node 7,", - " node 8,", - " ,", - " ,", - " hp cooling side,", - " Autosize,", - " 2.0,", - " ,", - " Autosize,", - " 1.0,", - " 1,", - " dummyCurve,", - " dummyCurve,", - " dummyCurve;", - "Curve:Linear,", - " dummyCurve,", - " 1,", - " 0,", - " 1,", - " 1;"}); - ASSERT_TRUE(process_idf(idf_objects)); - state->init_state(*state); - - // call the factory with a valid name to trigger reading inputs - EIRPlantLoopHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpEIRCooling, "HP COOLING SIDE"); - - // verify the size of the vector and the processed condition - EXPECT_EQ(2u, state->dataEIRPlantLoopHeatPump->heatPumps.size()); - - // for now we know the order is maintained, so get each heat pump object - EIRPlantLoopHeatPump *thisCoolingPLHP = &state->dataEIRPlantLoopHeatPump->heatPumps[0]; - EIRPlantLoopHeatPump *thisHeatingPLHP = &state->dataEIRPlantLoopHeatPump->heatPumps[1]; - - // validate that we have the right ones - EXPECT_EQ("HP COOLING SIDE", thisCoolingPLHP->name); - EXPECT_EQ("HP HEATING SIDE", thisHeatingPLHP->name); - - // We'll set up two plant loops: a load and a source loop - state->dataPlnt->TotNumLoops = 1; - state->dataPlnt->PlantLoop.allocate(state->dataPlnt->TotNumLoops); - - state->dataPlnt->PlantLoop(1).FluidName = "WATER"; - state->dataPlnt->PlantLoop(1).glycol = Fluid::GetWater(*state); - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Demand).TotalBranches = 1; - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Demand).Branch.allocate(1); - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).TotalComponents = 1; - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp.allocate(1); - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).TotalBranches = 1; - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch.allocate(1); - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).TotalComponents = 2; - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp.allocate(2); - - Real64 constexpr plantSizingLoadVolFlow = 0.01; - Real64 constexpr plantSizingLoadDeltaT = 1.0; - - state->dataSize->PlantSizData.allocate(2); - state->dataSize->PlantSizData(1).DesVolFlowRate = 0.01; - state->dataSize->PlantSizData(1).DeltaT = 1.0; - state->dataSize->PlantSizData(2).DesVolFlowRate = 0.03; - state->dataSize->PlantSizData(2).DeltaT = 1.0; - - auto &loop1supplyComponent1 = state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp(1); - auto &loop1supplyComponent2 = state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp(2); - - loop1supplyComponent1.Type = DataPlant::PlantEquipmentType::HeatPumpEIRHeating; - loop1supplyComponent2.Type = DataPlant::PlantEquipmentType::HeatPumpEIRCooling; - - loop1supplyComponent1.Name = thisHeatingPLHP->name; - loop1supplyComponent2.Name = thisCoolingPLHP->name; - - loop1supplyComponent1.NodeNumIn = thisHeatingPLHP->loadSideNodes.inlet; - loop1supplyComponent2.NodeNumIn = thisCoolingPLHP->loadSideNodes.inlet; - - // the init call expects a "from" calling point - PlantLocation myCoolingLoadLocation = PlantLocation(1, DataPlant::LoopSideLocation::Supply, 1, 1); - PlantLocation myHeatingLoadLocation = PlantLocation(1, DataPlant::LoopSideLocation::Supply, 1, 2); - - // set a couple global flags - state->dataGlobal->BeginEnvrnFlag = true; - - // initialize so the components can find themselves on the plant - thisCoolingPLHP->onInitLoopEquip(*state, myCoolingLoadLocation); - thisHeatingPLHP->onInitLoopEquip(*state, myHeatingLoadLocation); - - state->dataPlnt->PlantFinalSizesOkayToReport = true; - state->dataPlnt->PlantFirstSizesOkayToReport = true; - state->dataPlnt->PlantFirstSizesOkayToFinalize = true; - - // assign the plant sizing data - state->dataPlnt->PlantLoop(1).PlantSizNum = 1; - - Real64 expectedLoadCp = 4197.93; - Real64 expectedLoadRho = 999.898; - Real64 expectedLoadFlow = plantSizingLoadVolFlow; - Real64 expectedCapacity = expectedLoadRho * expectedLoadFlow * expectedLoadCp * plantSizingLoadDeltaT; - Real64 expectedSourceFlow = 2.0; - thisCoolingPLHP->sizeLoadSide(*state); - thisCoolingPLHP->sizeSrcSideASHP(*state); - EXPECT_NEAR(expectedLoadFlow, thisCoolingPLHP->loadSideDesignVolFlowRate, 0.0001); - EXPECT_NEAR(expectedSourceFlow, thisCoolingPLHP->sourceSideDesignVolFlowRate, 0.1); - EXPECT_NEAR(expectedCapacity, thisCoolingPLHP->referenceCapacity, 0.0001); - - // with a sizing run complete, we can also go ahead and get the design capacities... - // they should be nonzero for the load side of things - Real64 tmpMin = -1.0, tmpMax = -1.0, tmpOpt = -1.0; - thisCoolingPLHP->getDesignCapacities(*state, myCoolingLoadLocation, tmpMax, tmpMin, tmpOpt); - EXPECT_NEAR(0.0, tmpMin, 0.001); - EXPECT_NEAR(expectedCapacity, tmpMax, 0.001); - EXPECT_NEAR(expectedCapacity, tmpOpt, 0.001); -} - -TEST_F(EnergyPlusFixture, TestSizing_AutosizedFlowWithCompanion_AirSource) -{ - std::string const idf_objects = delimited_string({"HeatPump:PlantLoop:EIR:Cooling,", - " hp cooling side,", - " node 1,", - " node 2,", - " AirSource,", - " node 3,", - " node 4,", - " ,", - " ,", - " hp heating side,", - " 0.005,", - " Autosize,", - " ,", - " Autosize,", - " 1.0,", - " 1,", - " dummyCurve,", - " dummyCurve,", - " dummyCurve;", - "HeatPump:PlantLoop:EIR:Heating,", - " hp heating side,", - " node 5,", - " node 6,", - " AirSource,", - " node 7,", - " node 8,", - " ,", - " ,", - " hp cooling side,", - " 0.005,", - " Autosize,", - " ,", - " Autosize,", - " 2.0,", - " 1,", - " dummyCurve,", - " dummyCurve,", - " dummyCurve;", - "Curve:Linear,", - " dummyCurve,", - " 1,", - " 0,", - " 1,", - " 1;"}); - ASSERT_TRUE(process_idf(idf_objects)); - state->init_state(*state); - - // call the factory with a valid name to trigger reading inputs - EIRPlantLoopHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpEIRCooling, "HP COOLING SIDE"); - - // verify the size of the vector and the processed condition - EXPECT_EQ(2u, state->dataEIRPlantLoopHeatPump->heatPumps.size()); - - // for now we know the order is maintained, so get each heat pump object - EIRPlantLoopHeatPump *thisCoolingPLHP = &state->dataEIRPlantLoopHeatPump->heatPumps[0]; - EIRPlantLoopHeatPump *thisHeatingPLHP = &state->dataEIRPlantLoopHeatPump->heatPumps[1]; - - // We'll set up two plant loops: a load and a source loop - state->dataPlnt->TotNumLoops = 1; - state->dataPlnt->PlantLoop.allocate(state->dataPlnt->TotNumLoops); - - state->dataPlnt->PlantLoop(1).FluidName = "WATER"; - state->dataPlnt->PlantLoop(1).glycol = Fluid::GetWater(*state); - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Demand).TotalBranches = 1; - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Demand).Branch.allocate(1); - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).TotalComponents = 1; - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp.allocate(1); - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).TotalBranches = 1; - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch.allocate(1); - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).TotalComponents = 2; - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp.allocate(2); - - state->dataSize->PlantSizData.allocate(2); - state->dataSize->PlantSizData(1).DeltaT = 25.0; - state->dataSize->PlantSizData(2).DeltaT = 20.0; - - auto &loop1supplyComponent1 = state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp(1); - auto &loop1supplyComponent2 = state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp(2); - - loop1supplyComponent1.Type = DataPlant::PlantEquipmentType::HeatPumpEIRHeating; - loop1supplyComponent2.Type = DataPlant::PlantEquipmentType::HeatPumpEIRCooling; - - loop1supplyComponent1.Name = thisHeatingPLHP->name; - loop1supplyComponent2.Name = thisCoolingPLHP->name; - - loop1supplyComponent1.NodeNumIn = thisHeatingPLHP->loadSideNodes.inlet; - loop1supplyComponent2.NodeNumIn = thisCoolingPLHP->loadSideNodes.inlet; - - // the init call expects a "from" calling point - PlantLocation myCoolingLoadLocation = PlantLocation(1, DataPlant::LoopSideLocation::Supply, 1, 1); - PlantLocation myHeatingLoadLocation = PlantLocation(1, DataPlant::LoopSideLocation::Supply, 1, 2); - - // set a couple global flags - state->dataGlobal->BeginEnvrnFlag = true; - - state->dataPlnt->PlantFinalSizesOkayToReport = true; - state->dataPlnt->PlantFirstSizesOkayToReport = true; - state->dataPlnt->PlantFirstSizesOkayToFinalize = true; - - // assign the plant sizing data - state->dataPlnt->PlantLoop(1).PlantSizNum = 1; - - // initialize so the components can find themselves on the plant - thisCoolingPLHP->onInitLoopEquip(*state, myCoolingLoadLocation); - thisHeatingPLHP->onInitLoopEquip(*state, myHeatingLoadLocation); - - Real64 expectedClgSourceFlow = 86.71; - Real64 expectedHtgSourceFlow = 21.68; // changed from 86.71 due to issue#10381; - EXPECT_NEAR(expectedClgSourceFlow, thisCoolingPLHP->sourceSideDesignVolFlowRate, 0.1); - EXPECT_NEAR(expectedHtgSourceFlow, thisHeatingPLHP->sourceSideDesignVolFlowRate, 0.1); -} - -TEST_F(EnergyPlusFixture, Test_DoPhysics) -{ - - std::string const idf_objects = delimited_string({"HeatPump:PlantLoop:EIR:Cooling,", - " hp cooling side,", - " node 1,", - " node 2,", - " WaterSource,", - " node 3,", - " node 4,", - " ,", - " ,", - " hp heating side,", - " 0.005,", - " 0.002,", - " ,", - " 20000,", - " 3.0,", - " 1,", - " CapCurveFuncTemp,", - " EIRCurveFuncTemp,", - " EIRCurveFuncPLR;", - "HeatPump:PlantLoop:EIR:Heating,", - " hp heating side,", - " node 5,", - " node 6,", - " WaterSource,", - " node 7,", - " node 8,", - " ,", - " ,", - " hp cooling side,", - " 0.005,", - " 0.002,", - " ,", - " 20000,", - " 3.0,", - " 1,", - " CapCurveFuncTemp,", - " EIRCurveFuncTemp,", - " EIRCurveFuncPLR;", - "Curve:Biquadratic,", - " CapCurveFuncTemp,", - " 1.0,", - " 0.0,", - " 0.0,", - " 0.0,", - " 0.0,", - " 0.0,", - " 5.0,", - " 10.0,", - " 24.0,", - " 35.0,", - " ,", - " ,", - " Temperature,", - " Temperature,", - " Dimensionless;", - "Curve:Biquadratic,", - " EIRCurveFuncTemp,", - " 1.0,", - " 0.0,", - " 0.0,", - " 1.0,", - " 0.0,", - " 0.0,", - " 5.0,", - " 10.0,", - " 24.0,", - " 35.0,", - " ,", - " ,", - " Temperature,", - " Temperature,", - " Dimensionless;", - "Curve:Quadratic,", - " EIRCurveFuncPLR,", - " 1.0,", - " 0.0,", - " 0.0,", - " 0.0,", - " 1.0;"}); - - ASSERT_TRUE(process_idf(idf_objects)); - state->init_state(*state); - - // set up the plant loops - // first the load side - state->dataPlnt->TotNumLoops = 2; - state->dataPlnt->PlantLoop.allocate(2); - - state->dataPlnt->PlantLoop(1).FluidName = "WATER"; - state->dataPlnt->PlantLoop(1).glycol = Fluid::GetWater(*state); - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).TotalBranches = 1; - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch.allocate(1); - 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 &PLHPPlantLoadSideLoop = state->dataPlnt->PlantLoop(1); - auto &PLHPPlantLoadSideComp = state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp(1); - PLHPPlantLoadSideComp.Type = DataPlant::PlantEquipmentType::HeatPumpEIRCooling; - // then the source side - - state->dataPlnt->PlantLoop(2).FluidName = "WATER"; - state->dataPlnt->PlantLoop(2).glycol = Fluid::GetWater(*state); - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).TotalBranches = 1; - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch.allocate(1); - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).TotalComponents = 1; - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp.allocate(1); - - auto &PLHPPlantLoadSourceComp = state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp(1); - PLHPPlantLoadSourceComp.Type = DataPlant::PlantEquipmentType::HeatPumpEIRCooling; - - // call the factory with a valid name to trigger reading inputs - EIRPlantLoopHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpEIRCooling, "HP COOLING SIDE"); - - // verify the size of the vector and the processed condition - EXPECT_EQ(2u, state->dataEIRPlantLoopHeatPump->heatPumps.size()); - - // for now we know the order is maintained, so get each heat pump object - EIRPlantLoopHeatPump *thisCoolingPLHP = &state->dataEIRPlantLoopHeatPump->heatPumps[0]; - - // do a little setup here - thisCoolingPLHP->loadSidePlantLoc.loopNum = 1; - thisCoolingPLHP->loadSidePlantLoc.loopSideNum = DataPlant::LoopSideLocation::Supply; - thisCoolingPLHP->loadSidePlantLoc.branchNum = 1; - thisCoolingPLHP->loadSidePlantLoc.compNum = 1; - PlantUtilities::SetPlantLocationLinks(*state, thisCoolingPLHP->loadSidePlantLoc); - thisCoolingPLHP->loadSideNodes.outlet = 1; - thisCoolingPLHP->sourceSidePlantLoc.loopNum = 2; - PlantUtilities::SetPlantLocationLinks(*state, thisCoolingPLHP->sourceSidePlantLoc); - - // the factory would've called GetOnlySingleNode for the in/out pairs on the PLHP, add another one for the loop - // outlet setpoint node - state->dataLoopNodes->Node.allocate(5); - PLHPPlantLoadSideLoop.TempSetPointNodeNum = 5; - - // set up the plant setpoint conditions and test for single setpoint operation - PLHPPlantLoadSideLoop.LoopDemandCalcScheme = DataPlant::LoopDemandCalcScheme::SingleSetPoint; - PLHPPlantLoadSideComp.CurOpSchemeType = DataPlant::OpScheme::CompSetPtBased; - state->dataLoopNodes->Node(thisCoolingPLHP->loadSideNodes.outlet).TempSetPoint = 3.141; - state->dataLoopNodes->Node(5).TempSetPoint = 2.718; - PLHPPlantLoadSideComp.CurOpSchemeType = DataPlant::OpScheme::CoolingRB; - - // test for dual setpoint operation - PLHPPlantLoadSideLoop.LoopDemandCalcScheme = DataPlant::LoopDemandCalcScheme::DualSetPointDeadBand; - PLHPPlantLoadSideComp.CurOpSchemeType = DataPlant::OpScheme::CompSetPtBased; - state->dataLoopNodes->Node(thisCoolingPLHP->loadSideNodes.outlet).TempSetPointHi = 6.282; - state->dataLoopNodes->Node(5).TempSetPointHi = 5.436; - PLHPPlantLoadSideComp.CurOpSchemeType = DataPlant::OpScheme::CoolingRB; - - state->dataHVACGlobal->TimeStepSys = 60; - state->dataHVACGlobal->TimeStepSysSec = state->dataHVACGlobal->TimeStepSys * Constant::rSecsInHour; - - Real64 curLoad = -10000; - - thisCoolingPLHP->loadSideMassFlowRate = 0.3; - thisCoolingPLHP->sourceSideMassFlowRate = 0.8; - thisCoolingPLHP->loadSideInletTemp = 20; - thisCoolingPLHP->sourceSideInletTemp = 20; - thisCoolingPLHP->doPhysics(*state, curLoad); - - EXPECT_NEAR(thisCoolingPLHP->loadSideOutletTemp, 12.00, 0.1); - EXPECT_NEAR(thisCoolingPLHP->sourceSideOutletTemp, 47.90, 0.1); -} - -TEST_F(EnergyPlusFixture, Test_DoPhysics_AWHP) -{ - - std::string const idf_objects = - delimited_string({"HeatPump:PlantLoop:EIR:Cooling,", - " hp cooling side,", - " node 1,", - " node 2,", - " AirSource,", - " node 3,", - " node 4,", - " ,", - " ,", - " hp heating side,", - " 0.005,", - " 0.002,", - " ,", - " 20000,", - " 3.0,", - " 1,", - " CapCurveFuncTemp,", - " EIRCurveFuncTemp,", - " EIRCurveFuncPLR;", - - "HeatPump:PlantLoop:EIR:Heating,", - " hp heating side,", - " node 5,", - " node 6,", - " AirSource,", - " node 7,", - " node 8,", - " ,", - " ,", - " hp cooling side,", - " 0.005,", - " 0.002,", - " ,", - " 20000,", - " 3.0,", - " 1,", - " CapCurveFuncTemp,", - " EIRCurveFuncTemp,", - " EIRCurveFuncPLR;", - - "HeatPump:AirToWater,", - "test_AWHP, !- Name", - ", !- Availability Schedule Name Heating", - ", !- Availability Schedule Name Cooling", - "Load , !- Operating Mode Control Method", - "SingleMode, !- Operating Mode Control Option for Multiple Unit", - ", !- Operating Mode Control Schedule Name", - ", !- Minimum Part Load Ratio", - "20 , !- Rated Inlet Air Temperature in Heating Mode", - "0.002, !- Rated Air Flow Rate in Heating Mode", - "50 , !- Rated Leaving Water Temperature in Heating Mode", - "0.005, !- Rated Water Flow Rate in Heating Mode", - "-20, !- Minimum Outdoor Air Temperature in Heating Mode", - "25, !- Maximum Outdoor Air Temperature in Heating Mode", - ", !- Minimum Leaving Water Temperature Curve Name in Heating Mode", - ", !- Maximum Leaving Water Temperature Curve Name in Heating Mode", - "1.0, !- Sizing Factor for Heating", - "25, !- Rated Inlet Air Temperature in Cooling Mode", - "0.002, !- Rated Air Flow Rate in Cooling Mode", - "22 , !- Rated Leaving Water Temperature in Cooling Mode", - "0.005, !- Rated Water Flow Rate in Cooling Mode", - "18 , !- Minimum Outdoor Air Temperature in Cooling Mode", - "40, !- Maximum Outdoor Air Temperature in Cooling Mode", - ", !- Minimum Leaving Water Temperature Curve Name in Cooling Mode", - ", !- Maximum Leaving Water Temperature Curve Name in Cooling Mode", - "0.9, !- Sizing Factor for Cooling", - "Outdoor Air Inlet Node , !- Air Inlet Node Name", - "Outdoor Air Outlet Node, !- Air Outlet Node Name", - "Heating Coil Load Loop Intermediate Node, !- Hot Water Inlet Node Name", - "Heating Coil Load Loop Supply Outlet Node, !- Hot Water Outlet Node Name", - "Cooling Coil Load Loop Intermediate Node , !- Chilled Water Inlet Node Name", - "Cooling Coil Load Loop Supply Outlet Node , !- Chilled Water Outlet Node Name", - "10, !- Maximum Outdoor Dry Bulb Temperature For Defrost Operation", - "Timed, !- Heat Pump Defrost Control", - "0.2, !- Defrost Time Period Fraction", - "150, !- Resistive Defrost Heater Capacity", - ", !- Defrost Energy Input Ratio Function of Temperature Curve Name", - "1 , !- Compressor Multiplier", - "FixedSpeed , !- Control Type", - "100 , !- Crankcase Heater Capacity", - "EIRCurveFuncPLR, !- Crankcase Heater Capacity Function of Temperature Curve Name", - "20 , !- Maximum Ambient Temperature for Crankcase Heater Operation", - "1 , !- Number of Speeds for Heating", - "20000, !- Rated Heating Capacity at Speed 1", - "3, !- Rated COP for Heating at Speed 1", - "CapCurveFuncTemp, !- Normalized Heating Capacity Function of Temperature Curve Name at Speed 1", - "EIRCurveFuncTemp, !- Heating Energy Input Ratio Function of Temperature Curve Name at Speed 1", - "EIRCurveFuncPLR, !- Heating Energy Input Ratio Function of PLR Curve Name at Speed 1", - ", !- Rated Heating Capacity at Speed 2", - ", !- Rated COP for Heating at Speed 2", - ", !- Normalized Heating Capacity Function of Temperature Curve Name at Speed 2", - ", !- Heating Energy Input Ratio Function of Temperature Curve Name at Speed 2", - ", !- Heating Energy Input Ratio Function of PLR Curve Name at Speed 2", - ", !- Rated Heating Capacity at Speed 3", - ", !- Rated COP for Heating at Speed 3", - ", !- Normalized Heating Capacity Function of Temperature Curve Name at Speed 3", - ", !- Heating Energy Input Ratio Function of Temperature Curve Name at Speed 3", - ", !- Heating Energy Input Ratio Function of PLR Curve Name at Speed 3", - ", !- Rated Heating Capacity at Speed 4", - ", !- Rated COP for Heating at Speed 4", - ", !- Normalized Heating Capacity Function of Temperature Curve Name at Speed 4", - ", !- Heating Energy Input Ratio Function of Temperature Curve Name at Speed 4", - ", !- Heating Energy Input Ratio Function of PLR Curve Name at Speed 4", - ", !- Rated Heating Capacity at Speed 5", - ", !- Rated COP for Heating at Speed 5", - ", !- Normalized Heating Capacity Function of Temperature Curve Name at Speed 5", - ", !- Heating Energy Input Ratio Function of Temperature Curve Name at Speed 5", - ", !- Heating Energy Input Ratio Function of PLR Curve Name at Speed 5", - ", !- Booster Mode On Heating", - ", !- Rated Heating Capacity in Booster Mode", - ", !- Rated Heating COP in Booster Mode", - ", !- Normalized Heating Capacity Function of Temperature Curve Name in Booster Mode", - ", !- Heating Energy Input Ratio Function of Temperature Curve Name in Booster Mode", - ", !- Heating Energy Input Ratio Function of PLR Curve Name in Booster Mode", - "1, !- Number of Speeds for Cooling", - "20000, !- Rated Cooling Capacity at Speed 1", - "3, !- Rated COP for Cooling at Speed 1", - "CapCurveFuncTemp, !- Normalized Cooling Capacity Function of Temperature Curve Name at Speed 1", - "EIRCurveFuncTemp, !- Cooling Energy Input Ratio Function of Temperature Curve Name at Speed 1", - "EIRCurveFuncPLR, !- Cooling Energy Input Ratio Function of PLR Curve Name at Speed 1", - ", !- Rated Cooling Capacity at Speed 2", - ", !- Rated COP for Cooling at Speed 2", - ", !- Normalized Cooling Capacity Function of Temperature Curve Name at Speed 2", - ", !- Cooling Energy Input Ratio Function of Temperature Curve Name at Speed 2", - ", !- Cooling Energy Input Ratio Function of PLR Curve Name at Speed 2", - ", !- Rated Cooling Capacity at Speed 3", - ", !- Rated COP for Cooling at Speed 3", - ", !- Normalized Cooling Capacity Function of Temperature Curve Name at Speed 3", - ", !- Cooling Energy Input Ratio Function of Temperature Curve Name at Speed 3", - ", !- Cooling Energy Input Ratio Function of PLR Curve Name at Speed 3", - ", !- Rated Cooling Capacity at Speed 4", - ", !- Rated COP for Cooling at Speed 4", - ", !- Normalized Cooling Capacity Function of Temperature Curve Name at Speed 4", - ", !- Cooling Energy Input Ratio Function of Temperature Curve Name at Speed 4", - ", !- Cooling Energy Input Ratio Function of PLR Curve Name at Speed 4", - ", !- Rated Cooling Capacity at Speed 5", - ", !- Rated COP for Cooling at Speed 5", - ", !- Normalized Cooling Capacity Function of Temperature Curve Name at Speed 5", - ", !- Cooling Energy Input Ratio Function of Temperature Curve Name at Speed 5", - ", !- Cooling Energy Input Ratio Function of PLR Curve Name at Speed 5", - ", !- Booster Mode On Cooling", - ", !- Rated Cooling Capacity in Booster Mode", - ", !- Rated Cooling COP in Booster Mode", - ", !- Normalized Cooling Capacity Function of Temperature Curve Name in Booster Mode", - ", !- Cooling Energy Input Ratio Function of Temperature Curve Name in Booster Mode", - "; !- Cooling Energy Input Ratio Function of PLR Curve Name in Booster Mode", - - "Curve:Biquadratic,", - " CapCurveFuncTemp,", - " 1.0,", - " 0.0,", - " 0.0,", - " 0.0,", - " 0.0,", - " 0.0,", - " 5.0,", - " 10.0,", - " 24.0,", - " 35.0,", - " ,", - " ,", - " Temperature,", - " Temperature,", - " Dimensionless;", - "Curve:Biquadratic,", - " EIRCurveFuncTemp,", - " 1.0,", - " 0.0,", - " 0.0,", - " 1.0,", - " 0.0,", - " 0.0,", - " 5.0,", - " 10.0,", - " 24.0,", - " 35.0,", - " ,", - " ,", - " Temperature,", - " Temperature,", - " Dimensionless;", - "Curve:Quadratic,", - " EIRCurveFuncPLR,", - " 1.0,", - " 0.0,", - " 0.0,", - " 0.0,", - " 1.0;"}); - ASSERT_TRUE(process_idf(idf_objects)); - state->init_state(*state); - - // set up the plant loops - // first the load side - state->dataPlnt->TotNumLoops = 5; - state->dataPlnt->PlantLoop.allocate(5); - - state->dataPlnt->PlantLoop(1).FluidName = "WATER"; - state->dataPlnt->PlantLoop(1).glycol = Fluid::GetWater(*state); - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).TotalBranches = 1; - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch.allocate(1); - 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 &AWHPPlantLoadSideLoop = state->dataPlnt->PlantLoop(1); - auto &AWHPPlantLoadSideComp = state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp(1); - AWHPPlantLoadSideComp.Type = DataPlant::PlantEquipmentType::HeatPumpAirToWaterCooling; - // then the source side - - state->dataPlnt->PlantLoop(2).FluidName = "Air"; - state->dataPlnt->PlantLoop(2).glycol = Fluid::GetWater(*state); - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).TotalBranches = 1; - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch.allocate(1); - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).TotalComponents = 1; - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp.allocate(1); - - // call the factory with a valid name to trigger reading inputs - DataPlant::PlantEquipmentType equipType = DataPlant::PlantEquipmentType::HeatPumpAirToWaterCooling; - HeatPumpAirToWater::factory(*state, equipType, "test_AWHP"); - - state->dataPlnt->PlantLoop(3).FluidName = "WATER"; - state->dataPlnt->PlantLoop(3).glycol = Fluid::GetWater(*state); - state->dataPlnt->PlantLoop(3).LoopSide(DataPlant::LoopSideLocation::Supply).TotalBranches = 1; - state->dataPlnt->PlantLoop(3).LoopSide(DataPlant::LoopSideLocation::Supply).Branch.allocate(1); - state->dataPlnt->PlantLoop(3).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).TotalComponents = 1; - state->dataPlnt->PlantLoop(3).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp.allocate(1); - auto &PLHPPlantLoadSideLoop = state->dataPlnt->PlantLoop(1); - auto &PLHPPlantLoadSideComp = state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp(1); - PLHPPlantLoadSideComp.Type = DataPlant::PlantEquipmentType::HeatPumpAirToWaterCooling; - // then the source side - - state->dataPlnt->PlantLoop(4).FluidName = "Air"; - state->dataPlnt->PlantLoop(4).glycol = Fluid::GetWater(*state); - state->dataPlnt->PlantLoop(4).LoopSide(DataPlant::LoopSideLocation::Demand).TotalBranches = 1; - state->dataPlnt->PlantLoop(4).LoopSide(DataPlant::LoopSideLocation::Demand).Branch.allocate(1); - state->dataPlnt->PlantLoop(4).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).TotalComponents = 1; - state->dataPlnt->PlantLoop(4).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp.allocate(1); - - auto &PLHPPlantLoadSourceComp = state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp(1); - PLHPPlantLoadSourceComp.Type = DataPlant::PlantEquipmentType::HeatPumpEIRCooling; - - state->dataPlnt->PlantLoop(5).FluidName = "WATER"; - state->dataPlnt->PlantLoop(5).glycol = Fluid::GetWater(*state); - state->dataPlnt->PlantLoop(5).LoopSide(DataPlant::LoopSideLocation::Supply).TotalBranches = 1; - state->dataPlnt->PlantLoop(5).LoopSide(DataPlant::LoopSideLocation::Supply).Branch.allocate(1); - state->dataPlnt->PlantLoop(5).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).TotalComponents = 1; - state->dataPlnt->PlantLoop(5).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp.allocate(1); - auto &AWHPHeatPlantLoadSideComp = state->dataPlnt->PlantLoop(5).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp(1); - AWHPHeatPlantLoadSideComp.Type = DataPlant::PlantEquipmentType::HeatPumpAirToWaterHeating; - - // call the factory with a valid name to trigger reading inputs - EIRPlantLoopHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpEIRCooling, "hp cooling side"); - - // verify the size of the vector and the processed condition - EXPECT_EQ(2u, state->dataHeatPumpAirToWater->heatPumps.size()); - EXPECT_EQ(2u, state->dataEIRPlantLoopHeatPump->heatPumps.size()); - - // for now we know the order is maintained, so get each heat pump object - HeatPumpAirToWater *thisCoolingAWHP = &state->dataHeatPumpAirToWater->heatPumps[0]; - EIRPlantLoopHeatPump *thisCoolingPLHP = &state->dataEIRPlantLoopHeatPump->heatPumps[0]; - HeatPumpAirToWater *thisHeatingAWHP = &state->dataHeatPumpAirToWater->heatPumps[1]; - thisCoolingAWHP->companionHeatPumpCoil = thisHeatingAWHP; - thisHeatingAWHP->companionHeatPumpCoil = thisCoolingAWHP; - - // do a little setup here - thisCoolingAWHP->loadSidePlantLoc.loopNum = 1; - thisCoolingAWHP->loadSidePlantLoc.loopSideNum = DataPlant::LoopSideLocation::Supply; - thisCoolingAWHP->loadSidePlantLoc.branchNum = 1; - thisCoolingAWHP->loadSidePlantLoc.compNum = 1; - thisHeatingAWHP->loadSidePlantLoc.loopNum = 5; - thisHeatingAWHP->loadSidePlantLoc.loopSideNum = DataPlant::LoopSideLocation::Supply; - thisHeatingAWHP->loadSidePlantLoc.branchNum = 1; - thisHeatingAWHP->loadSidePlantLoc.compNum = 1; - PlantUtilities::SetPlantLocationLinks(*state, thisCoolingAWHP->loadSidePlantLoc); - thisCoolingAWHP->loadSideNodes.outlet = 1; - thisCoolingAWHP->sourceSidePlantLoc.loopNum = 2; - PlantUtilities::SetPlantLocationLinks(*state, thisCoolingAWHP->sourceSidePlantLoc); - - thisCoolingPLHP->loadSidePlantLoc.loopNum = 1; - thisCoolingPLHP->loadSidePlantLoc.loopSideNum = DataPlant::LoopSideLocation::Supply; - thisCoolingPLHP->loadSidePlantLoc.branchNum = 1; - thisCoolingPLHP->loadSidePlantLoc.compNum = 1; - PlantUtilities::SetPlantLocationLinks(*state, thisCoolingPLHP->loadSidePlantLoc); - thisCoolingPLHP->loadSideNodes.outlet = 1; - thisCoolingPLHP->sourceSidePlantLoc.loopNum = 2; - PlantUtilities::SetPlantLocationLinks(*state, thisCoolingPLHP->sourceSidePlantLoc); - - // the factory would've called GetOnlySingleNode for the in/out pairs on the PLHP, add another one for the loop - // outlet setpoint node - state->dataLoopNodes->Node.allocate(10); - AWHPPlantLoadSideLoop.TempSetPointNodeNum = 5; - PLHPPlantLoadSideLoop.TempSetPointNodeNum = 10; - - // set up the plant setpoint conditions and test for single setpoint operation - AWHPPlantLoadSideLoop.LoopDemandCalcScheme = DataPlant::LoopDemandCalcScheme::SingleSetPoint; - AWHPPlantLoadSideComp.CurOpSchemeType = DataPlant::OpScheme::CompSetPtBased; - state->dataLoopNodes->Node(thisCoolingAWHP->loadSideNodes.outlet).TempSetPoint = 3.141; - state->dataLoopNodes->Node(5).TempSetPoint = 2.718; - AWHPPlantLoadSideComp.CurOpSchemeType = DataPlant::OpScheme::CoolingRB; - - // test for dual setpoint operation - AWHPPlantLoadSideLoop.LoopDemandCalcScheme = DataPlant::LoopDemandCalcScheme::DualSetPointDeadBand; - AWHPPlantLoadSideComp.CurOpSchemeType = DataPlant::OpScheme::CompSetPtBased; - state->dataLoopNodes->Node(thisCoolingAWHP->loadSideNodes.outlet).TempSetPointHi = 6.282; - state->dataLoopNodes->Node(5).TempSetPointHi = 5.436; - AWHPPlantLoadSideComp.CurOpSchemeType = DataPlant::OpScheme::CoolingRB; - - // set up the plant setpoint conditions and test for single setpoint operation - PLHPPlantLoadSideLoop.LoopDemandCalcScheme = DataPlant::LoopDemandCalcScheme::SingleSetPoint; - PLHPPlantLoadSideComp.CurOpSchemeType = DataPlant::OpScheme::CompSetPtBased; - state->dataLoopNodes->Node(thisCoolingPLHP->loadSideNodes.outlet).TempSetPoint = 3.141; - state->dataLoopNodes->Node(10).TempSetPoint = 2.718; - PLHPPlantLoadSideComp.CurOpSchemeType = DataPlant::OpScheme::CoolingRB; - - // test for dual setpoint operation - PLHPPlantLoadSideLoop.LoopDemandCalcScheme = DataPlant::LoopDemandCalcScheme::DualSetPointDeadBand; - PLHPPlantLoadSideComp.CurOpSchemeType = DataPlant::OpScheme::CompSetPtBased; - state->dataLoopNodes->Node(thisCoolingAWHP->loadSideNodes.outlet).TempSetPointHi = 6.282; - state->dataLoopNodes->Node(10).TempSetPointHi = 5.436; - PLHPPlantLoadSideComp.CurOpSchemeType = DataPlant::OpScheme::CoolingRB; - - state->dataHVACGlobal->TimeStepSys = 60; - state->dataHVACGlobal->TimeStepSysSec = state->dataHVACGlobal->TimeStepSys * Constant::rSecsInHour; - - Real64 curLoad = -10000; - - thisCoolingAWHP->loadSideMassFlowRate = 0.3; - thisCoolingAWHP->sourceSideMassFlowRate = 0.8; - thisCoolingAWHP->loadSideInletTemp = 20; - thisCoolingAWHP->sourceSideInletTemp = 20; - thisCoolingAWHP->doPhysics(*state, curLoad); - - thisCoolingPLHP->loadSideMassFlowRate = 0.3; - thisCoolingPLHP->sourceSideMassFlowRate = 0.8; - thisCoolingPLHP->loadSideInletTemp = 20; - thisCoolingPLHP->sourceSideInletTemp = 20; - thisCoolingPLHP->doPhysics(*state, curLoad); - - EXPECT_NEAR(thisCoolingAWHP->loadSideOutletTemp, thisCoolingPLHP->loadSideOutletTemp, 0.1); - // this value is from the Test_DoPhysics with WaterSource changed to AirSource - EXPECT_NEAR(thisCoolingAWHP->sourceSideOutletTemp, thisCoolingPLHP->sourceSideOutletTemp, 0.1); - // fixme: add cases with 2 or more speed levels -} - -TEST_F(EnergyPlusFixture, CoolingMetering) -{ - std::string const idf_objects = delimited_string({"HeatPump:PlantLoop:EIR:Cooling,", - " hp cooling side,", - " node 1,", - " node 2,", - " WaterSource,", - " node 3,", - " node 4,", - " ,", - " ,", - " ,", - " 0.0001,", - " 0.0001,", - " ,", - " 1000,", - " 3.14,", - " ,", - " dummyCurve,", - " dummyCurve,", - " dummyCurve;", - "Curve:Linear,", - " dummyCurve,", - " 0.95,", - " 0,", - " 1,", - " 1;"}); - ASSERT_TRUE(process_idf(idf_objects)); - state->init_state(*state); - - // set up the plant loops - // first the load side - state->dataPlnt->TotNumLoops = 2; - state->dataPlnt->PlantLoop.allocate(2); - - state->dataPlnt->PlantLoop(1).FluidName = "WATER"; - state->dataPlnt->PlantLoop(1).glycol = Fluid::GetWater(*state); - state->dataPlnt->PlantLoop(1).LoopDemandCalcScheme = DataPlant::LoopDemandCalcScheme::SingleSetPoint; - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).TotalBranches = 1; - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch.allocate(1); - 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 &PLHPPlantLoadSideComp = state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp(1); - PLHPPlantLoadSideComp.Type = DataPlant::PlantEquipmentType::HeatPumpEIRCooling; - PLHPPlantLoadSideComp.CurOpSchemeType = DataPlant::OpScheme::CompSetPtBased; - // then the source side - - state->dataPlnt->PlantLoop(2).FluidName = "WATER"; - state->dataPlnt->PlantLoop(2).glycol = Fluid::GetWater(*state); - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).TotalBranches = 1; - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch.allocate(1); - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).TotalComponents = 1; - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp.allocate(1); - auto &PLHPPlantLoadSourceComp = state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp(1); - PLHPPlantLoadSourceComp.Type = DataPlant::PlantEquipmentType::HeatPumpEIRCooling; - - // the init call expects a "from" calling point - PlantLocation myLoadLocation = PlantLocation(1, DataPlant::LoopSideLocation::Supply, 1, 1); - - // call the factory with a valid name to trigger reading inputs - EIRPlantLoopHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpEIRCooling, "HP COOLING SIDE"); - - // verify the size of the vector and the processed condition - EXPECT_EQ(1u, state->dataEIRPlantLoopHeatPump->heatPumps.size()); - - // for now we know the order is maintained, so get each heat pump object - EIRPlantLoopHeatPump *thisCoolingPLHP = &state->dataEIRPlantLoopHeatPump->heatPumps[0]; - - // do a bit of extra wiring up to the plant - PLHPPlantLoadSideComp.Name = thisCoolingPLHP->name; - PLHPPlantLoadSideComp.NodeNumIn = thisCoolingPLHP->loadSideNodes.inlet; - PLHPPlantLoadSourceComp.Name = thisCoolingPLHP->name; - PLHPPlantLoadSourceComp.NodeNumIn = thisCoolingPLHP->sourceSideNodes.inlet; - - // call for all initialization - state->dataGlobal->BeginEnvrnFlag = true; - state->dataPlnt->PlantFirstSizesOkayToFinalize = true; - thisCoolingPLHP->onInitLoopEquip(*state, myLoadLocation); - - int NumFound; - std::string TypeOfComp = "HeatPump:PlantLoop:EIR:Cooling"; - std::string NameOfComp = thisCoolingPLHP->name; - int NumVariables = GetNumMeteredVariables(*state, TypeOfComp, NameOfComp); - Array1D meteredVars(NumVariables); // Variable Types (1=integer, 2=real, 3=meter) - - NumFound = GetMeteredVariables(*state, NameOfComp, meteredVars); - - EXPECT_EQ(2, NumFound); - EXPECT_ENUM_EQ(meteredVars(1).resource, Constant::eResource::EnergyTransfer); // ENERGYTRANSFER - EXPECT_ENUM_EQ(meteredVars(1).endUseCat, OutputProcessor::EndUseCat::Invalid); - EXPECT_ENUM_EQ(meteredVars(1).group, OutputProcessor::Group::Plant); - EXPECT_ENUM_EQ(meteredVars(2).resource, Constant::eResource::Electricity); // Electric - EXPECT_ENUM_EQ(meteredVars(2).endUseCat, OutputProcessor::EndUseCat::Cooling); - EXPECT_ENUM_EQ(meteredVars(2).group, OutputProcessor::Group::Plant); -} - -TEST_F(EnergyPlusFixture, HeatingMetering) -{ - std::string const idf_objects = delimited_string({"HeatPump:PlantLoop:EIR:Heating,", - " hp heating side,", - " node 1,", - " node 2,", - " WaterSource,", - " node 3,", - " node 4,", - " ,", - " ,", - " ,", - " 0.0001,", - " 0.0001,", - " ,", - " 1000,", - " 3.14,", - " ,", - " dummyCurve,", - " dummyCurve,", - " dummyCurve;", - "Curve:Linear,", - " dummyCurve,", - " 0.95,", - " 0,", - " 1,", - " 1;"}); - ASSERT_TRUE(process_idf(idf_objects)); - state->init_state(*state); - - // set up the plant loops - // first the load side - state->dataPlnt->TotNumLoops = 2; - state->dataPlnt->PlantLoop.allocate(2); - - state->dataPlnt->PlantLoop(1).FluidName = "WATER"; - state->dataPlnt->PlantLoop(1).glycol = Fluid::GetWater(*state); - state->dataPlnt->PlantLoop(1).LoopDemandCalcScheme = DataPlant::LoopDemandCalcScheme::SingleSetPoint; - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).TotalBranches = 1; - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch.allocate(1); - 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 &PLHPPlantLoadSideComp = state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp(1); - PLHPPlantLoadSideComp.Type = DataPlant::PlantEquipmentType::HeatPumpEIRHeating; - PLHPPlantLoadSideComp.CurOpSchemeType = DataPlant::OpScheme::CompSetPtBased; - // then the source side - - state->dataPlnt->PlantLoop(2).FluidName = "WATER"; - state->dataPlnt->PlantLoop(2).glycol = Fluid::GetWater(*state); - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).TotalBranches = 1; - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch.allocate(1); - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).TotalComponents = 1; - state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp.allocate(1); - auto &PLHPPlantLoadSourceComp = state->dataPlnt->PlantLoop(2).LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp(1); - PLHPPlantLoadSourceComp.Type = DataPlant::PlantEquipmentType::HeatPumpEIRHeating; - - // the init call expects a "from" calling point - PlantLocation myLoadLocation = PlantLocation(1, DataPlant::LoopSideLocation::Supply, 1, 1); - - // call the factory with a valid name to trigger reading inputs - EIRPlantLoopHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpEIRHeating, "HP HEATING SIDE"); - - // verify the size of the vector and the processed condition - EXPECT_EQ(1u, state->dataEIRPlantLoopHeatPump->heatPumps.size()); - - // for now we know the order is maintained, so get each heat pump object - EIRPlantLoopHeatPump *thisHeatingPLHP = &state->dataEIRPlantLoopHeatPump->heatPumps[0]; - - // do a bit of extra wiring up to the plant - PLHPPlantLoadSideComp.Name = thisHeatingPLHP->name; - PLHPPlantLoadSideComp.NodeNumIn = thisHeatingPLHP->loadSideNodes.inlet; - PLHPPlantLoadSourceComp.Name = thisHeatingPLHP->name; - PLHPPlantLoadSourceComp.NodeNumIn = thisHeatingPLHP->sourceSideNodes.inlet; - - // call for all initialization - state->dataGlobal->BeginEnvrnFlag = true; - state->dataPlnt->PlantFirstSizesOkayToFinalize = true; - thisHeatingPLHP->onInitLoopEquip(*state, myLoadLocation); - - int NumFound; - - std::string TypeOfComp = "HeatPump:PlantLoop:EIR:Heating"; - std::string NameOfComp = thisHeatingPLHP->name; - int NumVariables = GetNumMeteredVariables(*state, TypeOfComp, NameOfComp); - - Array1D_int VarIndexes(NumVariables); // Variable Numbers - Array1D meteredVars(NumVariables); // Variable Types (1=integer, 2=real, 3=meter) - - NumFound = GetMeteredVariables(*state, NameOfComp, meteredVars); - - EXPECT_EQ(2, NumFound); - EXPECT_ENUM_EQ(meteredVars(1).resource, Constant::eResource::EnergyTransfer); // ENERGYTRANSFER - EXPECT_ENUM_EQ(meteredVars(1).endUseCat, OutputProcessor::EndUseCat::Invalid); - EXPECT_ENUM_EQ(meteredVars(1).group, OutputProcessor::Group::Plant); - EXPECT_ENUM_EQ(meteredVars(2).resource, Constant::eResource::Electricity); // Electric - EXPECT_ENUM_EQ(meteredVars(2).endUseCat, OutputProcessor::EndUseCat::Heating); - EXPECT_ENUM_EQ(meteredVars(2).group, OutputProcessor::Group::Plant); -} - -TEST_F(EnergyPlusFixture, TestOperatingFlowRates_FullyAutosized_AirSource) -{ - std::string const idf_objects = - delimited_string({"HeatPump:PlantLoop:EIR:Cooling,", - " hp cooling side,", - " node 1,", - " node 2,", - " AirSource,", - " node 3,", - " node 4,", - " ,", - " ,", - " ,", - " Autosize,", - " Autosize,", - " ,", - " Autosize,", - " 3.14,", - " ,", - " dummyCurve,", - " dummyCurve,", - " dummyCurve,", - " ,", - " ,", - " ,", - " ,", - " ,", - " ,", - " ,", - " ,", - " ,", - " ,", - " ThermoCapFracCurve;", - - "Curve:Linear, ThermoCapFracCurve, 0.0, 0.06, 0.0, 10.0, 0.0, 1.0, Dimensionless, Dimensionless;", - "Curve:Linear,", - " dummyCurve,", - " 1,", - " 0,", - " 1,", - " 1;"}); - ASSERT_TRUE(process_idf(idf_objects)); - state->init_state(*state); - - bool firstHVACIteration = true; - // set up the plant loops - // first the load side - state->dataPlnt->TotNumLoops = 1; - state->dataPlnt->PlantLoop.allocate(1); - - state->dataPlnt->PlantLoop(1).FluidName = "WATER"; - state->dataPlnt->PlantLoop(1).glycol = Fluid::GetWater(*state); - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).TotalBranches = 1; - state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch.allocate(1); - 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 &PLHPPlantLoadSideComp = state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp(1); - PLHPPlantLoadSideComp.Type = DataPlant::PlantEquipmentType::HeatPumpEIRCooling; - - // the init call expects a "from" calling point - PlantLocation myLocation = PlantLocation(1, DataPlant::LoopSideLocation::Supply, 1, 1); - - // call the factory with a valid name to trigger reading inputs - EIRPlantLoopHeatPump::factory(*state, DataPlant::PlantEquipmentType::HeatPumpEIRCooling, "HP COOLING SIDE"); - - // verify the size of the vector and the processed condition - EXPECT_EQ(1u, state->dataEIRPlantLoopHeatPump->heatPumps.size()); - - // for now we know the order is maintained, so get each heat pump object - EIRPlantLoopHeatPump *thisCoolingPLHP = &state->dataEIRPlantLoopHeatPump->heatPumps[0]; - - // do a bit of extra wiring up to the plant - PLHPPlantLoadSideComp.Name = thisCoolingPLHP->name; - PLHPPlantLoadSideComp.NodeNumIn = thisCoolingPLHP->loadSideNodes.inlet; - - state->dataSize->PlantSizData.allocate(1); - state->dataSize->PlantSizData(1).DesVolFlowRate = 0.010; - state->dataSize->PlantSizData(1).DeltaT = 1.0; - - // call for all initialization - state->dataGlobal->BeginEnvrnFlag = true; - thisCoolingPLHP->onInitLoopEquip(*state, myLocation); - - state->dataPlnt->PlantFinalSizesOkayToReport = true; - state->dataPlnt->PlantFirstSizesOkayToReport = true; - state->dataPlnt->PlantFirstSizesOkayToFinalize = true; - - // assign the plant sizing data - state->dataPlnt->PlantLoop(1).PlantSizNum = 1; + // Component flow control type + auto &comp = DataPlant::CompData::getPlantComponent(*state, thisCoolingPLHP->loadSidePlantLoc); + comp.FlowCtrl = DataBranchAirLoopPlant::ControlType::SeriesActive; // call with run flag ON, flow locked at nonzero both state->dataPlnt->PlantLoop(1).LoopSide(DataPlant::LoopSideLocation::Supply).FlowLock = DataPlant::FlowLock::Locked; From 8de2a4fa1626a3533b608e0f238e18cf9d51cbd6 Mon Sep 17 00:00:00 2001 From: lymereJ Date: Tue, 26 May 2026 12:57:04 -0700 Subject: [PATCH 5/7] Add file to CMakeLists for PlantLoopHeatPump_EIR_AirSource_Parallel.idf test. --- testfiles/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/testfiles/CMakeLists.txt b/testfiles/CMakeLists.txt index 51460f3b727..b37189c05d3 100644 --- a/testfiles/CMakeLists.txt +++ b/testfiles/CMakeLists.txt @@ -465,6 +465,7 @@ add_simulation_test(IDF_FILE PlantLoopChainDeadband.idf EPW_FILE USA_IL_Chicago- add_simulation_test(IDF_FILE PlantLoopChainDualDeadband.idf EPW_FILE USA_IL_Chicago-OHare.Intl.AP.725300_TMY3.epw) add_simulation_test(IDF_FILE PlantLoopChainHeating.idf EPW_FILE USA_IL_Chicago-OHare.Intl.AP.725300_TMY3.epw) add_simulation_test(IDF_FILE PlantLoopHeatPump_EIR_AirSource.idf EPW_FILE USA_IL_Chicago-OHare.Intl.AP.725300_TMY3.epw) +add_simulation_test(IDF_FILE PlantLoopHeatPump_EIR_AirSource_Parallel.idf EPW_FILE USA_IL_Chicago-OHare.Intl.AP.725300_TMY3.epw) add_simulation_test(IDF_FILE PlantLoopHeatPump_EIR_AirSource_BufferTank.idf EPW_FILE USA_IL_Chicago-OHare.Intl.AP.725300_TMY3.epw) add_simulation_test(IDF_FILE PlantLoopHeatPump_EIR_WaterSource.idf EPW_FILE USA_IL_Chicago-OHare.Intl.AP.725300_TMY3.epw) add_simulation_test(IDF_FILE PlantLoopHeatPump_Fuel-Fired.idf EPW_FILE USA_IL_Chicago-OHare.Intl.AP.725300_TMY3.epw) From d5ec12de59c5bc0ac970a9dacfdc4f0282d86684 Mon Sep 17 00:00:00 2001 From: lymereJ Date: Tue, 26 May 2026 13:40:13 -0700 Subject: [PATCH 6/7] Exclude SP controls. --- src/EnergyPlus/PlantLoopHeatPumpEIR.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/EnergyPlus/PlantLoopHeatPumpEIR.cc b/src/EnergyPlus/PlantLoopHeatPumpEIR.cc index 36f3ce42327..df5766741ac 100644 --- a/src/EnergyPlus/PlantLoopHeatPumpEIR.cc +++ b/src/EnergyPlus/PlantLoopHeatPumpEIR.cc @@ -301,7 +301,7 @@ void EIRPlantLoopHeatPump::setOperatingFlowRatesASHP(EnergyPlusData &state, bool // or if parallel configuration and no load, the unit is turned off auto &comp = DataPlant::CompData::getPlantComponent(state, this->loadSidePlantLoc); if (((this->minSourceTempLimit > this->sourceSideInletTemp || this->maxSourceTempLimit < this->sourceSideInletTemp) || - (std::abs(currentLoad) < HVAC::SmallLoad && comp.FlowCtrl != DataBranchAirLoopPlant::ControlType::SeriesActive)) && + (std::abs(currentLoad) < HVAC::SmallLoad && this->sysControlType == ControlType::Load && comp.FlowCtrl != DataBranchAirLoopPlant::ControlType::SeriesActive)) && !this->heatRecoveryIsActive) { this->loadSideMassFlowRate = 0.0; this->sourceSideMassFlowRate = 0.0; From a5e4b9c29f99ca32cd1d9d86ca9d6e332772c71c Mon Sep 17 00:00:00 2001 From: lymereJ Date: Tue, 26 May 2026 15:25:39 -0700 Subject: [PATCH 7/7] Formatting. --- src/EnergyPlus/PlantLoopHeatPumpEIR.cc | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/EnergyPlus/PlantLoopHeatPumpEIR.cc b/src/EnergyPlus/PlantLoopHeatPumpEIR.cc index df5766741ac..e84d4fd7b77 100644 --- a/src/EnergyPlus/PlantLoopHeatPumpEIR.cc +++ b/src/EnergyPlus/PlantLoopHeatPumpEIR.cc @@ -298,11 +298,18 @@ void EIRPlantLoopHeatPump::setOperatingFlowRatesASHP(EnergyPlusData &state, bool // Set flows if the heat pump is running } else { // the heat pump must run // apply min/max operating limits based on source side entering fluid temperature - // or if parallel configuration and no load, the unit is turned off + // or if parallel configuration and no/very small load, the unit is turned off + bool tempOutOfRange = this->minSourceTempLimit > this->sourceSideInletTemp || this->maxSourceTempLimit < this->sourceSideInletTemp; auto &comp = DataPlant::CompData::getPlantComponent(state, this->loadSidePlantLoc); - if (((this->minSourceTempLimit > this->sourceSideInletTemp || this->maxSourceTempLimit < this->sourceSideInletTemp) || - (std::abs(currentLoad) < HVAC::SmallLoad && this->sysControlType == ControlType::Load && comp.FlowCtrl != DataBranchAirLoopPlant::ControlType::SeriesActive)) && - !this->heatRecoveryIsActive) { + bool loadIndicator = false; + if (this->sysControlType == ControlType::Setpoint) { + Real64 leavingSetpoint = this->getLoadSideOutletSetPointTemp(state); + loadIndicator = std::abs(leavingSetpoint - this->loadSideInletTemp) < HVAC::SmallTempDiff; + } else { + loadIndicator = std::abs(currentLoad) < HVAC::SmallLoad; + } + bool lowLoadCondition = loadIndicator && comp.FlowCtrl != DataBranchAirLoopPlant::ControlType::SeriesActive; + if ((tempOutOfRange || lowLoadCondition) && !this->heatRecoveryAvailable) { this->loadSideMassFlowRate = 0.0; this->sourceSideMassFlowRate = 0.0; this->running = false;