diff --git a/doc/engineering-reference/src/building-system-simulation-system-manager/plant-condenser-loops.tex b/doc/engineering-reference/src/building-system-simulation-system-manager/plant-condenser-loops.tex index 027000eb989..626df11eea8 100644 --- a/doc/engineering-reference/src/building-system-simulation-system-manager/plant-condenser-loops.tex +++ b/doc/engineering-reference/src/building-system-simulation-system-manager/plant-condenser-loops.tex @@ -666,6 +666,8 @@ \subsubsection{Common Pipe}\label{common-pipe} The flow at the inlet node of the half loop is equal to the flow at the outlet node of the half loop. \item The pumps can have different schedules and any loop can be shut off when the other loop is still running. +\item + If the primary-side, or supply-side, inlet pump is a variable speed pump, the common pipe model accepts it. For CommonPipe, the primary flow remains governed by the pump and loop controls; for Two-Way CommonPipe, the solver can request primary flow through the pump while resolving the interface balances. \end{itemize} \begin{figure}[hbtp] % fig 125 @@ -709,6 +711,8 @@ \subsubsection{Two-Way Common Pipe}\label{two-way-common-pipe} The mass flow rate at the Secondary Side Outlet Node is always equal to the mass flow rate at the Secondary Side Inlet Node. \item Only one additional node, either primary-side inlet or secondary-side inlet, (along with the primary-side/supply-side outlet node) can be controlled. The system of equations that describe the loop interface will be under specified if both the Primary and Secondary Inlet nodes have to be controlled. +\item + If the primary-side, or supply-side, inlet pump is a variable speed pump, the Two-Way Common Pipe solver can request primary flow through the pump as part of resolving the interface balances. The temperature control is still resolved by the common pipe flow balances using the available primary and secondary flows. \end{itemize} Figure~\ref{fig:schematic-of-a-two-way-common-pipe-used-in} shows a schematic of the Two-Way Common Pipe. There are two common pipe legs, shown as broken lines, allow for some recirculation at the half loop level.~ The model allows for common pipe flow in either or both directions. The model determines flow rates in the common pipes and temperatures at nodes based on the following: diff --git a/doc/input-output-reference/src/overview/group-plant-condenser-loops.tex b/doc/input-output-reference/src/overview/group-plant-condenser-loops.tex index 1acbcef2188..c055147d4d3 100644 --- a/doc/input-output-reference/src/overview/group-plant-condenser-loops.tex +++ b/doc/input-output-reference/src/overview/group-plant-condenser-loops.tex @@ -121,6 +121,8 @@ \subsubsection{Inputs}\label{inputs-035} This field specifies a primary-secondary plant loop simulation. When a common pipe option is specified, the plant side of the loop is the primary loop and the demand side of the loop is the secondary loop, and a pump object must be placed on the demand side inlet branch. The three options are ``\textbf{None}'', ``\textbf{CommonPipe}'' and ``\textbf{TwoWayCommonPipe}''. ``\textbf{None}'' means that there is no secondary loop and the plant loop is modeled as a single primary loop. ``\textbf{CommonPipe}'' means that the common pipe interface does not attempt any temperature control, it only satisfies secondary (demand side) flow requests. ``\textbf{TwoWayCommonPipe}'' allows control of the secondary (demand side) inlet temperature or the primary (plant side) inlet temperature by placing a setpoint on the corresponding node. If this field is left blank, it will default to ''None''. If the field is set to ``None'' and the program finds a pump on the demand side it will result in a fatal error.~ If the field is set to ``CommonPipe'' or ``TwoWayCommonPipe'' and the program does not find a pump on the demand side it will result in a fatal error. The common pipe simulation is currently limited to simulating loop pumps, i.e.~each pump should be placed on the inlet branch of the plant side or demand side of the loop. +If a variable speed pump is placed on the plant side inlet branch with \textbf{CommonPipe}, the primary flow remains governed by the pump and loop flow controls. With \textbf{TwoWayCommonPipe}, the common pipe solver can request primary flow through a variable speed plant-side inlet pump when resolving the primary inlet temperature balance. + \paragraph{Field: Pressure Simulation Type}\label{field-pressure-simulation-type} This field lets user to choose if this plant loop will be involved in a pressure drop calculation.~ This requires that at least one branch on the loop have pressure drop data.~ If not, an error will be thrown due to the input mismatch.~ Currently there are two pressure drop simulation types: ``PumpPowerCorrection'' and ``LoopFlowCorrection''.~ In both of these methods, branch pressure drop data is used to calculate a dynamic loop pressure drop and is used to update pumping power accordingly.~ The flow correction method allows the user to enter information regarding the pressure curve of a constant speed pump so that the simulation can dynamically resolve the pressure vs.~flow relationship in the plant loop.~ This is limited to constant speed pumps, as the variable speed pumps are expected to resolve as if it were controlled by a variable drive, so that they can inherently meet any pressure and flow demand required by the loop.~ This is also limited to ``loop pumps'', where there is a single pump on the plant loop. Common pipe simulations and ``branch pump'' simulations are not compatible with this level of pressure simulation.~ See the documentation for the \hyperref[pumpconstantspeed]{Pump:ConstantSpeed} in order to determine required inputs for this pressure simulation method to be performed.~ In the pressure drop system, parallel flow rates are not resolved (to match the pressure drop for the parallel system).~ Enhancements to this calculation are planned which will allow parallel branch flow resolution.~ See the Engineering Reference document for more information on how this works. diff --git a/idd/Energy+.idd.in b/idd/Energy+.idd.in index 95b61433b29..011f54569c1 100644 --- a/idd/Energy+.idd.in +++ b/idd/Energy+.idd.in @@ -83467,6 +83467,11 @@ PlantLoop, \note TwoWayCommonPipe = Primary-secondary with control of secondary supply temperature or \note primary return temperature (requires a setpoint be placed on the \note plant side or demand side inlet node). + \note If a variable speed pump is placed on the plant side inlet branch with + \note CommonPipe or TwoWayCommonPipe, EnergyPlus accepts it. For + \note CommonPipe, the primary flow remains governed by the pump and loop + \note controls. For TwoWayCommonPipe, the solver can request primary flow + \note through the pump as part of resolving the common pipe balance. \type choice \key CommonPipe \key TwoWayCommonPipe diff --git a/src/EnergyPlus/HVACInterfaceManager.cc b/src/EnergyPlus/HVACInterfaceManager.cc index 629fb5aa24c..2a24fe646fe 100644 --- a/src/EnergyPlus/HVACInterfaceManager.cc +++ b/src/EnergyPlus/HVACInterfaceManager.cc @@ -839,9 +839,14 @@ void UpdateCommonPipe(EnergyPlusData &state, ManageSingleCommonPipe(state, LoopNum, TankOutletLoopSide, TankAverageTemp, MixedOutletTemp); // 2-way (controlled) common pipe simulation } else if (CommonPipeType == DataPlant::CommonPipeType::TwoWay) { - PlantLocation TankOutletPlantLoc = {LoopNum, TankOutletLoopSide, 0, 0}; + if (!state.dataHVACInterfaceMgr->CommonPipeSetupFinished) { + SetupCommonPipes(state); + } + auto const &plantCommonPipe = state.dataHVACInterfaceMgr->PlantCommonPipe(LoopNum); + auto const &tankOutletPlantLoc = (TankOutletLoopSide == DataPlant::LoopSideLocation::Supply) ? plantCommonPipe.SupplySideInletPlantLoc + : plantCommonPipe.DemandSideInletPlantLoc; - ManageTwoWayCommonPipe(state, TankOutletPlantLoc, TankAverageTemp); + ManageTwoWayCommonPipe(state, tankOutletPlantLoc, TankAverageTemp); MixedOutletTemp = state.dataLoopNodes->Node(TankOutletNode).Temp; } @@ -1010,6 +1015,7 @@ void ManageTwoWayCommonPipe(EnergyPlusData &state, PlantLocation const &plantLoc auto &plantCommonPipe(state.dataHVACInterfaceMgr->PlantCommonPipe(plantLoc.loopNum)); auto &thisPlantLoop = state.dataPlnt->PlantLoop(plantLoc.loopNum); + bool const canRequestPrimaryInletFlow = plantCommonPipe.SupplySideInletPlantLoc.side->FlowLock != DataPlant::FlowLock::Locked; // fill local node indexes int const NodeNumPriIn = thisPlantLoop.LoopSide(DataPlant::LoopSideLocation::Supply).NodeNumIn; @@ -1117,19 +1123,23 @@ void ManageTwoWayCommonPipe(EnergyPlusData &state, PlantLocation const &plantLoc // eq. 3 if ((plantCommonPipe.SupplySideInletPumpType == FlowType::Variable) && (curCallingCase == UpdateType::SupplyLedPrimaryInlet)) { - // MdotPri is a variable to be calculated and flow request needs to be made - if (std::abs(TempCPPrimaryCntrlSetPoint) > DataPlant::DeltaTempTol) { - - MdotPri = (MdotPriRCLeg * TempPriOutTankOut + MdotPriToSec * TempSecOutTankOut) / (TempCPPrimaryCntrlSetPoint); - - if (MdotPri < DataBranchAirLoopPlant::MassFlowTolerance) { - MdotPri = 0.0; + // Use the available secondary flow and request the primary recirculation needed to meet the primary inlet setpoint. + MdotPriToSec = MdotSec; + if (std::abs(TempCPPrimaryCntrlSetPoint - TempPriOutTankOut) > DataPlant::DeltaTempTol) { + MdotPriRCLeg = MdotPriToSec * (TempSecOutTankOut - TempCPPrimaryCntrlSetPoint) / (TempCPPrimaryCntrlSetPoint - TempPriOutTankOut); + if (MdotPriRCLeg < DataBranchAirLoopPlant::MassFlowTolerance) { + MdotPriRCLeg = 0.0; } } else { - MdotPri = MdotSec; + MdotPriRCLeg = 0.0; + } + MdotPri = MdotPriToSec + MdotPriRCLeg; + if (MdotPri < DataBranchAirLoopPlant::MassFlowTolerance) { + MdotPri = 0.0; + } + if (canRequestPrimaryInletFlow) { + PlantUtilities::SetActuatedBranchFlowRate(state, MdotPri, NodeNumPriIn, plantCommonPipe.SupplySideInletPlantLoc, false); } - PlantLocation thisPlantLoc = {plantLoc.loopNum, DataPlant::LoopSideLocation::Supply, 1, 0}; - PlantUtilities::SetActuatedBranchFlowRate(state, MdotPri, NodeNumPriIn, thisPlantLoc, false); } // eq. 2 @@ -1177,8 +1187,9 @@ void ManageTwoWayCommonPipe(EnergyPlusData &state, PlantLocation const &plantLoc } else { MdotPri = MdotSec; } - PlantLocation thisPlantLoc = {plantLoc.loopNum, DataPlant::LoopSideLocation::Supply, 1, 0}; - PlantUtilities::SetActuatedBranchFlowRate(state, MdotPri, NodeNumPriIn, thisPlantLoc, false); + if (canRequestPrimaryInletFlow) { + PlantUtilities::SetActuatedBranchFlowRate(state, MdotPri, NodeNumPriIn, plantCommonPipe.SupplySideInletPlantLoc, false); + } } // eq. 4 @@ -1232,6 +1243,10 @@ void SetupCommonPipes(EnergyPlusData &state) auto &thisCommonPipe = state.dataHVACInterfaceMgr->PlantCommonPipe(CurLoopNum); auto const &first_demand_component_type = thisPlantLoop.LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp(1).Type; auto const &first_supply_component_type = thisPlantLoop.LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp(1).Type; + thisCommonPipe.SupplySideInletPlantLoc = {CurLoopNum, DataPlant::LoopSideLocation::Supply, 1, 0}; + PlantUtilities::SetPlantLocationLinks(state, thisCommonPipe.SupplySideInletPlantLoc); + thisCommonPipe.DemandSideInletPlantLoc = {CurLoopNum, DataPlant::LoopSideLocation::Demand, 1, 0}; + PlantUtilities::SetPlantLocationLinks(state, thisCommonPipe.DemandSideInletPlantLoc); switch (thisPlantLoop.CommonPipeType) { case DataPlant::CommonPipeType::No: @@ -1261,12 +1276,12 @@ void SetupCommonPipes(EnergyPlusData &state) OutputProcessor::StoreType::Average, thisPlantLoop.Name); - if (first_supply_component_type == DataPlant::PlantEquipmentType::PumpVariableSpeed) { - // If/when the model supports variable-pumping primary, this can be removed. - ShowWarningError(state, "SetupCommonPipes: detected variable speed pump on supply inlet of CommonPipe plant loop"); - ShowContinueError(state, std::format("Occurs on plant loop name = {}", thisPlantLoop.Name)); - ShowContinueError(state, "The common pipe model does not support varying the flow rate on the primary/supply side"); - ShowContinueError(state, "The primary/supply side will operate as if constant speed, and the simulation continues"); + if (first_supply_component_type == DataPlant::PlantEquipmentType::PumpConstantSpeed || + first_supply_component_type == DataPlant::PlantEquipmentType::PumpBankConstantSpeed) { + thisCommonPipe.SupplySideInletPumpType = FlowType::Constant; + } else if (first_supply_component_type == DataPlant::PlantEquipmentType::PumpVariableSpeed || + first_supply_component_type == DataPlant::PlantEquipmentType::PumpBankVariableSpeed) { + thisCommonPipe.SupplySideInletPumpType = FlowType::Variable; } break; case DataPlant::CommonPipeType::TwoWay: // Controlled ('two-way') common pipe @@ -1301,20 +1316,19 @@ void SetupCommonPipes(EnergyPlusData &state) thisPlantLoop.Name); // check type of pump on supply side inlet - if (first_supply_component_type == DataPlant::PlantEquipmentType::PumpConstantSpeed) { + if (first_supply_component_type == DataPlant::PlantEquipmentType::PumpConstantSpeed || + first_supply_component_type == DataPlant::PlantEquipmentType::PumpBankConstantSpeed) { thisCommonPipe.SupplySideInletPumpType = FlowType::Constant; - } else if (first_supply_component_type == DataPlant::PlantEquipmentType::PumpVariableSpeed) { + } else if (first_supply_component_type == DataPlant::PlantEquipmentType::PumpVariableSpeed || + first_supply_component_type == DataPlant::PlantEquipmentType::PumpBankVariableSpeed) { thisCommonPipe.SupplySideInletPumpType = FlowType::Variable; - // If/when the model supports variable-pumping primary, this can be removed. - ShowWarningError(state, "SetupCommonPipes: detected variable speed pump on supply inlet of TwoWayCommonPipe plant loop"); - ShowContinueError(state, std::format("Occurs on plant loop name = {}", thisPlantLoop.Name)); - ShowContinueError(state, "The common pipe model does not support varying the flow rate on the primary/supply side"); - ShowContinueError(state, "The primary/supply side will operate as if constant speed, and the simulation continues"); } // check type of pump on demand side inlet - if (first_demand_component_type == DataPlant::PlantEquipmentType::PumpConstantSpeed) { + if (first_demand_component_type == DataPlant::PlantEquipmentType::PumpConstantSpeed || + first_demand_component_type == DataPlant::PlantEquipmentType::PumpBankConstantSpeed) { thisCommonPipe.DemandSideInletPumpType = FlowType::Constant; - } else if (first_demand_component_type == DataPlant::PlantEquipmentType::PumpVariableSpeed) { + } else if (first_demand_component_type == DataPlant::PlantEquipmentType::PumpVariableSpeed || + first_demand_component_type == DataPlant::PlantEquipmentType::PumpBankVariableSpeed) { thisCommonPipe.DemandSideInletPumpType = FlowType::Variable; } break; diff --git a/src/EnergyPlus/HVACInterfaceManager.hh b/src/EnergyPlus/HVACInterfaceManager.hh index 6aabf92f48a..ee905736e47 100644 --- a/src/EnergyPlus/HVACInterfaceManager.hh +++ b/src/EnergyPlus/HVACInterfaceManager.hh @@ -85,6 +85,8 @@ namespace HVACInterfaceManager { DataPlant::CommonPipeType CommonPipeType = DataPlant::CommonPipeType::No; // type of common pipe used if any FlowType SupplySideInletPumpType = FlowType::Invalid; FlowType DemandSideInletPumpType = FlowType::Invalid; + PlantLocation SupplySideInletPlantLoc; + PlantLocation DemandSideInletPlantLoc; // Following report variables are used in uncontrolled common pipe int FlowDir = 0; // Direction in which flow is in Common Pipe Real64 Flow = 0.0; // Flow in the Common Pipe diff --git a/src/EnergyPlus/Plant/LoopSide.cc b/src/EnergyPlus/Plant/LoopSide.cc index fbeb80911f7..6c8bd7f4141 100644 --- a/src/EnergyPlus/Plant/LoopSide.cc +++ b/src/EnergyPlus/Plant/LoopSide.cc @@ -975,20 +975,44 @@ namespace DataPlant { // special primary side flow request for two way common pipe int const CompIndex = component.CompNum; switch (component.Type) { - // remove var speed pumps from this case statement if can set MassFlowRateRequest case DataPlant::PlantEquipmentType::PumpConstantSpeed: + if (CompIndex > 0) { + auto const &primaryPump = state.dataPumps->PumpEquip(CompIndex); + Real64 const primaryPumpFlowRequest = primaryPump.MassFlowRateMax; + Real64 const primaryPumpTurnOnRequest = (primaryPump.PumpControl == Pumps::PumpControlType::Continuous) + ? min(primaryPumpFlowRequest, node_with_request.MassFlowRateMaxAvail) + : node_with_request.MassFlowRateRequest; + ThisBranchFlowRequestNeedIfOn = max(ThisBranchFlowRequestNeedIfOn, primaryPumpFlowRequest); + ThisBranchFlowRequestNeedAndTurnOn = max(ThisBranchFlowRequestNeedAndTurnOn, primaryPumpTurnOnRequest); + } + break; case DataPlant::PlantEquipmentType::PumpVariableSpeed: case DataPlant::PlantEquipmentType::PumpBankVariableSpeed: if (CompIndex > 0) { - ThisBranchFlowRequestNeedIfOn = - max(ThisBranchFlowRequestNeedIfOn, state.dataPumps->PumpEquip(CompIndex).MassFlowRateMax); + auto &primaryPump = state.dataPumps->PumpEquip(CompIndex); + Real64 const primaryPumpMaxAvail = + max(0.0, min(primaryPump.MassFlowRateMax, node_with_request.MassFlowRateMaxAvail)); + Real64 const commonPipeFlowRequest = std::clamp(node_with_request.MassFlowRateRequest, 0.0, primaryPumpMaxAvail); + Real64 const primaryPumpTurnOnRequest = + (primaryPump.PumpControl == Pumps::PumpControlType::Continuous) + ? max(commonPipeFlowRequest, min(primaryPump.MassFlowRateMin, primaryPumpMaxAvail)) + : commonPipeFlowRequest; + ThisBranchFlowRequestNeedIfOn = max(ThisBranchFlowRequestNeedIfOn, commonPipeFlowRequest); + ThisBranchFlowRequestNeedAndTurnOn = max(ThisBranchFlowRequestNeedAndTurnOn, primaryPumpTurnOnRequest); + if (primaryPumpTurnOnRequest > DataBranchAirLoopPlant::MassFlowTolerance) { + primaryPump.LoopSolverOverwriteFlag = false; + } } break; case DataPlant::PlantEquipmentType::PumpBankConstantSpeed: if (CompIndex > 0) { - ThisBranchFlowRequestNeedIfOn = max(ThisBranchFlowRequestNeedIfOn, - state.dataPumps->PumpEquip(CompIndex).MassFlowRateMax / - state.dataPumps->PumpEquip(CompIndex).NumPumpsInBank); + auto const &primaryPump = state.dataPumps->PumpEquip(CompIndex); + Real64 const primaryPumpFlowRequest = primaryPump.MassFlowRateMax / primaryPump.NumPumpsInBank; + Real64 const primaryPumpTurnOnRequest = (primaryPump.PumpControl == Pumps::PumpControlType::Continuous) + ? min(primaryPumpFlowRequest, node_with_request.MassFlowRateMaxAvail) + : node_with_request.MassFlowRateRequest; + ThisBranchFlowRequestNeedIfOn = max(ThisBranchFlowRequestNeedIfOn, primaryPumpFlowRequest); + ThisBranchFlowRequestNeedAndTurnOn = max(ThisBranchFlowRequestNeedAndTurnOn, primaryPumpTurnOnRequest); } break; default: diff --git a/tst/EnergyPlus/unit/HVACInterfaceManager.unit.cc b/tst/EnergyPlus/unit/HVACInterfaceManager.unit.cc index b955c83b48b..1956fd98d66 100644 --- a/tst/EnergyPlus/unit/HVACInterfaceManager.unit.cc +++ b/tst/EnergyPlus/unit/HVACInterfaceManager.unit.cc @@ -63,6 +63,7 @@ #include #include #include +#include namespace EnergyPlus { TEST_F(EnergyPlusFixture, ExcessiveHeatStorage_Test) @@ -192,4 +193,225 @@ TEST_F(EnergyPlusFixture, UpdateHVACInterface_Test) EXPECT_TRUE(state->dataConvergeParams->AirLoopConvergence(1).HVACCO2NotConverged[2]); EXPECT_TRUE(state->dataConvergeParams->AirLoopConvergence(1).HVACGenContamNotConverged[2]); } + +TEST_F(EnergyPlusFixture, SetupCommonPipesCommonPipeVariablePrimaryPumpSetsVariableSupplyPumpType) +{ + using namespace DataPlant; + using namespace HVACInterfaceManager; + + constexpr int loopNum = 1; + state->dataPlnt->TotNumLoops = 1; + state->dataPlnt->PlantLoop.allocate(1); + + auto &plantLoop = state->dataPlnt->PlantLoop(loopNum); + plantLoop.Name = "Test Plant Loop"; + plantLoop.CommonPipeType = CommonPipeType::Single; + + auto &supplySide = plantLoop.LoopSide(LoopSideLocation::Supply); + supplySide.TotalBranches = 1; + supplySide.Branch.allocate(1); + supplySide.Branch(1).TotalComponents = 1; + supplySide.Branch(1).Comp.allocate(1); + supplySide.Branch(1).Comp(1).Type = PlantEquipmentType::PumpVariableSpeed; + + auto &demandSide = plantLoop.LoopSide(LoopSideLocation::Demand); + demandSide.TotalBranches = 1; + demandSide.Branch.allocate(1); + demandSide.Branch(1).TotalComponents = 1; + demandSide.Branch(1).Comp.allocate(1); + demandSide.Branch(1).Comp(1).Type = PlantEquipmentType::PumpConstantSpeed; + + SetupCommonPipes(*state); + + auto const &commonPipe = state->dataHVACInterfaceMgr->PlantCommonPipe(loopNum); + EXPECT_EQ(CommonPipeType::Single, commonPipe.CommonPipeType); + EXPECT_EQ(FlowType::Variable, commonPipe.SupplySideInletPumpType); +} + +TEST_F(EnergyPlusFixture, SetupCommonPipesTwoWayVariablePrimaryPumpBankSetsVariableSupplyPumpType) +{ + using namespace DataPlant; + using namespace HVACInterfaceManager; + + constexpr int loopNum = 1; + state->dataPlnt->TotNumLoops = 1; + state->dataPlnt->PlantLoop.allocate(1); + + auto &plantLoop = state->dataPlnt->PlantLoop(loopNum); + plantLoop.Name = "Test Plant Loop"; + plantLoop.CommonPipeType = CommonPipeType::TwoWay; + + auto &supplySide = plantLoop.LoopSide(LoopSideLocation::Supply); + supplySide.TotalBranches = 1; + supplySide.Branch.allocate(1); + supplySide.Branch(1).TotalComponents = 1; + supplySide.Branch(1).Comp.allocate(1); + supplySide.Branch(1).Comp(1).Type = PlantEquipmentType::PumpBankVariableSpeed; + + auto &demandSide = plantLoop.LoopSide(LoopSideLocation::Demand); + demandSide.TotalBranches = 1; + demandSide.Branch.allocate(1); + demandSide.Branch(1).TotalComponents = 1; + demandSide.Branch(1).Comp.allocate(1); + demandSide.Branch(1).Comp(1).Type = PlantEquipmentType::PumpBankConstantSpeed; + + SetupCommonPipes(*state); + + auto const &commonPipe = state->dataHVACInterfaceMgr->PlantCommonPipe(loopNum); + EXPECT_EQ(CommonPipeType::TwoWay, commonPipe.CommonPipeType); + EXPECT_EQ(FlowType::Variable, commonPipe.SupplySideInletPumpType); + EXPECT_EQ(FlowType::Constant, commonPipe.DemandSideInletPumpType); +} + +TEST_F(EnergyPlusFixture, SetupLoopFlowRequestTwoWayVariablePrimaryPumpFollowsCommonPipeRequest) +{ + using namespace DataPlant; + + constexpr int loopNum = 1; + constexpr int pumpNum = 1; + constexpr int primaryInletNode = 1; + constexpr Real64 pumpMaxMassFlow = 0.5; + constexpr Real64 commonPipeMassFlowRequest = 0.3; + + state->dataPlnt->TotNumLoops = 1; + state->dataPlnt->PlantLoop.allocate(1); + state->dataPumps->PumpEquip.allocate(1); + state->dataLoopNodes->Node.allocate(1); + + auto &plantLoop = state->dataPlnt->PlantLoop(loopNum); + plantLoop.Name = "Test Plant Loop"; + plantLoop.CommonPipeType = CommonPipeType::TwoWay; + + auto &supplySide = plantLoop.LoopSide(LoopSideLocation::Supply); + supplySide.TotalBranches = 1; + supplySide.TotalPumps = 1; + supplySide.Branch.allocate(1); + supplySide.Branch(1).TotalComponents = 1; + supplySide.Branch(1).Comp.allocate(1); + supplySide.Branch(1).Comp(1).Type = PlantEquipmentType::PumpVariableSpeed; + supplySide.Branch(1).Comp(1).CompNum = pumpNum; + supplySide.Branch(1).Comp(1).NodeNumIn = primaryInletNode; + supplySide.plantLoc = {loopNum, LoopSideLocation::Supply, 0, 0}; + + auto &demandSide = plantLoop.LoopSide(LoopSideLocation::Demand); + demandSide.TotalBranches = 1; + demandSide.Branch.allocate(1); + demandSide.Branch(1).TotalComponents = 1; + demandSide.Branch(1).Comp.allocate(1); + demandSide.Branch(1).Comp(1).Type = PlantEquipmentType::PumpConstantSpeed; + demandSide.plantLoc = {loopNum, LoopSideLocation::Demand, 0, 0}; + + auto &pump = state->dataPumps->PumpEquip(pumpNum); + pump.MassFlowRateMax = pumpMaxMassFlow; + pump.PumpControl = Pumps::PumpControlType::Intermittent; + pump.LoopSolverOverwriteFlag = true; + + Real64 const noRequestLoopFlow = supplySide.SetupLoopFlowRequest(*state, LoopSideLocation::Demand); + EXPECT_NEAR(0.0, noRequestLoopFlow, 0.0000001); + EXPECT_NEAR(0.0, supplySide.flowRequestNeedAndTurnOn, 0.0000001); + EXPECT_TRUE(pump.LoopSolverOverwriteFlag); + + state->dataLoopNodes->Node(primaryInletNode).MassFlowRateMaxAvail = pumpMaxMassFlow; + state->dataLoopNodes->Node(primaryInletNode).MassFlowRateRequest = commonPipeMassFlowRequest; + Real64 const loopFlow = supplySide.SetupLoopFlowRequest(*state, LoopSideLocation::Demand); + + EXPECT_NEAR(commonPipeMassFlowRequest, loopFlow, 0.0000001); + EXPECT_NEAR(commonPipeMassFlowRequest, supplySide.flowRequestNeedAndTurnOn, 0.0000001); + EXPECT_NEAR(commonPipeMassFlowRequest, supplySide.flowRequestNeedIfOn, 0.0000001); + EXPECT_NEAR(commonPipeMassFlowRequest, supplySide.flowRequestFinal, 0.0000001); + EXPECT_NEAR(0.0, demandSide.flowRequestFinal, 0.0000001); + EXPECT_FALSE(pump.LoopSolverOverwriteFlag); + + constexpr Real64 lowerCommonPipeMassFlowRequest = 0.1; + state->dataLoopNodes->Node(primaryInletNode).MassFlowRateRequest = lowerCommonPipeMassFlowRequest; + pump.PumpControl = Pumps::PumpControlType::Continuous; + pump.LoopSolverOverwriteFlag = true; + Real64 const continuousLoopFlow = supplySide.SetupLoopFlowRequest(*state, LoopSideLocation::Demand); + + EXPECT_NEAR(lowerCommonPipeMassFlowRequest, continuousLoopFlow, 0.0000001); + EXPECT_NEAR(lowerCommonPipeMassFlowRequest, supplySide.flowRequestNeedAndTurnOn, 0.0000001); + EXPECT_NEAR(lowerCommonPipeMassFlowRequest, supplySide.flowRequestFinal, 0.0000001); + EXPECT_FALSE(pump.LoopSolverOverwriteFlag); + + constexpr Real64 pumpMinMassFlow = 0.05; + state->dataLoopNodes->Node(primaryInletNode).MassFlowRateRequest = 0.0; + pump.MassFlowRateMin = pumpMinMassFlow; + pump.LoopSolverOverwriteFlag = true; + Real64 const minimumLoopFlow = supplySide.SetupLoopFlowRequest(*state, LoopSideLocation::Demand); + + EXPECT_NEAR(pumpMinMassFlow, minimumLoopFlow, 0.0000001); + EXPECT_NEAR(pumpMinMassFlow, supplySide.flowRequestNeedAndTurnOn, 0.0000001); + EXPECT_NEAR(0.0, supplySide.flowRequestNeedIfOn, 0.0000001); + EXPECT_NEAR(pumpMinMassFlow, supplySide.flowRequestFinal, 0.0000001); + EXPECT_FALSE(pump.LoopSolverOverwriteFlag); +} + +TEST_F(EnergyPlusFixture, ManageTwoWayCommonPipeVariablePrimaryPumpBankVariesFlowToMeetPrimaryInletSetPoint) +{ + using namespace DataPlant; + using namespace HVACInterfaceManager; + + constexpr int loopNum = 1; + constexpr int priInNode = 1; + constexpr int priOutNode = 2; + constexpr int secInNode = 3; + constexpr int secOutNode = 4; + + state->dataPlnt->TotNumLoops = 1; + state->dataPlnt->PlantLoop.allocate(1); + state->dataLoopNodes->Node.allocate(4); + + auto &plantLoop = state->dataPlnt->PlantLoop(loopNum); + plantLoop.Name = "Test Plant Loop"; + plantLoop.CommonPipeType = CommonPipeType::TwoWay; + + auto &supplySide = plantLoop.LoopSide(LoopSideLocation::Supply); + supplySide.NodeNumIn = priInNode; + supplySide.NodeNumOut = priOutNode; + supplySide.InletNodeSetPt = true; + supplySide.TotalBranches = 1; + supplySide.Branch.allocate(1); + supplySide.Branch(1).TotalComponents = 1; + supplySide.Branch(1).Comp.allocate(1); + supplySide.Branch(1).Comp(1).Type = PlantEquipmentType::PumpBankVariableSpeed; + supplySide.Branch(1).Comp(1).NodeNumIn = priInNode; + supplySide.Branch(1).Comp(1).NodeNumOut = priOutNode; + + auto &demandSide = plantLoop.LoopSide(LoopSideLocation::Demand); + demandSide.NodeNumIn = secInNode; + demandSide.NodeNumOut = secOutNode; + demandSide.InletNodeSetPt = false; + demandSide.LoopSideInlet_TankTemp = 6.0; + demandSide.TotalBranches = 1; + demandSide.Branch.allocate(1); + demandSide.Branch(1).TotalComponents = 1; + demandSide.Branch(1).Comp.allocate(1); + demandSide.Branch(1).Comp(1).Type = PlantEquipmentType::PumpConstantSpeed; + + state->dataLoopNodes->Node(priInNode).TempSetPoint = 10.0; + state->dataLoopNodes->Node(priInNode).MassFlowRateMax = 10.0; + state->dataLoopNodes->Node(priInNode).MassFlowRateMaxAvail = 10.0; + + PlantLocation plantLoc{loopNum, LoopSideLocation::Supply, 1, 0}; + + state->dataLoopNodes->Node(secOutNode).MassFlowRate = 0.4; + ManageTwoWayCommonPipe(*state, plantLoc, 12.0); + + auto const &commonPipe = state->dataHVACInterfaceMgr->PlantCommonPipe(loopNum); + EXPECT_NEAR(0.6, state->dataLoopNodes->Node(priInNode).MassFlowRateRequest, 0.0000001); + EXPECT_NEAR(0.6, state->dataLoopNodes->Node(priInNode).MassFlowRate, 0.0000001); + EXPECT_NEAR(0.4, commonPipe.PriToSecFlow, 0.0000001); + EXPECT_NEAR(0.2, commonPipe.PriCPLegFlow, 0.0000001); + EXPECT_NEAR(10.0, state->dataLoopNodes->Node(priInNode).Temp, 0.0000001); + + state->dataLoopNodes->Node(secOutNode).MassFlowRate = 1.0; + ManageTwoWayCommonPipe(*state, plantLoc, 12.0); + + EXPECT_NEAR(1.5, state->dataLoopNodes->Node(priInNode).MassFlowRateRequest, 0.0000001); + EXPECT_NEAR(1.5, state->dataLoopNodes->Node(priInNode).MassFlowRate, 0.0000001); + EXPECT_NEAR(1.0, commonPipe.PriToSecFlow, 0.0000001); + EXPECT_NEAR(0.5, commonPipe.PriCPLegFlow, 0.0000001); + EXPECT_NEAR(10.0, state->dataLoopNodes->Node(priInNode).Temp, 0.0000001); +} + } // namespace EnergyPlus