LCOV - code coverage report
Current view: top level - EnergyPlus - HVACInterfaceManager.cc (source / functions) Coverage Total Hit
Test: lcov.output.filtered Lines: 90.7 % 635 576
Test Date: 2025-06-02 07:23:51 Functions: 100.0 % 7 7

            Line data    Source code
       1              : // EnergyPlus, Copyright (c) 1996-2025, The Board of Trustees of the University of Illinois,
       2              : // The Regents of the University of California, through Lawrence Berkeley National Laboratory
       3              : // (subject to receipt of any required approvals from the U.S. Dept. of Energy), Oak Ridge
       4              : // National Laboratory, managed by UT-Battelle, Alliance for Sustainable Energy, LLC, and other
       5              : // contributors. All rights reserved.
       6              : //
       7              : // NOTICE: This Software was developed under funding from the U.S. Department of Energy and the
       8              : // U.S. Government consequently retains certain rights. As such, the U.S. Government has been
       9              : // granted for itself and others acting on its behalf a paid-up, nonexclusive, irrevocable,
      10              : // worldwide license in the Software to reproduce, distribute copies to the public, prepare
      11              : // derivative works, and perform publicly and display publicly, and to permit others to do so.
      12              : //
      13              : // Redistribution and use in source and binary forms, with or without modification, are permitted
      14              : // provided that the following conditions are met:
      15              : //
      16              : // (1) Redistributions of source code must retain the above copyright notice, this list of
      17              : //     conditions and the following disclaimer.
      18              : //
      19              : // (2) Redistributions in binary form must reproduce the above copyright notice, this list of
      20              : //     conditions and the following disclaimer in the documentation and/or other materials
      21              : //     provided with the distribution.
      22              : //
      23              : // (3) Neither the name of the University of California, Lawrence Berkeley National Laboratory,
      24              : //     the University of Illinois, U.S. Dept. of Energy nor the names of its contributors may be
      25              : //     used to endorse or promote products derived from this software without specific prior
      26              : //     written permission.
      27              : //
      28              : // (4) Use of EnergyPlus(TM) Name. If Licensee (i) distributes the software in stand-alone form
      29              : //     without changes from the version obtained under this License, or (ii) Licensee makes a
      30              : //     reference solely to the software portion of its product, Licensee must refer to the
      31              : //     software as "EnergyPlus version X" software, where "X" is the version number Licensee
      32              : //     obtained under this License and may not use a different name for the software. Except as
      33              : //     specifically required in this Section (4), Licensee shall not use in a company name, a
      34              : //     product name, in advertising, publicity, or other promotional activities any name, trade
      35              : //     name, trademark, logo, or other designation of "EnergyPlus", "E+", "e+" or confusingly
      36              : //     similar designation, without the U.S. Department of Energy's prior written consent.
      37              : //
      38              : // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
      39              : // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
      40              : // AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
      41              : // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
      42              : // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
      43              : // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
      44              : // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
      45              : // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
      46              : // POSSIBILITY OF SUCH DAMAGE.
      47              : 
      48              : // C++ Headers
      49              : #include <cmath>
      50              : 
      51              : // ObjexxFCL Headers
      52              : #include <ObjexxFCL/Fmath.hh>
      53              : 
      54              : // EnergyPlus Headers
      55              : #include <EnergyPlus/Data/EnergyPlusData.hh>
      56              : #include <EnergyPlus/DataAirLoop.hh>
      57              : #include <EnergyPlus/DataBranchAirLoopPlant.hh>
      58              : #include <EnergyPlus/DataContaminantBalance.hh>
      59              : #include <EnergyPlus/DataConvergParams.hh>
      60              : #include <EnergyPlus/DataHVACGlobals.hh>
      61              : #include <EnergyPlus/FluidProperties.hh>
      62              : #include <EnergyPlus/HVACInterfaceManager.hh>
      63              : #include <EnergyPlus/OutputProcessor.hh>
      64              : #include <EnergyPlus/Plant/DataPlant.hh>
      65              : #include <EnergyPlus/PlantUtilities.hh>
      66              : #include <EnergyPlus/UtilityRoutines.hh>
      67              : 
      68              : namespace EnergyPlus::HVACInterfaceManager {
      69              : 
      70              : // MODULE INFORMATION:
      71              : //       AUTHOR         Rick Strand
      72              : //       DATE WRITTEN   October 1998
      73              : //       MODIFIED       na
      74              : //       RE-ENGINEERED  na
      75              : 
      76              : // PURPOSE OF THIS MODULE:
      77              : // This module contains one or more routines for checking the convergence
      78              : // of the various HVAC loops and passing information across interface
      79              : // boundaries.
      80              : 
      81              : // METHODOLOGY EMPLOYED:
      82              : // The upper level HVAC managers call the routine(s) contained in this
      83              : // module as a last step.  The node information is passed across the
      84              : // interface boundary and the logical flag is set if the nodes across
      85              : // from each other are not within tolerance.
      86              : 
      87     25944006 : void UpdateHVACInterface(EnergyPlusData &state,
      88              :                          int const AirLoopNum, // airloop number for which air loop this is
      89              :                          DataConvergParams::CalledFrom const CalledFrom,
      90              :                          int const OutletNode,    // Node number for the outlet of the side of the loop just simulated
      91              :                          int const InletNode,     // Node number for the inlet of the side that needs the outlet node data
      92              :                          bool &OutOfToleranceFlag // True when the other side of the loop need to be (re)simulated
      93              : )
      94              : {
      95              : 
      96              :     // SUBROUTINE INFORMATION:
      97              :     //       AUTHOR         Rick Strand
      98              :     //       DATE WRITTEN   October 1998
      99              : 
     100              :     // PURPOSE OF THIS SUBROUTINE:
     101              :     // This subroutine manages any generic HVAC loop interface.
     102              : 
     103              :     // METHODOLOGY EMPLOYED:
     104              :     // This is a simple "forward" interface where all of the properties
     105              :     // from the outlet of one side of the loop get transferred directly
     106              :     // to the inlet node of the corresponding other side of the loop.
     107              : 
     108     25944006 :     auto &TmpRealARR = state.dataHVACInterfaceMgr->TmpRealARR;
     109     25944006 :     auto &airLoopConv = state.dataConvergeParams->AirLoopConvergence(AirLoopNum);
     110     25944006 :     auto &thisInletNode = state.dataLoopNodes->Node(InletNode);
     111     25944006 :     int const iCall = (int)CalledFrom;
     112              : 
     113     25944006 :     if ((CalledFrom == DataConvergParams::CalledFrom::AirSystemDemandSide) && (OutletNode == 0)) {
     114              :         // Air loop has no return path - only check mass flow and then set return inlet node mass flow to sum of demand side inlet nodes
     115              : 
     116        15269 :         airLoopConv.HVACMassFlowNotConverged[iCall] = false;
     117        15269 :         airLoopConv.HVACHumRatNotConverged[iCall] = false;
     118        15269 :         airLoopConv.HVACTempNotConverged[iCall] = false;
     119        15269 :         airLoopConv.HVACEnergyNotConverged[iCall] = false;
     120              : 
     121        15269 :         Real64 totDemandSideMassFlow = 0.0;
     122        15269 :         Real64 totDemandSideMinAvail = 0.0;
     123        15269 :         Real64 totDemandSideMaxAvail = 0.0;
     124        30538 :         for (int demIn = 1; demIn <= state.dataAirLoop->AirToZoneNodeInfo(AirLoopNum).NumSupplyNodes; ++demIn) {
     125        15269 :             int demInNode = state.dataAirLoop->AirToZoneNodeInfo(AirLoopNum).ZoneEquipSupplyNodeNum(demIn);
     126        15269 :             auto const &node = state.dataLoopNodes->Node(demInNode);
     127        15269 :             totDemandSideMassFlow += node.MassFlowRate;
     128        15269 :             totDemandSideMinAvail += node.MassFlowRateMinAvail;
     129        15269 :             totDemandSideMaxAvail += node.MassFlowRateMaxAvail;
     130              :         }
     131        15269 :         TmpRealARR = airLoopConv.HVACFlowDemandToSupplyTolValue;
     132        15269 :         airLoopConv.HVACFlowDemandToSupplyTolValue[0] = std::abs(totDemandSideMassFlow - thisInletNode.MassFlowRate);
     133       152690 :         for (int logIndex = 1; logIndex < DataConvergParams::ConvergLogStackDepth; logIndex++) {
     134       137421 :             airLoopConv.HVACFlowDemandToSupplyTolValue[logIndex] = TmpRealARR[logIndex - 1];
     135              :         }
     136        15269 :         if (airLoopConv.HVACFlowDemandToSupplyTolValue[0] > DataConvergParams::HVACFlowRateToler) {
     137         5242 :             airLoopConv.HVACMassFlowNotConverged[iCall] = true;
     138         5242 :             OutOfToleranceFlag = true; // Something has changed--resimulate the other side of the loop
     139              :         }
     140              : 
     141        15269 :         thisInletNode.MassFlowRate = totDemandSideMassFlow;
     142        15269 :         thisInletNode.MassFlowRateMinAvail = totDemandSideMinAvail;
     143        15269 :         thisInletNode.MassFlowRateMaxAvail = totDemandSideMaxAvail;
     144        15269 :         return;
     145              :     }
     146              : 
     147              :     // Calculate the approximate energy difference across interface for comparison
     148              :     Real64 DeltaEnergy =
     149     25928737 :         DataConvergParams::HVACCpApprox * ((state.dataLoopNodes->Node(OutletNode).MassFlowRate * state.dataLoopNodes->Node(OutletNode).Temp) -
     150     25928737 :                                            (thisInletNode.MassFlowRate * thisInletNode.Temp));
     151              : 
     152     25928737 :     if ((CalledFrom == DataConvergParams::CalledFrom::AirSystemDemandSide) && (OutletNode > 0)) {
     153              : 
     154     12292565 :         airLoopConv.HVACMassFlowNotConverged[iCall] = false;
     155     12292565 :         airLoopConv.HVACHumRatNotConverged[iCall] = false;
     156     12292565 :         airLoopConv.HVACTempNotConverged[iCall] = false;
     157     12292565 :         airLoopConv.HVACEnergyNotConverged[iCall] = false;
     158     12292565 :         if (state.dataContaminantBalance->Contaminant.CO2Simulation) {
     159        40525 :             airLoopConv.HVACCO2NotConverged[iCall] = false;
     160              :         }
     161     12292565 :         if (state.dataContaminantBalance->Contaminant.GenericContamSimulation) {
     162        11609 :             airLoopConv.HVACGenContamNotConverged[iCall] = false;
     163              :         }
     164              : 
     165     12292565 :         TmpRealARR = airLoopConv.HVACFlowDemandToSupplyTolValue;
     166     12292565 :         airLoopConv.HVACFlowDemandToSupplyTolValue[0] = std::abs(state.dataLoopNodes->Node(OutletNode).MassFlowRate - thisInletNode.MassFlowRate);
     167    122925650 :         for (int logIndex = 1; logIndex < DataConvergParams::ConvergLogStackDepth; logIndex++) {
     168    110633085 :             airLoopConv.HVACFlowDemandToSupplyTolValue[logIndex] = TmpRealARR[logIndex - 1];
     169              :         }
     170     12292565 :         if (airLoopConv.HVACFlowDemandToSupplyTolValue[0] > DataConvergParams::HVACFlowRateToler) {
     171      3344162 :             airLoopConv.HVACMassFlowNotConverged[iCall] = true;
     172      3344162 :             OutOfToleranceFlag = true; // Something has changed--resimulate the other side of the loop
     173              :         }
     174              : 
     175     12292565 :         TmpRealARR = airLoopConv.HVACHumDemandToSupplyTolValue;
     176     12292565 :         airLoopConv.HVACHumDemandToSupplyTolValue[0] = std::abs(state.dataLoopNodes->Node(OutletNode).HumRat - thisInletNode.HumRat);
     177    122925650 :         for (int logIndex = 1; logIndex < DataConvergParams::ConvergLogStackDepth; logIndex++) {
     178    110633085 :             airLoopConv.HVACHumDemandToSupplyTolValue[logIndex] = TmpRealARR[logIndex - 1];
     179              :         }
     180     12292565 :         if (airLoopConv.HVACHumDemandToSupplyTolValue[0] > DataConvergParams::HVACHumRatToler) {
     181       449712 :             airLoopConv.HVACHumRatNotConverged[iCall] = true;
     182       449712 :             OutOfToleranceFlag = true; // Something has changed--resimulate the other side of the loop
     183              :         }
     184              : 
     185     12292565 :         TmpRealARR = airLoopConv.HVACTempDemandToSupplyTolValue;
     186     12292565 :         airLoopConv.HVACTempDemandToSupplyTolValue[0] = std::abs(state.dataLoopNodes->Node(OutletNode).Temp - thisInletNode.Temp);
     187    122925650 :         for (int logIndex = 1; logIndex < DataConvergParams::ConvergLogStackDepth; logIndex++) {
     188    110633085 :             airLoopConv.HVACTempDemandToSupplyTolValue[logIndex] = TmpRealARR[logIndex - 1];
     189              :         }
     190     12292565 :         if (airLoopConv.HVACTempDemandToSupplyTolValue[0] > DataConvergParams::HVACTemperatureToler) {
     191      2604685 :             airLoopConv.HVACTempNotConverged[iCall] = true;
     192      2604685 :             OutOfToleranceFlag = true; // Something has changed--resimulate the other side of the loop
     193              :         }
     194              : 
     195     12292565 :         TmpRealARR = airLoopConv.HVACEnergyDemandToSupplyTolValue;
     196     12292565 :         airLoopConv.HVACEnergyDemandToSupplyTolValue[0] = std::abs(DeltaEnergy);
     197    122925650 :         for (int logIndex = 1; logIndex < DataConvergParams::ConvergLogStackDepth; logIndex++) {
     198    110633085 :             airLoopConv.HVACEnergyDemandToSupplyTolValue[logIndex] = TmpRealARR[logIndex - 1];
     199              :         }
     200     12292565 :         if (std::abs(DeltaEnergy) > DataConvergParams::HVACEnergyToler) {
     201      4616043 :             airLoopConv.HVACEnergyNotConverged[iCall] = true;
     202      4616043 :             OutOfToleranceFlag = true; // Something has changed--resimulate the other side of the loop
     203              :         }
     204              : 
     205     12292565 :         TmpRealARR = airLoopConv.HVACEnthalpyDemandToSupplyTolValue;
     206     12292565 :         airLoopConv.HVACEnthalpyDemandToSupplyTolValue[0] = std::abs(state.dataLoopNodes->Node(OutletNode).Enthalpy - thisInletNode.Enthalpy);
     207    122925650 :         for (int logIndex = 1; logIndex < DataConvergParams::ConvergLogStackDepth; logIndex++) {
     208    110633085 :             airLoopConv.HVACEnthalpyDemandToSupplyTolValue[logIndex] = TmpRealARR[logIndex - 1];
     209              :         }
     210     12292565 :         if (airLoopConv.HVACEnthalpyDemandToSupplyTolValue[0] > DataConvergParams::HVACEnthalpyToler) {
     211      1132222 :             OutOfToleranceFlag = true; // Something has changed--resimulate the other side of the loop
     212              :         }
     213              : 
     214     12292565 :         TmpRealARR = airLoopConv.HVACPressureDemandToSupplyTolValue;
     215     12292565 :         airLoopConv.HVACPressureDemandToSupplyTolValue[0] = std::abs(state.dataLoopNodes->Node(OutletNode).Press - thisInletNode.Press);
     216    122925650 :         for (int logIndex = 1; logIndex < DataConvergParams::ConvergLogStackDepth; logIndex++) {
     217    110633085 :             airLoopConv.HVACPressureDemandToSupplyTolValue[logIndex] = TmpRealARR[logIndex - 1];
     218              :         }
     219     12292565 :         if (airLoopConv.HVACPressureDemandToSupplyTolValue[0] > DataConvergParams::HVACPressToler) {
     220        12279 :             OutOfToleranceFlag = true; // Something has changed--resimulate the other side of the loop
     221              :         }
     222              : 
     223     12292565 :         if (state.dataContaminantBalance->Contaminant.CO2Simulation) {
     224        40525 :             TmpRealARR = airLoopConv.HVACCO2DemandToSupplyTolValue;
     225        40525 :             airLoopConv.HVACCO2DemandToSupplyTolValue[0] = std::abs(state.dataLoopNodes->Node(OutletNode).CO2 - thisInletNode.CO2);
     226       405250 :             for (int logIndex = 1; logIndex < DataConvergParams::ConvergLogStackDepth; logIndex++) {
     227       364725 :                 airLoopConv.HVACCO2DemandToSupplyTolValue[logIndex] = TmpRealARR[logIndex - 1];
     228              :             }
     229        40525 :             if (airLoopConv.HVACCO2DemandToSupplyTolValue[0] > DataConvergParams::HVACCO2Toler) {
     230         9287 :                 airLoopConv.HVACCO2NotConverged[iCall] = true;
     231         9287 :                 OutOfToleranceFlag = true; // Something has changed--resimulate the other side of the loop
     232              :             }
     233              :         }
     234              : 
     235     12292565 :         if (state.dataContaminantBalance->Contaminant.GenericContamSimulation) {
     236        11609 :             TmpRealARR = airLoopConv.HVACGenContamDemandToSupplyTolValue;
     237        11609 :             airLoopConv.HVACGenContamDemandToSupplyTolValue[0] = std::abs(state.dataLoopNodes->Node(OutletNode).GenContam - thisInletNode.GenContam);
     238       116090 :             for (int logIndex = 1; logIndex < DataConvergParams::ConvergLogStackDepth; logIndex++) {
     239       104481 :                 airLoopConv.HVACGenContamDemandToSupplyTolValue[logIndex] = TmpRealARR[logIndex - 1];
     240              :             }
     241        11609 :             if (airLoopConv.HVACGenContamDemandToSupplyTolValue[0] > DataConvergParams::HVACGenContamToler) {
     242         4028 :                 airLoopConv.HVACGenContamNotConverged[iCall] = true;
     243         4028 :                 OutOfToleranceFlag = true; // Something has changed--resimulate the other side of the loop
     244              :             }
     245              :         }
     246              : 
     247     25928737 :     } else if (CalledFrom == DataConvergParams::CalledFrom::AirSystemSupplySideDeck1) {
     248              : 
     249     13581580 :         airLoopConv.HVACMassFlowNotConverged[iCall] = false;
     250     13581580 :         airLoopConv.HVACHumRatNotConverged[iCall] = false;
     251     13581580 :         airLoopConv.HVACTempNotConverged[iCall] = false;
     252     13581580 :         airLoopConv.HVACEnergyNotConverged[iCall] = false;
     253     13581580 :         if (state.dataContaminantBalance->Contaminant.CO2Simulation) {
     254        40486 :             airLoopConv.HVACCO2NotConverged[iCall] = false;
     255              :         }
     256     13581580 :         if (state.dataContaminantBalance->Contaminant.GenericContamSimulation) {
     257        11600 :             airLoopConv.HVACGenContamNotConverged[iCall] = false;
     258              :         }
     259              : 
     260     13581580 :         TmpRealARR = airLoopConv.HVACFlowSupplyDeck1ToDemandTolValue;
     261     13581580 :         airLoopConv.HVACFlowSupplyDeck1ToDemandTolValue[0] =
     262     13581580 :             std::abs(state.dataLoopNodes->Node(OutletNode).MassFlowRate - thisInletNode.MassFlowRate);
     263    135815800 :         for (int logIndex = 1; logIndex < DataConvergParams::ConvergLogStackDepth; logIndex++) {
     264    122234220 :             airLoopConv.HVACFlowSupplyDeck1ToDemandTolValue[logIndex] = TmpRealARR[logIndex - 1];
     265              :         }
     266     13581580 :         if (airLoopConv.HVACFlowSupplyDeck1ToDemandTolValue[0] > DataConvergParams::HVACFlowRateToler) {
     267      3230323 :             airLoopConv.HVACMassFlowNotConverged[iCall] = true;
     268      3230323 :             OutOfToleranceFlag = true; // Something has changed--resimulate the other side of the loop
     269              :         }
     270              : 
     271     13581580 :         TmpRealARR = airLoopConv.HVACHumSupplyDeck1ToDemandTolValue;
     272     13581580 :         airLoopConv.HVACHumSupplyDeck1ToDemandTolValue[0] = std::abs(state.dataLoopNodes->Node(OutletNode).HumRat - thisInletNode.HumRat);
     273    135815800 :         for (int logIndex = 1; logIndex < DataConvergParams::ConvergLogStackDepth; logIndex++) {
     274    122234220 :             airLoopConv.HVACHumSupplyDeck1ToDemandTolValue[logIndex] = TmpRealARR[logIndex - 1];
     275              :         }
     276     13581580 :         if (airLoopConv.HVACHumSupplyDeck1ToDemandTolValue[0] > DataConvergParams::HVACHumRatToler) {
     277      1926260 :             airLoopConv.HVACHumRatNotConverged[iCall] = true;
     278      1926260 :             OutOfToleranceFlag = true; // Something has changed--resimulate the other side of the loop
     279              :         }
     280              : 
     281     13581580 :         TmpRealARR = airLoopConv.HVACTempSupplyDeck1ToDemandTolValue;
     282     13581580 :         airLoopConv.HVACTempSupplyDeck1ToDemandTolValue[0] = std::abs(state.dataLoopNodes->Node(OutletNode).Temp - thisInletNode.Temp);
     283    135815800 :         for (int logIndex = 1; logIndex < DataConvergParams::ConvergLogStackDepth; logIndex++) {
     284    122234220 :             airLoopConv.HVACTempSupplyDeck1ToDemandTolValue[logIndex] = TmpRealARR[logIndex - 1];
     285              :         }
     286     13581580 :         if (airLoopConv.HVACTempSupplyDeck1ToDemandTolValue[0] > DataConvergParams::HVACTemperatureToler) {
     287      6547267 :             airLoopConv.HVACTempNotConverged[iCall] = true;
     288      6547267 :             OutOfToleranceFlag = true; // Something has changed--resimulate the other side of the loop
     289              :         }
     290              : 
     291     13581580 :         TmpRealARR = airLoopConv.HVACEnergySupplyDeck1ToDemandTolValue;
     292     13581580 :         airLoopConv.HVACEnergySupplyDeck1ToDemandTolValue[0] = DeltaEnergy;
     293    135815800 :         for (int logIndex = 1; logIndex < DataConvergParams::ConvergLogStackDepth; logIndex++) {
     294    122234220 :             airLoopConv.HVACEnergySupplyDeck1ToDemandTolValue[logIndex] = TmpRealARR[logIndex - 1];
     295              :         }
     296     13581580 :         if (std::abs(DeltaEnergy) > DataConvergParams::HVACEnergyToler) {
     297      6885781 :             airLoopConv.HVACEnergyNotConverged[iCall] = true;
     298      6885781 :             OutOfToleranceFlag = true; // Something has changed--resimulate the other side of the loop
     299              :         }
     300              : 
     301     13581580 :         TmpRealARR = airLoopConv.HVACEnthalpySupplyDeck1ToDemandTolValue;
     302     13581580 :         airLoopConv.HVACEnthalpySupplyDeck1ToDemandTolValue[0] = std::abs(state.dataLoopNodes->Node(OutletNode).Enthalpy - thisInletNode.Enthalpy);
     303    135815800 :         for (int logIndex = 1; logIndex < DataConvergParams::ConvergLogStackDepth; logIndex++) {
     304    122234220 :             airLoopConv.HVACEnthalpySupplyDeck1ToDemandTolValue[logIndex] = TmpRealARR[logIndex - 1];
     305              :         }
     306     13581580 :         if (airLoopConv.HVACEnthalpySupplyDeck1ToDemandTolValue[0] > DataConvergParams::HVACEnthalpyToler) {
     307      4542443 :             OutOfToleranceFlag = true; // Something has changed--resimulate the other side of the loop
     308              :         }
     309              : 
     310     13581580 :         TmpRealARR = airLoopConv.HVACPressureSupplyDeck1ToDemandTolValue;
     311     13581580 :         airLoopConv.HVACPressureSupplyDeck1ToDemandTolValue[0] = std::abs(state.dataLoopNodes->Node(OutletNode).Press - thisInletNode.Press);
     312    135815800 :         for (int logIndex = 1; logIndex < DataConvergParams::ConvergLogStackDepth; logIndex++) {
     313    122234220 :             airLoopConv.HVACPressureSupplyDeck1ToDemandTolValue[logIndex] = TmpRealARR[logIndex - 1];
     314              :         }
     315     13581580 :         if (airLoopConv.HVACPressureSupplyDeck1ToDemandTolValue[0] > DataConvergParams::HVACPressToler) {
     316       367746 :             OutOfToleranceFlag = true; // Something has changed--resimulate the other side of the loop
     317              :         }
     318              :         // CO2 check
     319     13581580 :         if (state.dataContaminantBalance->Contaminant.CO2Simulation) {
     320        40486 :             TmpRealARR = airLoopConv.HVACCO2SupplyDeck1ToDemandTolValue;
     321        40486 :             airLoopConv.HVACCO2SupplyDeck1ToDemandTolValue[0] = std::abs(state.dataLoopNodes->Node(OutletNode).CO2 - thisInletNode.CO2);
     322       404860 :             for (int logIndex = 1; logIndex < DataConvergParams::ConvergLogStackDepth; logIndex++) {
     323       364374 :                 airLoopConv.HVACCO2SupplyDeck1ToDemandTolValue[logIndex] = TmpRealARR[logIndex - 1];
     324              :             }
     325        40486 :             if (airLoopConv.HVACCO2SupplyDeck1ToDemandTolValue[0] > DataConvergParams::HVACCO2Toler) {
     326         9275 :                 airLoopConv.HVACCO2NotConverged[iCall] = true;
     327         9275 :                 OutOfToleranceFlag = true; // Something has changed--resimulate the other side of the loop
     328              :             }
     329              :         }
     330              : 
     331     13581580 :         if (state.dataContaminantBalance->Contaminant.GenericContamSimulation) {
     332        11600 :             TmpRealARR = airLoopConv.HVACGenContamSupplyDeck1ToDemandTolValue;
     333        11600 :             airLoopConv.HVACGenContamSupplyDeck1ToDemandTolValue[0] =
     334        11600 :                 std::abs(state.dataLoopNodes->Node(OutletNode).GenContam - thisInletNode.GenContam);
     335       116000 :             for (int logIndex = 1; logIndex < DataConvergParams::ConvergLogStackDepth; logIndex++) {
     336       104400 :                 airLoopConv.HVACGenContamSupplyDeck1ToDemandTolValue[logIndex] = TmpRealARR[logIndex - 1];
     337              :             }
     338        11600 :             if (airLoopConv.HVACGenContamSupplyDeck1ToDemandTolValue[0] > DataConvergParams::HVACGenContamToler) {
     339         2590 :                 airLoopConv.HVACGenContamNotConverged[iCall] = true;
     340         2590 :                 OutOfToleranceFlag = true; // Something has changed--resimulate the other side of the loop
     341              :             }
     342              :         }
     343              : 
     344        54592 :     } else if (CalledFrom == DataConvergParams::CalledFrom::AirSystemSupplySideDeck2) {
     345              : 
     346        54592 :         airLoopConv.HVACMassFlowNotConverged[iCall] = false;
     347        54592 :         airLoopConv.HVACHumRatNotConverged[iCall] = false;
     348        54592 :         airLoopConv.HVACTempNotConverged[iCall] = false;
     349        54592 :         airLoopConv.HVACEnergyNotConverged[iCall] = false;
     350        54592 :         if (state.dataContaminantBalance->Contaminant.CO2Simulation) {
     351            0 :             airLoopConv.HVACCO2NotConverged[iCall] = false;
     352              :         }
     353        54592 :         if (state.dataContaminantBalance->Contaminant.GenericContamSimulation) {
     354            0 :             airLoopConv.HVACGenContamNotConverged[iCall] = false;
     355              :         }
     356              : 
     357        54592 :         TmpRealARR = airLoopConv.HVACFlowSupplyDeck2ToDemandTolValue;
     358        54592 :         airLoopConv.HVACFlowSupplyDeck2ToDemandTolValue[0] =
     359        54592 :             std::abs(state.dataLoopNodes->Node(OutletNode).MassFlowRate - thisInletNode.MassFlowRate);
     360       545920 :         for (int logIndex = 1; logIndex < DataConvergParams::ConvergLogStackDepth; logIndex++) {
     361       491328 :             airLoopConv.HVACFlowSupplyDeck2ToDemandTolValue[logIndex] = TmpRealARR[logIndex - 1];
     362              :         }
     363        54592 :         if (airLoopConv.HVACFlowSupplyDeck2ToDemandTolValue[0] > DataConvergParams::HVACFlowRateToler) {
     364        18390 :             airLoopConv.HVACMassFlowNotConverged[iCall] = true;
     365        18390 :             OutOfToleranceFlag = true; // Something has changed--resimulate the other side of the loop
     366              :         }
     367              : 
     368        54592 :         TmpRealARR = airLoopConv.HVACHumSupplyDeck2ToDemandTolValue;
     369        54592 :         airLoopConv.HVACHumSupplyDeck2ToDemandTolValue[0] = std::abs(state.dataLoopNodes->Node(OutletNode).HumRat - thisInletNode.HumRat);
     370       545920 :         for (int logIndex = 1; logIndex < DataConvergParams::ConvergLogStackDepth; logIndex++) {
     371       491328 :             airLoopConv.HVACHumSupplyDeck2ToDemandTolValue[logIndex] = TmpRealARR[logIndex - 1];
     372              :         }
     373        54592 :         if (airLoopConv.HVACHumSupplyDeck2ToDemandTolValue[0] > DataConvergParams::HVACHumRatToler) {
     374         6705 :             airLoopConv.HVACHumRatNotConverged[iCall] = true;
     375         6705 :             OutOfToleranceFlag = true; // Something has changed--resimulate the other side of the loop
     376              :         }
     377              : 
     378        54592 :         TmpRealARR = airLoopConv.HVACTempSupplyDeck2ToDemandTolValue;
     379        54592 :         airLoopConv.HVACTempSupplyDeck2ToDemandTolValue[0] = std::abs(state.dataLoopNodes->Node(OutletNode).Temp - thisInletNode.Temp);
     380       545920 :         for (int logIndex = 1; logIndex < DataConvergParams::ConvergLogStackDepth; logIndex++) {
     381       491328 :             airLoopConv.HVACTempSupplyDeck2ToDemandTolValue[logIndex] = TmpRealARR[logIndex - 1];
     382              :         }
     383        54592 :         if (airLoopConv.HVACTempSupplyDeck2ToDemandTolValue[0] > DataConvergParams::HVACTemperatureToler) {
     384        11282 :             airLoopConv.HVACTempNotConverged[iCall] = true;
     385        11282 :             OutOfToleranceFlag = true; // Something has changed--resimulate the other side of the loop
     386              :         }
     387              : 
     388        54592 :         TmpRealARR = airLoopConv.HVACEnergySupplyDeck2ToDemandTolValue;
     389        54592 :         airLoopConv.HVACEnergySupplyDeck2ToDemandTolValue[0] = DeltaEnergy;
     390       545920 :         for (int logIndex = 1; logIndex < DataConvergParams::ConvergLogStackDepth; logIndex++) {
     391       491328 :             airLoopConv.HVACEnergySupplyDeck2ToDemandTolValue[logIndex] = TmpRealARR[logIndex - 1];
     392              :         }
     393        54592 :         if (std::abs(DeltaEnergy) > DataConvergParams::HVACEnergyToler) {
     394        20338 :             airLoopConv.HVACEnergyNotConverged[iCall] = true;
     395        20338 :             OutOfToleranceFlag = true; // Something has changed--resimulate the other side of the loop
     396              :         }
     397              : 
     398        54592 :         TmpRealARR = airLoopConv.HVACEnthalpySupplyDeck2ToDemandTolValue;
     399        54592 :         airLoopConv.HVACEnthalpySupplyDeck2ToDemandTolValue[0] = std::abs(state.dataLoopNodes->Node(OutletNode).Enthalpy - thisInletNode.Enthalpy);
     400       545920 :         for (int logIndex = 1; logIndex < DataConvergParams::ConvergLogStackDepth; logIndex++) {
     401       491328 :             airLoopConv.HVACEnthalpySupplyDeck2ToDemandTolValue[logIndex] = TmpRealARR[logIndex - 1];
     402              :         }
     403        54592 :         if (airLoopConv.HVACEnthalpySupplyDeck2ToDemandTolValue[0] > DataConvergParams::HVACEnthalpyToler) {
     404        12263 :             OutOfToleranceFlag = true; // Something has changed--resimulate the other side of the loop
     405              :         }
     406              : 
     407        54592 :         TmpRealARR = airLoopConv.HVACPressueSupplyDeck2ToDemandTolValue;
     408        54592 :         airLoopConv.HVACPressueSupplyDeck2ToDemandTolValue[0] = std::abs(state.dataLoopNodes->Node(OutletNode).Press - thisInletNode.Press);
     409       545920 :         for (int logIndex = 1; logIndex < DataConvergParams::ConvergLogStackDepth; logIndex++) {
     410       491328 :             airLoopConv.HVACPressueSupplyDeck2ToDemandTolValue[logIndex] = TmpRealARR[logIndex - 1];
     411              :         }
     412        54592 :         if (airLoopConv.HVACPressueSupplyDeck2ToDemandTolValue[0] > DataConvergParams::HVACPressToler) {
     413          108 :             OutOfToleranceFlag = true; // Something has changed--resimulate the other side of the loop
     414              :         }
     415              : 
     416        54592 :         if (state.dataContaminantBalance->Contaminant.CO2Simulation) {
     417            0 :             TmpRealARR = airLoopConv.HVACCO2SupplyDeck2ToDemandTolValue;
     418            0 :             airLoopConv.HVACCO2SupplyDeck2ToDemandTolValue[0] = std::abs(state.dataLoopNodes->Node(OutletNode).CO2 - thisInletNode.CO2);
     419            0 :             for (int logIndex = 1; logIndex < DataConvergParams::ConvergLogStackDepth; logIndex++) {
     420            0 :                 airLoopConv.HVACCO2SupplyDeck2ToDemandTolValue[logIndex] = TmpRealARR[logIndex - 1];
     421              :             }
     422            0 :             if (airLoopConv.HVACCO2SupplyDeck2ToDemandTolValue[0] > DataConvergParams::HVACCO2Toler) {
     423            0 :                 airLoopConv.HVACCO2NotConverged[iCall] = true;
     424            0 :                 OutOfToleranceFlag = true; // Something has changed--resimulate the other side of the loop
     425              :             }
     426              :         }
     427        54592 :         if (state.dataContaminantBalance->Contaminant.GenericContamSimulation) {
     428            0 :             TmpRealARR = airLoopConv.HVACGenContamSupplyDeck2ToDemandTolValue;
     429            0 :             airLoopConv.HVACGenContamSupplyDeck2ToDemandTolValue[0] =
     430            0 :                 std::abs(state.dataLoopNodes->Node(OutletNode).GenContam - thisInletNode.GenContam);
     431            0 :             for (int logIndex = 1; logIndex < DataConvergParams::ConvergLogStackDepth; logIndex++) {
     432            0 :                 airLoopConv.HVACGenContamSupplyDeck2ToDemandTolValue[logIndex] = TmpRealARR[logIndex - 1];
     433              :             }
     434            0 :             if (airLoopConv.HVACGenContamSupplyDeck2ToDemandTolValue[0] > DataConvergParams::HVACGenContamToler) {
     435            0 :                 airLoopConv.HVACGenContamNotConverged[iCall] = true;
     436            0 :                 OutOfToleranceFlag = true; // Something has changed--resimulate the other side of the loop
     437              :             }
     438              :         }
     439              :     }
     440              : 
     441              :     // Always update the new inlet conditions
     442     25928737 :     thisInletNode.Temp = state.dataLoopNodes->Node(OutletNode).Temp;
     443     25928737 :     thisInletNode.MassFlowRate = state.dataLoopNodes->Node(OutletNode).MassFlowRate;
     444     25928737 :     thisInletNode.MassFlowRateMinAvail = state.dataLoopNodes->Node(OutletNode).MassFlowRateMinAvail;
     445     25928737 :     thisInletNode.MassFlowRateMaxAvail = state.dataLoopNodes->Node(OutletNode).MassFlowRateMaxAvail;
     446     25928737 :     thisInletNode.Quality = state.dataLoopNodes->Node(OutletNode).Quality;
     447     25928737 :     thisInletNode.Press = state.dataLoopNodes->Node(OutletNode).Press;
     448     25928737 :     thisInletNode.Enthalpy = state.dataLoopNodes->Node(OutletNode).Enthalpy;
     449     25928737 :     thisInletNode.HumRat = state.dataLoopNodes->Node(OutletNode).HumRat;
     450              : 
     451     25928737 :     if (state.dataContaminantBalance->Contaminant.CO2Simulation) {
     452        81011 :         thisInletNode.CO2 = state.dataLoopNodes->Node(OutletNode).CO2;
     453              :     }
     454              : 
     455     25928737 :     if (state.dataContaminantBalance->Contaminant.GenericContamSimulation) {
     456        23209 :         thisInletNode.GenContam = state.dataLoopNodes->Node(OutletNode).GenContam;
     457              :     }
     458              : }
     459              : 
     460     36489570 : void UpdatePlantLoopInterface(EnergyPlusData &state,
     461              :                               PlantLocation const &plantLoc,    // The 'outlet node' Location
     462              :                               int const ThisLoopSideOutletNode, // Node number for the inlet of the side that needs the outlet node data
     463              :                               int const OtherLoopSideInletNode, // Node number for the outlet of the side of the loop just simulated
     464              :                               bool &OutOfToleranceFlag,         // True when the other side of the loop need to be (re)simulated
     465              :                               DataPlant::CommonPipeType const CommonPipeType)
     466              : {
     467              : 
     468              :     // SUBROUTINE INFORMATION:
     469              :     //       AUTHOR         Rick Strand
     470              :     //       DATE WRITTEN   October 1998
     471              :     //       MODIFIED       na
     472              :     //       RE-ENGINEERED  Brent Griffith, Sept. 2010
     473              :     //       RE-ENGINEERED  Dan Fisher,     Sept. 2010
     474              : 
     475              :     // PURPOSE OF THIS SUBROUTINE:
     476              :     // This subroutine manages any generic HVAC loop interface.
     477              : 
     478              :     // METHODOLOGY EMPLOYED:
     479              :     // This is a simple "forward" interface where all of the properties
     480              :     // from the outlet of one side of the loop get transfered
     481              :     // to the inlet node of the corresponding other side of the loop.
     482              :     // Temperatures are 'lagged' by loop capacitance (i.e. a 'tank')
     483              :     // between the outlet and inlet nodes.
     484              :     // the update from the demand side to the supply side always triggers
     485              :     // resimulation of the supply side if any state variable (or energy) is
     486              :     // out of tolerance.  Remsimulation of the demand side is only triggered if
     487              :     // flow or energy are out of tolerance.  This in effect checks flow and
     488              :     // ~.25C temperature difference.
     489              : 
     490              :     // SUBROUTINE PARAMETER DEFINITIONS:
     491              :     static constexpr std::string_view RoutineName("UpdatePlantLoopInterface");
     492              : 
     493     36489570 :     int LoopNum = plantLoc.loopNum;
     494     36489570 :     DataPlant::LoopSideLocation ThisLoopSideNum = plantLoc.loopSideNum;
     495     36489570 :     auto &convergence(state.dataConvergeParams->PlantConvergence(LoopNum));
     496              : 
     497              :     // reset out of tolerance flags
     498     36489570 :     convergence.PlantMassFlowNotConverged = false;
     499     36489570 :     convergence.PlantTempNotConverged = false;
     500              : 
     501              :     // set the LoopSide inlet node
     502     36489570 :     int ThisLoopSideInletNode = state.dataPlnt->PlantLoop(LoopNum).LoopSide(ThisLoopSideNum).NodeNumIn;
     503              : 
     504              :     // save the inlet node temp for DeltaEnergy check
     505     36489570 :     Real64 OldOtherLoopSideInletMdot = state.dataLoopNodes->Node(OtherLoopSideInletNode).MassFlowRate;
     506     36489570 :     Real64 OldTankOutletTemp = state.dataLoopNodes->Node(OtherLoopSideInletNode).Temp;
     507              : 
     508              :     // calculate the specific heat
     509     36489570 :     Real64 Cp = state.dataPlnt->PlantLoop(LoopNum).glycol->getSpecificHeat(state, OldTankOutletTemp, RoutineName);
     510              : 
     511              :     // update the enthalpy
     512     36489570 :     state.dataLoopNodes->Node(OtherLoopSideInletNode).Enthalpy = Cp * state.dataLoopNodes->Node(OtherLoopSideInletNode).Temp;
     513              : 
     514              :     // update the temperatures and flow rates
     515     36489570 :     auto &flow_demand_to_supply_tol(convergence.PlantFlowDemandToSupplyTolValue);
     516     36489570 :     auto &flow_supply_to_demand_tol(convergence.PlantFlowSupplyToDemandTolValue);
     517              :     Real64 MixedOutletTemp;
     518              :     Real64 TankOutletTemp;
     519     36489570 :     if (CommonPipeType == DataPlant::CommonPipeType::Single || CommonPipeType == DataPlant::CommonPipeType::TwoWay) {
     520              :         // update the temperature
     521       687288 :         UpdateCommonPipe(state, plantLoc, CommonPipeType, MixedOutletTemp);
     522       687288 :         state.dataLoopNodes->Node(OtherLoopSideInletNode).Temp = MixedOutletTemp;
     523       687288 :         TankOutletTemp = MixedOutletTemp;
     524       687288 :         if (ThisLoopSideNum == DataPlant::LoopSideLocation::Demand) {
     525       343644 :             rshift1(flow_demand_to_supply_tol);
     526       343644 :             flow_demand_to_supply_tol[0] = std::abs(OldOtherLoopSideInletMdot - state.dataLoopNodes->Node(OtherLoopSideInletNode).MassFlowRate);
     527       343644 :             if (flow_demand_to_supply_tol[0] > DataConvergParams::PlantFlowRateToler) {
     528            0 :                 convergence.PlantMassFlowNotConverged = true;
     529              :             }
     530              :         } else {
     531       343644 :             rshift1(flow_supply_to_demand_tol);
     532       343644 :             flow_supply_to_demand_tol[0] = std::abs(OldOtherLoopSideInletMdot - state.dataLoopNodes->Node(OtherLoopSideInletNode).MassFlowRate);
     533       343644 :             if (flow_supply_to_demand_tol[0] > DataConvergParams::PlantFlowRateToler) {
     534            0 :                 convergence.PlantMassFlowNotConverged = true;
     535              :             }
     536              :         }
     537              :         // Set the flow rate.  Continuity requires that the flow rates at the half loop inlet and outlet match
     538       687288 :         state.dataLoopNodes->Node(ThisLoopSideInletNode).MassFlowRate = state.dataLoopNodes->Node(ThisLoopSideOutletNode).MassFlowRate;
     539              :         // Update this LoopSide inlet node Min/MaxAvail to this LoopSide outlet node Min/MaxAvail
     540       687288 :         state.dataLoopNodes->Node(ThisLoopSideInletNode).MassFlowRateMinAvail =
     541       687288 :             state.dataLoopNodes->Node(ThisLoopSideOutletNode).MassFlowRateMinAvail;
     542       687288 :         state.dataLoopNodes->Node(ThisLoopSideInletNode).MassFlowRateMaxAvail =
     543       687288 :             state.dataLoopNodes->Node(ThisLoopSideOutletNode).MassFlowRateMaxAvail;
     544              : 
     545              :     } else { // no common pipe
     546     35802282 :         UpdateHalfLoopInletTemp(state, LoopNum, ThisLoopSideNum, TankOutletTemp);
     547              :         // update the temperature
     548     35802282 :         state.dataLoopNodes->Node(OtherLoopSideInletNode).Temp = TankOutletTemp;
     549              :         // Set the flow tolerance array
     550     35802282 :         if (ThisLoopSideNum == DataPlant::LoopSideLocation::Demand) {
     551     17906090 :             rshift1(flow_demand_to_supply_tol);
     552     35812180 :             flow_demand_to_supply_tol[0] = std::abs(state.dataLoopNodes->Node(ThisLoopSideOutletNode).MassFlowRate -
     553     17906090 :                                                     state.dataLoopNodes->Node(OtherLoopSideInletNode).MassFlowRate);
     554     17906090 :             if (flow_demand_to_supply_tol[0] > DataConvergParams::PlantFlowRateToler) {
     555      1778528 :                 convergence.PlantMassFlowNotConverged = true;
     556              :             }
     557              :         } else {
     558     17896192 :             rshift1(flow_supply_to_demand_tol);
     559     35792384 :             flow_supply_to_demand_tol[0] = std::abs(state.dataLoopNodes->Node(ThisLoopSideOutletNode).MassFlowRate -
     560     17896192 :                                                     state.dataLoopNodes->Node(OtherLoopSideInletNode).MassFlowRate);
     561     17896192 :             if (flow_supply_to_demand_tol[0] > DataConvergParams::PlantFlowRateToler) {
     562       623558 :                 convergence.PlantMassFlowNotConverged = true;
     563              :             }
     564              :         }
     565              :         //    PlantFlowTolValue(PlantQuePtr)  = ABS(Node(ThisLoopSideOutletNode)%MassFlowRate-Node(OtherLoopSideInletNode)%MassFlowRate)
     566              :         // Set the flow rate
     567     35802282 :         state.dataLoopNodes->Node(OtherLoopSideInletNode).MassFlowRate = state.dataLoopNodes->Node(ThisLoopSideOutletNode).MassFlowRate;
     568              :         // update the MIN/MAX available flow rates
     569     35802282 :         state.dataLoopNodes->Node(OtherLoopSideInletNode).MassFlowRateMinAvail =
     570     35802282 :             state.dataLoopNodes->Node(ThisLoopSideOutletNode).MassFlowRateMinAvail;
     571     35802282 :         state.dataLoopNodes->Node(OtherLoopSideInletNode).MassFlowRateMaxAvail =
     572     35802282 :             state.dataLoopNodes->Node(ThisLoopSideOutletNode).MassFlowRateMaxAvail;
     573              :         // update Quality.  Note: This update assumes that STEAM cannot be used with common pipes.
     574     35802282 :         state.dataLoopNodes->Node(OtherLoopSideInletNode).Quality = state.dataLoopNodes->Node(ThisLoopSideOutletNode).Quality;
     575              :         // pressure update  Note: This update assumes that PRESSURE SIMULATION cannot be used with common pipes.
     576     35802282 :         if (state.dataPlnt->PlantLoop(LoopNum).HasPressureComponents) {
     577              :             // Don't update pressure, let the pressure simulation handle pressures
     578              :         } else {
     579              :             // Do update pressure!
     580     35781222 :             state.dataLoopNodes->Node(OtherLoopSideInletNode).Press = state.dataLoopNodes->Node(ThisLoopSideOutletNode).Press;
     581              :         }
     582              :     }
     583              : 
     584              :     // temperature
     585     36489570 :     if (ThisLoopSideNum == DataPlant::LoopSideLocation::Demand) {
     586     18249734 :         auto &temp_demand_to_supply_tol(convergence.PlantTempDemandToSupplyTolValue);
     587     18249734 :         rshift1(temp_demand_to_supply_tol);
     588     18249734 :         temp_demand_to_supply_tol[0] = std::abs(OldTankOutletTemp - state.dataLoopNodes->Node(OtherLoopSideInletNode).Temp);
     589     18249734 :         if (temp_demand_to_supply_tol[0] > DataConvergParams::PlantTemperatureToler) {
     590      4414358 :             convergence.PlantTempNotConverged = true;
     591              :         }
     592              :     } else {
     593     18239836 :         auto &temp_supply_to_demand_tol(convergence.PlantTempSupplyToDemandTolValue);
     594     18239836 :         rshift1(temp_supply_to_demand_tol);
     595     18239836 :         temp_supply_to_demand_tol[0] = std::abs(OldTankOutletTemp - state.dataLoopNodes->Node(OtherLoopSideInletNode).Temp);
     596     18239836 :         if (temp_supply_to_demand_tol[0] > DataConvergParams::PlantTemperatureToler) {
     597      2068392 :             convergence.PlantTempNotConverged = true;
     598              :         }
     599              :     }
     600              : 
     601              :     // Set out of tolerance flags
     602     36489570 :     if (ThisLoopSideNum == DataPlant::LoopSideLocation::Demand) {
     603     18249734 :         if (convergence.PlantMassFlowNotConverged || convergence.PlantTempNotConverged) {
     604      4815408 :             OutOfToleranceFlag = true;
     605              :         }
     606              :     } else {
     607     18239836 :         if (convergence.PlantMassFlowNotConverged) {
     608       623558 :             OutOfToleranceFlag = true;
     609              :         }
     610              :     }
     611     36489570 : }
     612              : 
     613     35802282 : void UpdateHalfLoopInletTemp(EnergyPlusData &state, int const LoopNum, const DataPlant::LoopSideLocation TankInletLoopSide, Real64 &TankOutletTemp)
     614              : {
     615              : 
     616              :     // SUBROUTINE INFORMATION:
     617              :     //       AUTHOR         Rick Strand
     618              :     //       DATE WRITTEN   September 2001
     619              :     //       MODIFIED       Simon Rees, July 2007
     620              :     //                      Brent Griffith, Feb. 2010, add LoopNum arg
     621              :     //       RE-ENGINEERED  Brent Griffith, Sept 2010, generalize for both loop sides
     622              :     //                                           add pump heat from other loop
     623              :     //                      B.Griffith and L.Gu, Oct 2011, solve via analytical soln, use average over timestep
     624              : 
     625              :     // PURPOSE OF THIS SUBROUTINE:
     626              :     // This subroutine calculates the new loop side inlet temperature
     627              :     // based on the previous temperature of the mixed tank, mass flow rate and the new
     628              :     // outlet temperature on the supply side.  The temperature does not
     629              :     // pass directly across because the loop has some capacitance. It is
     630              :     // called separately but used for both supply-to-demand, and demand-to-supply
     631              : 
     632              :     // METHODOLOGY EMPLOYED:
     633              :     // This uses a analytical solution for changes in the
     634              :     // fluid loop temperature.  The user defines some volume of fluid
     635              :     // for the loop which gets converted to a fixed amount of mass.
     636              :     // The loop side inlet node is modeled as the outlet of a fully mixed
     637              :     // tank. Note that this routine is called repeatedly to re calculate
     638              :     // loop capacitance based on current plant conditions
     639              : 
     640     35802282 :     Real64 SysTimeElapsed = state.dataHVACGlobal->SysTimeElapsed;
     641              : 
     642              :     // SUBROUTINE PARAMETER DEFINITIONS:
     643     35802282 :     Real64 constexpr FracTotLoopMass(0.5); // Fraction of total loop mass assigned to the half loop
     644              :     static constexpr std::string_view RoutineName("UpdateHalfLoopInletTemp");
     645              : 
     646              :     // find tank inlet and outlet nodes
     647     35802282 :     DataPlant::LoopSideLocation TankOutletLoopSide = DataPlant::LoopSideOther[static_cast<int>(TankInletLoopSide)];
     648     35802282 :     int TankInletNode = state.dataPlnt->PlantLoop(LoopNum).LoopSide(TankInletLoopSide).NodeNumOut;
     649     35802282 :     Real64 TankInletTemp = state.dataLoopNodes->Node(TankInletNode).Temp;
     650              : 
     651              :     // This needs to be based on time to deal with system downstepping and repeated timesteps
     652     35802282 :     Real64 TimeElapsed = (state.dataGlobal->HourOfDay - 1) + state.dataGlobal->TimeStep * state.dataGlobal->TimeStepZone + SysTimeElapsed;
     653     35802282 :     if (state.dataPlnt->PlantLoop(LoopNum).LoopSide(TankOutletLoopSide).TimeElapsed != TimeElapsed) {
     654      6748350 :         state.dataPlnt->PlantLoop(LoopNum).LoopSide(TankOutletLoopSide).LastTempInterfaceTankOutlet =
     655      6748350 :             state.dataPlnt->PlantLoop(LoopNum).LoopSide(TankOutletLoopSide).TempInterfaceTankOutlet;
     656      6748350 :         state.dataPlnt->PlantLoop(LoopNum).LoopSide(TankOutletLoopSide).TimeElapsed = TimeElapsed;
     657              :     }
     658              : 
     659     35802282 :     Real64 LastTankOutletTemp = state.dataPlnt->PlantLoop(LoopNum).LoopSide(TankOutletLoopSide).LastTempInterfaceTankOutlet;
     660              : 
     661              :     // calculate the specific heat for the capacitance calculation
     662     35802282 :     Real64 Cp = state.dataPlnt->PlantLoop(LoopNum).glycol->getSpecificHeat(state, LastTankOutletTemp, RoutineName);
     663              :     // set the fraction of loop mass assigned to each half loop outlet capacitance ('tank') calculation
     664              : 
     665              :     // calculate new loop inlet temperature.  The calculation is a simple 'tank' (thermal capacitance) calculation that includes:
     666              :     //--half of loop mass.  The other half is accounted for at the other half loop interface
     667              :     //--pump heat.  Pump heat for a single loop setpoint with pumps only on the supply side is added at the supply side inlet.
     668              :     //   Pump heat for a dual setpoint loop is added to each loop side inlet
     669              :     //  The previous tank temperature value is used to prevent accumulation of pump heat during iterations while recalculating
     670              :     // tank conditions each call.
     671              :     // Analytical solution for ODE, formulated for both final tank temp and average tank temp.
     672              : 
     673     35802282 :     Real64 TimeStepSeconds = state.dataHVACGlobal->TimeStepSysSec;
     674     35802282 :     Real64 MassFlowRate = state.dataLoopNodes->Node(TankInletNode).MassFlowRate;
     675     35802282 :     Real64 PumpHeat = state.dataPlnt->PlantLoop(LoopNum).LoopSide(TankOutletLoopSide).TotalPumpHeat;
     676     35802282 :     Real64 ThisTankMass = FracTotLoopMass * state.dataPlnt->PlantLoop(LoopNum).Mass;
     677              :     Real64 TankFinalTemp;
     678              :     Real64 TankAverageTemp;
     679     35802282 :     if (ThisTankMass <= 0.0) { // no mass, no plant loop volume
     680            0 :         if (MassFlowRate > 0.0) {
     681            0 :             TankFinalTemp = TankInletTemp + PumpHeat / (MassFlowRate * Cp);
     682            0 :             TankAverageTemp = (TankFinalTemp + LastTankOutletTemp) / 2.0;
     683              :         } else {
     684            0 :             TankFinalTemp = LastTankOutletTemp;
     685            0 :             TankAverageTemp = LastTankOutletTemp;
     686              :         }
     687              : 
     688              :     } else { // tank has mass
     689     35802282 :         if (MassFlowRate > 0.0) {
     690     19162288 :             Real64 const mdotCp = MassFlowRate * Cp;
     691     19162288 :             Real64 const mdotCpTempIn = mdotCp * TankInletTemp;
     692     19162288 :             Real64 const tankMassCp = ThisTankMass * Cp;
     693     19162288 :             Real64 const ExponentTerm = mdotCp / tankMassCp * TimeStepSeconds;
     694     19162288 :             if (ExponentTerm >= 700.0) {
     695            0 :                 TankFinalTemp = (mdotCp * TankInletTemp + PumpHeat) / mdotCp;
     696              : 
     697            0 :                 TankAverageTemp = (tankMassCp / mdotCp * (LastTankOutletTemp - (mdotCpTempIn + PumpHeat) / mdotCp) / TimeStepSeconds +
     698            0 :                                    (mdotCpTempIn + PumpHeat) / mdotCp);
     699              :             } else {
     700     19162288 :                 TankFinalTemp = (LastTankOutletTemp - (mdotCpTempIn + PumpHeat) / mdotCp) * std::exp(-ExponentTerm) +
     701     19162288 :                                 (mdotCpTempIn + PumpHeat) / (MassFlowRate * Cp);
     702              : 
     703     19162288 :                 TankAverageTemp = (tankMassCp / mdotCp * (LastTankOutletTemp - (mdotCpTempIn + PumpHeat) / mdotCp) * (1.0 - std::exp(-ExponentTerm)) /
     704              :                                        TimeStepSeconds +
     705     19162288 :                                    (mdotCpTempIn + PumpHeat) / mdotCp);
     706              :             }
     707              :         } else {
     708     16639994 :             TankFinalTemp = PumpHeat / (ThisTankMass * Cp) * TimeStepSeconds + LastTankOutletTemp;
     709     16639994 :             TankAverageTemp = (TankFinalTemp + LastTankOutletTemp) / 2.0;
     710              :         }
     711              :     }
     712              : 
     713              :     // update last tank outlet temperature
     714     35802282 :     state.dataPlnt->PlantLoop(LoopNum).LoopSide(TankOutletLoopSide).TempInterfaceTankOutlet = TankFinalTemp;
     715              : 
     716              :     // update heat transport and heat storage rates
     717     35802282 :     state.dataPlnt->PlantLoop(LoopNum).LoopSide(TankOutletLoopSide).LoopSideInlet_MdotCpDeltaT =
     718     35802282 :         (TankInletTemp - TankAverageTemp) * Cp * MassFlowRate;
     719     35802282 :     state.dataPlnt->PlantLoop(LoopNum).LoopSide(TankOutletLoopSide).LoopSideInlet_McpDTdt =
     720     35802282 :         (ThisTankMass * Cp * (TankFinalTemp - LastTankOutletTemp)) / TimeStepSeconds;
     721              : 
     722              :     // update report variable
     723     35802282 :     state.dataPlnt->PlantLoop(LoopNum).LoopSide(TankOutletLoopSide).LoopSideInlet_TankTemp = TankAverageTemp;
     724              : 
     725     35802282 :     TankOutletTemp = TankAverageTemp;
     726     35802282 : }
     727              : 
     728       687288 : void UpdateCommonPipe(EnergyPlusData &state,
     729              :                       PlantLocation const &TankInletPlantLoc,
     730              :                       DataPlant::CommonPipeType const CommonPipeType,
     731              :                       Real64 &MixedOutletTemp)
     732              : {
     733              : 
     734              :     // SUBROUTINE INFORMATION:
     735              :     //       AUTHOR         Rick Strand
     736              :     //       DATE WRITTEN   September 2001
     737              :     //       MODIFIED       Simon Rees, July 2007
     738              :     //                      Brent Griffith, Feb. 2010, add LoopNum arg
     739              :     //       RE-ENGINEERED  Brent Griffith, Sept 2010, generalize for both loop sides
     740              :     //                                           add pump heat from other loop
     741              :     //                      B.Griffith and L.Gu, Oct 2011, solve via analytical soln, use average over timestep
     742              : 
     743              :     // PURPOSE OF THIS SUBROUTINE:
     744              :     // This subroutine calculates the new loop side inlet temperature
     745              :     // based on the previous temperature of the mixed tank, mass flow rate and the new
     746              :     // outlet temperature on the supply side.  The temperature does not
     747              :     // pass directly across because the loop has some capacitance. It is
     748              :     // called separately but used for both supply-to-demand, and demand-to-supply
     749              : 
     750              :     // METHODOLOGY EMPLOYED:
     751              :     // This uses a analytical solution for changes in the
     752              :     // fluid loop temperature.  The user defines some volume of fluid
     753              :     // for the loop which gets converted to a fixed amount of mass.
     754              :     // The loop side inlet node is modeled as the outlet of a fully mixed
     755              :     // tank. Note that this routine is called repeatedly to re calculate
     756              :     // loop capacitance based on current plant conditions
     757              : 
     758              :     // Using/Aliasing
     759       687288 :     Real64 SysTimeElapsed = state.dataHVACGlobal->SysTimeElapsed;
     760              : 
     761              :     // SUBROUTINE PARAMETER DEFINITIONS:
     762              :     static constexpr std::string_view RoutineName("UpdateCommonPipe");
     763              : 
     764              :     // find tank inlet and outlet nodes
     765       687288 :     int LoopNum = TankInletPlantLoc.loopNum;
     766       687288 :     DataPlant::LoopSideLocation TankInletLoopSide = TankInletPlantLoc.loopSideNum;
     767       687288 :     DataPlant::LoopSideLocation TankOutletLoopSide = DataPlant::LoopSideOther[static_cast<int>(TankInletPlantLoc.loopSideNum)]; // Outlet loopside
     768       687288 :     int TankInletNode = state.dataPlnt->PlantLoop(LoopNum).LoopSide(TankInletLoopSide).NodeNumOut;
     769       687288 :     int TankOutletNode = state.dataPlnt->PlantLoop(LoopNum).LoopSide(TankOutletLoopSide).NodeNumIn;
     770              : 
     771       687288 :     Real64 TankInletTemp = state.dataLoopNodes->Node(TankInletNode).Temp;
     772              : 
     773              :     Real64 FracTotLoopMass; // Fraction of total loop mass assigned to the half loop
     774       687288 :     if (TankInletLoopSide == DataPlant::LoopSideLocation::Demand) {
     775              :         // for common pipe loops, assume 75% of plant loop volume is on the demand side
     776       343644 :         FracTotLoopMass = 0.25;
     777              :     } else {
     778       343644 :         FracTotLoopMass = 0.75;
     779              :     }
     780              : 
     781              :     // This needs to be based on time to deal with system downstepping and repeated timesteps
     782       687288 :     Real64 TimeElapsed = (state.dataGlobal->HourOfDay - 1) + state.dataGlobal->TimeStep * state.dataGlobal->TimeStepZone + SysTimeElapsed;
     783       687288 :     if (state.dataPlnt->PlantLoop(LoopNum).LoopSide(TankOutletLoopSide).TimeElapsed != TimeElapsed) {
     784        44218 :         state.dataPlnt->PlantLoop(LoopNum).LoopSide(TankOutletLoopSide).LastTempInterfaceTankOutlet =
     785        44218 :             state.dataPlnt->PlantLoop(LoopNum).LoopSide(TankOutletLoopSide).TempInterfaceTankOutlet;
     786        44218 :         state.dataPlnt->PlantLoop(LoopNum).LoopSide(TankOutletLoopSide).TimeElapsed = TimeElapsed;
     787              :     }
     788              : 
     789       687288 :     Real64 LastTankOutletTemp = state.dataPlnt->PlantLoop(LoopNum).LoopSide(TankOutletLoopSide).LastTempInterfaceTankOutlet;
     790              : 
     791              :     // calculate the specific heat for the capacitance calculation
     792       687288 :     Real64 Cp = state.dataPlnt->PlantLoop(LoopNum).glycol->getSpecificHeat(state, LastTankOutletTemp, RoutineName);
     793              : 
     794              :     // set the fraction of loop mass assigned to each half loop outlet capacitance ('tank') calculation
     795              : 
     796              :     // calculate new loop inlet temperature.  The calculation is a simple 'tank' (thermal capacitance) calculation that includes:
     797              :     //--half of loop mass.  The other half is accounted for at the other half loop interface
     798              :     //--pump heat.  Pump heat for a single loop setpoint with pumps only on the supply side is added at the supply side inlet.
     799              :     // Pump heat for a dual setpoint loop is added to each loop side inlet
     800              :     // The previous inlet side temp,'ThisLoopSideTankOutletTemp' is used to prevent accumulation of pump heat during iterations.
     801              :     // The placement of the 'tank' for common pipes is *after* the outlet node and *before* the flow split or flow mixing.
     802              :     // This requires no logical check in the code since for purposes of temperature calculations, it is identical to the
     803              :     // no common pipe case.
     804              :     // calculation is separated because for common pipe, a different split for mass fraction is applied
     805              :     // The pump heat source is swapped around here compared to no common pipe (so pump heat sort stays on its own side).
     806       687288 :     Real64 TimeStepSeconds = state.dataHVACGlobal->TimeStepSysSec;
     807       687288 :     Real64 MassFlowRate = state.dataLoopNodes->Node(TankInletNode).MassFlowRate;
     808       687288 :     Real64 PumpHeat = state.dataPlnt->PlantLoop(LoopNum).LoopSide(TankInletLoopSide).TotalPumpHeat;
     809       687288 :     Real64 ThisTankMass = FracTotLoopMass * state.dataPlnt->PlantLoop(LoopNum).Mass;
     810              : 
     811              :     Real64 TankFinalTemp;
     812              :     Real64 TankAverageTemp;
     813       687288 :     if (ThisTankMass <= 0.0) { // no mass, no plant loop volume
     814            0 :         if (MassFlowRate > 0.0) {
     815            0 :             TankFinalTemp = TankInletTemp + PumpHeat / (MassFlowRate * Cp);
     816            0 :             TankAverageTemp = (TankFinalTemp + LastTankOutletTemp) / 2.0;
     817              :         } else {
     818            0 :             TankFinalTemp = LastTankOutletTemp;
     819            0 :             TankAverageTemp = LastTankOutletTemp;
     820              :         }
     821              : 
     822              :     } else { // tank has mass
     823       687288 :         if (MassFlowRate > 0.0) {
     824       302197 :             TankFinalTemp = (LastTankOutletTemp - (MassFlowRate * Cp * TankInletTemp + PumpHeat) / (MassFlowRate * Cp)) *
     825       302197 :                                 std::exp(-(MassFlowRate * Cp) / (ThisTankMass * Cp) * TimeStepSeconds) +
     826       302197 :                             (MassFlowRate * Cp * TankInletTemp + PumpHeat) / (MassFlowRate * Cp);
     827       302197 :             TankAverageTemp = ((ThisTankMass * Cp) / (MassFlowRate * Cp) *
     828       302197 :                                    (LastTankOutletTemp - (MassFlowRate * Cp * TankInletTemp + PumpHeat) / (MassFlowRate * Cp)) *
     829       302197 :                                    (1.0 - std::exp(-(MassFlowRate * Cp) / (ThisTankMass * Cp) * TimeStepSeconds)) / TimeStepSeconds +
     830       302197 :                                (MassFlowRate * Cp * TankInletTemp + PumpHeat) / (MassFlowRate * Cp));
     831              :         } else {
     832              : 
     833       385091 :             TankFinalTemp = PumpHeat / (ThisTankMass * Cp) * TimeStepSeconds + LastTankOutletTemp;
     834       385091 :             TankAverageTemp = (TankFinalTemp + LastTankOutletTemp) / 2.0;
     835              :         }
     836              :     }
     837              :     // Common Pipe Simulation
     838       687288 :     if (CommonPipeType == DataPlant::CommonPipeType::Single) {
     839       261156 :         ManageSingleCommonPipe(state, LoopNum, TankOutletLoopSide, TankAverageTemp, MixedOutletTemp);
     840              :         // 2-way (controlled) common pipe simulation
     841       426132 :     } else if (CommonPipeType == DataPlant::CommonPipeType::TwoWay) {
     842       426132 :         PlantLocation TankOutletPlantLoc = {LoopNum, TankOutletLoopSide, 0, 0};
     843              : 
     844       426132 :         ManageTwoWayCommonPipe(state, TankOutletPlantLoc, TankAverageTemp);
     845       426132 :         MixedOutletTemp = state.dataLoopNodes->Node(TankOutletNode).Temp;
     846              :     }
     847              : 
     848       687288 :     state.dataPlnt->PlantLoop(LoopNum).LoopSide(TankOutletLoopSide).TempInterfaceTankOutlet = TankFinalTemp;
     849              : 
     850       687288 :     state.dataPlnt->PlantLoop(LoopNum).LoopSide(TankOutletLoopSide).LoopSideInlet_TankTemp = TankAverageTemp;
     851       687288 : }
     852              : 
     853       261156 : void ManageSingleCommonPipe(EnergyPlusData &state,
     854              :                             int const LoopNum,                          // plant loop number
     855              :                             DataPlant::LoopSideLocation const LoopSide, // plant loop side number
     856              :                             Real64 const TankOutletTemp, // inlet temperature to the common pipe passed in from the capacitance calculation
     857              :                             Real64 &MixedOutletTemp      // inlet temperature to the common pipe passed in from the capacitance calculation
     858              : )
     859              : {
     860              : 
     861              :     // SUBROUTINE INFORMATION:
     862              :     //       AUTHOR         Sankaranarayanan K P
     863              :     //       DATE WRITTEN   November 2006
     864              :     //       MODIFIED       B. Griffith, Jan 2010 clean up setup to allow mixing common pipe modes
     865              :     //                      B. Griffith, Mar 2010 add LoopNum arg and simplify
     866              :     //       RE-ENGINEERED  D. Fisher, Sept. 2010
     867              :     //                      B. Griffith, Oct 2011, major rewrite for plant upgrade
     868              : 
     869              :     // PURPOSE OF THIS SUBROUTINE:
     870              :     // To determine the conditions in common pipe viz., the flow flow temperature and direction of flow.
     871              : 
     872              :     // METHODOLOGY EMPLOYED:
     873              :     // Determine the flow on both sides of the common pipe. Decide if flow is coming into common pipe
     874              :     // or going out of common pipe. After that determine which interface calls the subroutine, i.e. if
     875              :     // called from "Demand to Supply" interface or "Supply to Demand" interface. Update the node temperatures
     876              :     // accordingly.
     877              : 
     878              :     // One time call to set up report variables and set common pipe 'type' flag
     879       261156 :     if (!state.dataHVACInterfaceMgr->CommonPipeSetupFinished) {
     880            4 :         SetupCommonPipes(state);
     881              :     }
     882              : 
     883       261156 :     auto &plantCommonPipe = state.dataHVACInterfaceMgr->PlantCommonPipe(LoopNum);
     884              : 
     885              :     // fill local node indexes
     886       261156 :     int NodeNumPriIn = state.dataPlnt->PlantLoop(LoopNum).LoopSide(DataPlant::LoopSideLocation::Supply).NodeNumIn;
     887       261156 :     int NodeNumPriOut = state.dataPlnt->PlantLoop(LoopNum).LoopSide(DataPlant::LoopSideLocation::Supply).NodeNumOut;
     888       261156 :     int NodeNumSecIn = state.dataPlnt->PlantLoop(LoopNum).LoopSide(DataPlant::LoopSideLocation::Demand).NodeNumIn;
     889       261156 :     int NodeNumSecOut = state.dataPlnt->PlantLoop(LoopNum).LoopSide(DataPlant::LoopSideLocation::Demand).NodeNumOut;
     890              : 
     891       261156 :     if (plantCommonPipe.MyEnvrnFlag && state.dataGlobal->BeginEnvrnFlag) {
     892           18 :         plantCommonPipe.Flow = 0.0;
     893           18 :         plantCommonPipe.Temp = 0.0;
     894           18 :         plantCommonPipe.FlowDir = NoRecircFlow;
     895           18 :         plantCommonPipe.MyEnvrnFlag = false;
     896              :     }
     897       261156 :     if (!state.dataGlobal->BeginEnvrnFlag) {
     898       259812 :         plantCommonPipe.MyEnvrnFlag = true;
     899              :     }
     900              : 
     901              :     // every time inits
     902       261156 :     Real64 MdotSec = state.dataLoopNodes->Node(NodeNumSecOut).MassFlowRate;
     903       261156 :     Real64 MdotPri = state.dataLoopNodes->Node(NodeNumPriOut).MassFlowRate;
     904              : 
     905              :     Real64 TempSecOutTankOut;
     906              :     Real64 TempPriOutTankOut;
     907       261156 :     if (LoopSide == DataPlant::LoopSideLocation::Supply) {
     908       130578 :         TempSecOutTankOut = TankOutletTemp;
     909       130578 :         TempPriOutTankOut = state.dataPlnt->PlantLoop(LoopNum).LoopSide(DataPlant::LoopSideLocation::Demand).LoopSideInlet_TankTemp;
     910              :     } else {
     911       130578 :         TempPriOutTankOut = TankOutletTemp;
     912       130578 :         TempSecOutTankOut = state.dataPlnt->PlantLoop(LoopNum).LoopSide(DataPlant::LoopSideLocation::Supply).LoopSideInlet_TankTemp;
     913              :     }
     914              : 
     915              :     // first do mass balances and find common pipe flow rate and direction
     916              :     Real64 MdotPriRCLeg; // flow rate of primary recirculation thru common pipe kg/s
     917              :     Real64 MdotSecRCLeg; // flow rate of secondary recirculation thru common pipe kg/s
     918              :     Real64 TempSecInlet; // temperature at secondary inlet deg C
     919              :     Real64 TempPriInlet; // temperature at primary inlet deg C
     920              :     int CPFlowDir;       // flow direction in single common pipe
     921              :     Real64 CommonPipeTemp;
     922       261156 :     if (MdotPri > MdotSec) {
     923        72349 :         MdotPriRCLeg = MdotPri - MdotSec;
     924        72349 :         if (MdotPriRCLeg < DataBranchAirLoopPlant::MassFlowTolerance) {
     925            0 :             MdotPriRCLeg = 0.0;
     926            0 :             CPFlowDir = NoRecircFlow;
     927              :         } else {
     928        72349 :             CPFlowDir = PrimaryRecirc;
     929              :         }
     930        72349 :         MdotSecRCLeg = 0.0;
     931        72349 :         CommonPipeTemp = TempPriOutTankOut;
     932       188807 :     } else if (MdotPri < MdotSec) {
     933         5248 :         MdotSecRCLeg = MdotSec - MdotPri;
     934         5248 :         if (MdotSecRCLeg < DataBranchAirLoopPlant::MassFlowTolerance) {
     935            0 :             MdotSecRCLeg = 0.0;
     936            0 :             CPFlowDir = NoRecircFlow;
     937              :         } else {
     938         5248 :             CPFlowDir = SecondaryRecirc;
     939              :         }
     940         5248 :         MdotPriRCLeg = 0.0;
     941         5248 :         CommonPipeTemp = TempSecOutTankOut;
     942              :     } else { // equal
     943       183559 :         MdotPriRCLeg = 0.0;
     944       183559 :         MdotSecRCLeg = 0.0;
     945       183559 :         CPFlowDir = NoRecircFlow;
     946       183559 :         CommonPipeTemp = (TempPriOutTankOut + TempSecOutTankOut) / 2.0;
     947              :     }
     948              : 
     949              :     // now calculate inlet temps
     950              : 
     951       261156 :     if (MdotSec > 0.0) {
     952        78818 :         TempSecInlet = (MdotPri * TempPriOutTankOut + MdotSecRCLeg * TempSecOutTankOut - MdotPriRCLeg * TempPriOutTankOut) / (MdotSec);
     953              :     } else {
     954       182338 :         TempSecInlet = TempPriOutTankOut;
     955              :     }
     956       261156 :     if (MdotPri > 0.0) {
     957        76517 :         TempPriInlet = (MdotSec * TempSecOutTankOut + MdotPriRCLeg * TempPriOutTankOut - MdotSecRCLeg * TempSecOutTankOut) / (MdotPri);
     958              :     } else {
     959       184639 :         TempPriInlet = TempSecOutTankOut;
     960              :     }
     961              : 
     962              :     // Update the Common Pipe Data structure for reporting purposes.
     963       261156 :     plantCommonPipe.Flow = max(MdotPriRCLeg, MdotSecRCLeg);
     964       261156 :     plantCommonPipe.Temp = CommonPipeTemp;
     965       261156 :     plantCommonPipe.FlowDir = CPFlowDir;
     966       261156 :     state.dataLoopNodes->Node(NodeNumSecIn).Temp = TempSecInlet;
     967       261156 :     state.dataLoopNodes->Node(NodeNumPriIn).Temp = TempPriInlet;
     968              : 
     969       261156 :     if (LoopSide == DataPlant::LoopSideLocation::Supply) {
     970       130578 :         MixedOutletTemp = TempPriInlet;
     971              :     } else {
     972       130578 :         MixedOutletTemp = TempSecInlet;
     973              :     }
     974       261156 : }
     975              : 
     976       426132 : void ManageTwoWayCommonPipe(EnergyPlusData &state, PlantLocation const &plantLoc, Real64 const TankOutletTemp)
     977              : {
     978              : 
     979              :     // SUBROUTINE INFORMATION:
     980              :     //       AUTHOR         B. Griffith
     981              :     //       DATE WRITTEN   June 2011
     982              :     //       MODIFIED       na
     983              :     //       RE-ENGINEERED  B. Griffith, Oct 2011.  rewrite
     984              : 
     985              :     // PURPOSE OF THIS SUBROUTINE:
     986              :     // manage two-way common pipe modeling at half-loop interface
     987              : 
     988              :     // METHODOLOGY EMPLOYED:
     989              :     // calculate mixed temperatures and various flow rates
     990              :     // sequential substitution of system of equations
     991              : 
     992              :     // REFERENCES:
     993              :     // reimplementation of CheckTwoWayCommonPipeConditions by Sankaranarayanan K P Jan 2007
     994              : 
     995              :     // SUBROUTINE PARAMETER DEFINITIONS:
     996              :     enum class UpdateType
     997              :     {
     998              :         DemandLedPrimaryInlet,
     999              :         DemandLedSecondaryInlet,
    1000              :         SupplyLedPrimaryInlet,
    1001              :         SupplyLedSecondaryInlet
    1002       426132 :     } curCallingCase = UpdateType::SupplyLedPrimaryInlet;
    1003       426132 :     constexpr int MaxIterLimitCaseA(8);
    1004       426132 :     constexpr int MaxIterLimitCaseB(4);
    1005              : 
    1006              :     // one time setups
    1007       426132 :     if (!state.dataHVACInterfaceMgr->CommonPipeSetupFinished) {
    1008           10 :         SetupCommonPipes(state);
    1009              :     }
    1010              : 
    1011       426132 :     auto &plantCommonPipe(state.dataHVACInterfaceMgr->PlantCommonPipe(plantLoc.loopNum));
    1012       426132 :     auto &thisPlantLoop = state.dataPlnt->PlantLoop(plantLoc.loopNum);
    1013              : 
    1014              :     // fill local node indexes
    1015       426132 :     int const NodeNumPriIn = thisPlantLoop.LoopSide(DataPlant::LoopSideLocation::Supply).NodeNumIn;
    1016       426132 :     int const NodeNumPriOut = thisPlantLoop.LoopSide(DataPlant::LoopSideLocation::Supply).NodeNumOut;
    1017       426132 :     int const NodeNumSecIn = thisPlantLoop.LoopSide(DataPlant::LoopSideLocation::Demand).NodeNumIn;
    1018       426132 :     int const NodeNumSecOut = thisPlantLoop.LoopSide(DataPlant::LoopSideLocation::Demand).NodeNumOut;
    1019              : 
    1020              :     // begin environment inits
    1021       426132 :     if (plantCommonPipe.MyEnvrnFlag && state.dataGlobal->BeginEnvrnFlag) {
    1022           60 :         plantCommonPipe.PriToSecFlow = 0.0;
    1023           60 :         plantCommonPipe.SecToPriFlow = 0.0;
    1024           60 :         plantCommonPipe.PriCPLegFlow = 0.0;
    1025           60 :         plantCommonPipe.SecCPLegFlow = 0.0;
    1026           60 :         plantCommonPipe.MyEnvrnFlag = false;
    1027              :     }
    1028              : 
    1029       426132 :     if (!state.dataGlobal->BeginEnvrnFlag) {
    1030       422492 :         plantCommonPipe.MyEnvrnFlag = true;
    1031              :     }
    1032              : 
    1033              :     // every time inits
    1034       426132 :     Real64 MdotSec = state.dataLoopNodes->Node(NodeNumSecOut).MassFlowRate; // assume known and fixed by demand side operation
    1035       426132 :     Real64 TempCPPrimaryCntrlSetPoint = state.dataLoopNodes->Node(NodeNumPriIn).TempSetPoint;
    1036       426132 :     Real64 TempCPSecondaryCntrlSetPoint = state.dataLoopNodes->Node(NodeNumSecIn).TempSetPoint;
    1037              : 
    1038              :     // 6 unknowns follow, fill with current values
    1039       426132 :     Real64 MdotPriToSec = plantCommonPipe.PriToSecFlow;
    1040       426132 :     Real64 MdotPriRCLeg = plantCommonPipe.PriCPLegFlow;
    1041       426132 :     Real64 MdotSecRCLeg = plantCommonPipe.SecCPLegFlow;
    1042       426132 :     Real64 TempSecInlet = state.dataLoopNodes->Node(NodeNumSecIn).Temp;
    1043       426132 :     Real64 TempPriInlet = state.dataLoopNodes->Node(NodeNumPriIn).Temp;
    1044              :     Real64 MdotPri =
    1045       426132 :         state.dataLoopNodes->Node(NodeNumPriOut).MassFlowRate; // may or may not be an unknown, If variable speed primary side, then unknown
    1046              : 
    1047              :     Real64 TempPriOutTankOut;
    1048              :     Real64 TempSecOutTankOut;
    1049       426132 :     if (plantLoc.loopSideNum == DataPlant::LoopSideLocation::Supply) {
    1050       213066 :         TempSecOutTankOut = TankOutletTemp;
    1051       213066 :         TempPriOutTankOut = thisPlantLoop.LoopSide(DataPlant::LoopSideLocation::Demand).LoopSideInlet_TankTemp;
    1052              :     } else {
    1053       213066 :         TempPriOutTankOut = TankOutletTemp;
    1054       213066 :         TempSecOutTankOut = thisPlantLoop.LoopSide(DataPlant::LoopSideLocation::Supply).LoopSideInlet_TankTemp;
    1055              :     }
    1056              : 
    1057              :     // determine current case
    1058              :     // which side is being updated
    1059              :     // commonpipe control point is the inlet of one of the half loops
    1060       426132 :     if (plantLoc.loopSideNum == DataPlant::LoopSideLocation::Supply) { // update primary inlet
    1061       244972 :         if (thisPlantLoop.LoopSide(DataPlant::LoopSideLocation::Supply).InletNodeSetPt &&
    1062        31906 :             !thisPlantLoop.LoopSide(DataPlant::LoopSideLocation::Demand).InletNodeSetPt) {
    1063        31906 :             curCallingCase = UpdateType::SupplyLedPrimaryInlet;
    1064              : 
    1065       362320 :         } else if (!thisPlantLoop.LoopSide(DataPlant::LoopSideLocation::Supply).InletNodeSetPt &&
    1066       181160 :                    thisPlantLoop.LoopSide(DataPlant::LoopSideLocation::Demand).InletNodeSetPt) {
    1067       181160 :             curCallingCase = UpdateType::DemandLedPrimaryInlet;
    1068              :         }
    1069              :     } else { // update secondary inlet
    1070       244972 :         if (thisPlantLoop.LoopSide(DataPlant::LoopSideLocation::Supply).InletNodeSetPt &&
    1071        31906 :             !thisPlantLoop.LoopSide(DataPlant::LoopSideLocation::Demand).InletNodeSetPt) {
    1072        31906 :             curCallingCase = UpdateType::SupplyLedSecondaryInlet;
    1073              : 
    1074       362320 :         } else if (!thisPlantLoop.LoopSide(DataPlant::LoopSideLocation::Supply).InletNodeSetPt &&
    1075       181160 :                    thisPlantLoop.LoopSide(DataPlant::LoopSideLocation::Demand).InletNodeSetPt) {
    1076       181160 :             curCallingCase = UpdateType::DemandLedSecondaryInlet;
    1077              :         }
    1078              :     }
    1079              : 
    1080       426132 :     switch (curCallingCase) {
    1081        63812 :     case UpdateType::SupplyLedPrimaryInlet:
    1082              :     case UpdateType::SupplyLedSecondaryInlet:
    1083              :         // CASE A, Primary/Supply Led
    1084              :         // six equations and six unknowns (although one has a setpoint)
    1085       574308 :         for (int loop = 1; loop <= MaxIterLimitCaseA; ++loop) {
    1086              : 
    1087              :             // eq 1
    1088       510496 :             if (std::abs(TempSecOutTankOut - TempCPPrimaryCntrlSetPoint) > DataPlant::DeltaTempTol) {
    1089       510496 :                 MdotPriToSec = MdotPriRCLeg * (TempCPPrimaryCntrlSetPoint - TempPriOutTankOut) / (TempSecOutTankOut - TempCPPrimaryCntrlSetPoint);
    1090       510496 :                 if (MdotPriToSec < DataBranchAirLoopPlant::MassFlowTolerance) {
    1091       225596 :                     MdotPriToSec = 0.0;
    1092              :                 }
    1093       510496 :                 if (MdotPriToSec > MdotSec) {
    1094        74679 :                     MdotPriToSec = MdotSec;
    1095              :                 }
    1096              :             } else {
    1097            0 :                 MdotPriToSec = MdotSec; //  what to do (?)
    1098              :             }
    1099              :             // eq. 5
    1100       510496 :             MdotPriRCLeg = MdotPri - MdotPriToSec;
    1101       510496 :             if (MdotPriRCLeg < DataBranchAirLoopPlant::MassFlowTolerance) {
    1102       224584 :                 MdotPriRCLeg = 0.0;
    1103              :             }
    1104              : 
    1105              :             // eq. 4
    1106       510496 :             MdotSecRCLeg = MdotSec - MdotPriToSec;
    1107       510496 :             if (MdotSecRCLeg < DataBranchAirLoopPlant::MassFlowTolerance) {
    1108       236391 :                 MdotSecRCLeg = 0.0;
    1109              :             }
    1110              : 
    1111              :             // eq  6
    1112       510496 :             if ((MdotPriToSec + MdotSecRCLeg) > DataBranchAirLoopPlant::MassFlowTolerance) {
    1113       344176 :                 TempSecInlet = (MdotPriToSec * TempPriOutTankOut + MdotSecRCLeg * TempSecOutTankOut) / (MdotPriToSec + MdotSecRCLeg);
    1114              :             } else {
    1115       166320 :                 TempSecInlet = TempPriOutTankOut;
    1116              :             }
    1117              : 
    1118              :             // eq. 3
    1119       510496 :             if ((plantCommonPipe.SupplySideInletPumpType == FlowType::Variable) && (curCallingCase == UpdateType::SupplyLedPrimaryInlet)) {
    1120              :                 // MdotPri is a variable to be calculated and flow request needs to be made
    1121            0 :                 if (std::abs(TempCPPrimaryCntrlSetPoint) > DataPlant::DeltaTempTol) {
    1122              : 
    1123            0 :                     MdotPri = (MdotPriRCLeg * TempPriOutTankOut + MdotPriToSec * TempSecOutTankOut) / (TempCPPrimaryCntrlSetPoint);
    1124              : 
    1125            0 :                     if (MdotPri < DataBranchAirLoopPlant::MassFlowTolerance) {
    1126            0 :                         MdotPri = 0.0;
    1127              :                     }
    1128              :                 } else {
    1129            0 :                     MdotPri = MdotSec;
    1130              :                 }
    1131            0 :                 PlantLocation thisPlantLoc = {plantLoc.loopNum, DataPlant::LoopSideLocation::Supply, 1, 0};
    1132            0 :                 PlantUtilities::SetActuatedBranchFlowRate(state, MdotPri, NodeNumPriIn, thisPlantLoc, false);
    1133              :             }
    1134              : 
    1135              :             // eq. 2
    1136       510496 :             if ((MdotPriToSec + MdotPriRCLeg) > DataBranchAirLoopPlant::MassFlowTolerance) {
    1137       344112 :                 TempPriInlet = (MdotPriToSec * TempSecOutTankOut + MdotPriRCLeg * TempPriOutTankOut) / (MdotPriToSec + MdotPriRCLeg);
    1138              :             } else {
    1139       166384 :                 TempPriInlet = TempSecOutTankOut;
    1140              :             }
    1141              :         }
    1142        63812 :         break;
    1143       362320 :     case UpdateType::DemandLedPrimaryInlet:
    1144              :     case UpdateType::DemandLedSecondaryInlet:
    1145              :         // case B. Secondary/demand led
    1146              : 
    1147              :         // six equations and six unknowns (although one has a setpoint)
    1148      1811600 :         for (int loop = 1; loop <= MaxIterLimitCaseB; ++loop) {
    1149              :             // eq 1,
    1150      1449280 :             if (std::abs(TempPriOutTankOut - TempSecOutTankOut) > DataPlant::DeltaTempTol) {
    1151      1063088 :                 MdotPriToSec = MdotSec * (TempCPSecondaryCntrlSetPoint - TempSecOutTankOut) / (TempPriOutTankOut - TempSecOutTankOut);
    1152      1063088 :                 if (MdotPriToSec < DataBranchAirLoopPlant::MassFlowTolerance) {
    1153       337536 :                     MdotPriToSec = 0.0;
    1154              :                 }
    1155      1063088 :                 if (MdotPriToSec > MdotSec) {
    1156       390460 :                     MdotPriToSec = MdotSec;
    1157              :                 }
    1158              :             } else {
    1159       386192 :                 MdotPriToSec = MdotSec;
    1160              :             }
    1161              : 
    1162              :             // eq. 2,
    1163      1449280 :             if ((MdotPriToSec + MdotPriRCLeg) > DataBranchAirLoopPlant::MassFlowTolerance) {
    1164       726840 :                 TempPriInlet = (MdotPriToSec * TempSecOutTankOut + MdotPriRCLeg * TempPriOutTankOut) / (MdotPriToSec + MdotPriRCLeg);
    1165              :             } else {
    1166       722440 :                 TempPriInlet = TempSecOutTankOut;
    1167              :             }
    1168              : 
    1169              :             // eq. 3
    1170      1449280 :             if ((plantCommonPipe.SupplySideInletPumpType == FlowType::Variable) && (curCallingCase == UpdateType::DemandLedPrimaryInlet)) {
    1171              :                 // MdotPri is a variable to be calculated and flow request made
    1172            0 :                 if (std::abs(TempPriOutTankOut - TempPriInlet) > DataPlant::DeltaTempTol) {
    1173            0 :                     MdotPri = MdotSec * (TempCPSecondaryCntrlSetPoint - TempSecOutTankOut) / (TempPriOutTankOut - TempPriInlet);
    1174            0 :                     if (MdotPri < DataBranchAirLoopPlant::MassFlowTolerance) {
    1175            0 :                         MdotPri = 0.0;
    1176              :                     }
    1177              :                 } else {
    1178            0 :                     MdotPri = MdotSec;
    1179              :                 }
    1180            0 :                 PlantLocation thisPlantLoc = {plantLoc.loopNum, DataPlant::LoopSideLocation::Supply, 1, 0};
    1181            0 :                 PlantUtilities::SetActuatedBranchFlowRate(state, MdotPri, NodeNumPriIn, thisPlantLoc, false);
    1182              :             }
    1183              : 
    1184              :             // eq. 4
    1185      1449280 :             MdotSecRCLeg = MdotSec - MdotPriToSec;
    1186      1449280 :             if (MdotSecRCLeg < DataBranchAirLoopPlant::MassFlowTolerance) {
    1187      1114012 :                 MdotSecRCLeg = 0.0;
    1188              :             }
    1189              : 
    1190              :             // eq. 5
    1191      1449280 :             MdotPriRCLeg = MdotPri - MdotPriToSec;
    1192      1449280 :             if (MdotPriRCLeg < DataBranchAirLoopPlant::MassFlowTolerance) {
    1193       761428 :                 MdotPriRCLeg = 0.0;
    1194              :             }
    1195              : 
    1196              :             // eq  6
    1197      1449280 :             if ((MdotPriToSec + MdotSecRCLeg) > DataBranchAirLoopPlant::MassFlowTolerance) {
    1198       725824 :                 TempSecInlet = (MdotPriToSec * TempPriOutTankOut + MdotSecRCLeg * TempSecOutTankOut) / (MdotPriToSec + MdotSecRCLeg);
    1199              :             } else {
    1200       723456 :                 TempSecInlet = TempPriOutTankOut;
    1201              :             }
    1202              :         }
    1203              :     }
    1204              : 
    1205              :     // update
    1206       426132 :     plantCommonPipe.PriToSecFlow = MdotPriToSec;
    1207       426132 :     plantCommonPipe.SecToPriFlow = MdotPriToSec;
    1208       426132 :     plantCommonPipe.PriCPLegFlow = MdotPriRCLeg;
    1209       426132 :     plantCommonPipe.SecCPLegFlow = MdotSecRCLeg;
    1210       426132 :     state.dataLoopNodes->Node(NodeNumSecIn).Temp = TempSecInlet;
    1211       426132 :     state.dataLoopNodes->Node(NodeNumPriIn).Temp = TempPriInlet;
    1212       426132 : }
    1213              : 
    1214           14 : void SetupCommonPipes(EnergyPlusData &state)
    1215              : {
    1216              : 
    1217              :     // SUBROUTINE INFORMATION:
    1218              :     //       AUTHOR         B. Griffith
    1219              :     //       DATE WRITTEN   Jan. 2010
    1220              :     //       MODIFIED       B. Griffith Oct. 2011
    1221              :     //       RE-ENGINEERED  na
    1222              : 
    1223              :     // PURPOSE OF THIS SUBROUTINE:
    1224              :     // collect allocation, outputs, and other set up for common pipes
    1225              : 
    1226           14 :     state.dataHVACInterfaceMgr->PlantCommonPipe.allocate(state.dataPlnt->TotNumLoops);
    1227              : 
    1228           52 :     for (int CurLoopNum = 1; CurLoopNum <= state.dataPlnt->TotNumLoops; ++CurLoopNum) {
    1229              : 
    1230              :         // reference to easily lookup the first item once
    1231           38 :         auto &thisPlantLoop = state.dataPlnt->PlantLoop(CurLoopNum);
    1232           38 :         auto &thisCommonPipe = state.dataHVACInterfaceMgr->PlantCommonPipe(CurLoopNum);
    1233           38 :         auto const &first_demand_component_type = thisPlantLoop.LoopSide(DataPlant::LoopSideLocation::Demand).Branch(1).Comp(1).Type;
    1234           38 :         auto const &first_supply_component_type = thisPlantLoop.LoopSide(DataPlant::LoopSideLocation::Supply).Branch(1).Comp(1).Type;
    1235              : 
    1236           38 :         switch (thisPlantLoop.CommonPipeType) {
    1237           24 :         case DataPlant::CommonPipeType::No:
    1238           24 :             thisCommonPipe.CommonPipeType = DataPlant::CommonPipeType::No;
    1239           24 :             break;
    1240            4 :         case DataPlant::CommonPipeType::Single: // Uncontrolled ('single') common pipe
    1241            4 :             thisCommonPipe.CommonPipeType = DataPlant::CommonPipeType::Single;
    1242            8 :             SetupOutputVariable(state,
    1243              :                                 "Plant Common Pipe Mass Flow Rate",
    1244              :                                 Constant::Units::kg_s,
    1245            4 :                                 thisCommonPipe.Flow,
    1246              :                                 OutputProcessor::TimeStepType::System,
    1247              :                                 OutputProcessor::StoreType::Average,
    1248            4 :                                 thisPlantLoop.Name);
    1249            8 :             SetupOutputVariable(state,
    1250              :                                 "Plant Common Pipe Temperature",
    1251              :                                 Constant::Units::C,
    1252            4 :                                 thisCommonPipe.Temp,
    1253              :                                 OutputProcessor::TimeStepType::System,
    1254              :                                 OutputProcessor::StoreType::Average,
    1255            4 :                                 thisPlantLoop.Name);
    1256            4 :             SetupOutputVariable(state,
    1257              :                                 "Plant Common Pipe Flow Direction Status",
    1258              :                                 Constant::Units::None,
    1259            4 :                                 thisCommonPipe.FlowDir,
    1260              :                                 OutputProcessor::TimeStepType::System,
    1261              :                                 OutputProcessor::StoreType::Average,
    1262            4 :                                 thisPlantLoop.Name);
    1263              : 
    1264            4 :             if (first_supply_component_type == DataPlant::PlantEquipmentType::PumpVariableSpeed) {
    1265              :                 // If/when the model supports variable-pumping primary, this can be removed.
    1266            2 :                 ShowWarningError(state, "SetupCommonPipes: detected variable speed pump on supply inlet of CommonPipe plant loop");
    1267            1 :                 ShowContinueError(state, format("Occurs on plant loop name = {}", thisPlantLoop.Name));
    1268            2 :                 ShowContinueError(state, "The common pipe model does not support varying the flow rate on the primary/supply side");
    1269            3 :                 ShowContinueError(state, "The primary/supply side will operate as if constant speed, and the simulation continues");
    1270              :             }
    1271            4 :             break;
    1272           10 :         case DataPlant::CommonPipeType::TwoWay: // Controlled ('two-way') common pipe
    1273           10 :             thisCommonPipe.CommonPipeType = DataPlant::CommonPipeType::TwoWay;
    1274           20 :             SetupOutputVariable(state,
    1275              :                                 "Plant Common Pipe Primary Mass Flow Rate",
    1276              :                                 Constant::Units::kg_s,
    1277           10 :                                 thisCommonPipe.PriCPLegFlow,
    1278              :                                 OutputProcessor::TimeStepType::System,
    1279              :                                 OutputProcessor::StoreType::Average,
    1280           10 :                                 thisPlantLoop.Name);
    1281           20 :             SetupOutputVariable(state,
    1282              :                                 "Plant Common Pipe Secondary Mass Flow Rate",
    1283              :                                 Constant::Units::kg_s,
    1284           10 :                                 thisCommonPipe.SecCPLegFlow,
    1285              :                                 OutputProcessor::TimeStepType::System,
    1286              :                                 OutputProcessor::StoreType::Average,
    1287           10 :                                 thisPlantLoop.Name);
    1288           20 :             SetupOutputVariable(state,
    1289              :                                 "Plant Common Pipe Primary to Secondary Mass Flow Rate",
    1290              :                                 Constant::Units::kg_s,
    1291           10 :                                 thisCommonPipe.PriToSecFlow,
    1292              :                                 OutputProcessor::TimeStepType::System,
    1293              :                                 OutputProcessor::StoreType::Average,
    1294           10 :                                 thisPlantLoop.Name);
    1295           20 :             SetupOutputVariable(state,
    1296              :                                 "Plant Common Pipe Secondary to Primary Mass Flow Rate",
    1297              :                                 Constant::Units::kg_s,
    1298           10 :                                 thisCommonPipe.SecToPriFlow,
    1299              :                                 OutputProcessor::TimeStepType::System,
    1300              :                                 OutputProcessor::StoreType::Average,
    1301           10 :                                 thisPlantLoop.Name);
    1302              : 
    1303              :             // check type of pump on supply side inlet
    1304           10 :             if (first_supply_component_type == DataPlant::PlantEquipmentType::PumpConstantSpeed) {
    1305            9 :                 thisCommonPipe.SupplySideInletPumpType = FlowType::Constant;
    1306            1 :             } else if (first_supply_component_type == DataPlant::PlantEquipmentType::PumpVariableSpeed) {
    1307            0 :                 thisCommonPipe.SupplySideInletPumpType = FlowType::Variable;
    1308              :                 // If/when the model supports variable-pumping primary, this can be removed.
    1309            0 :                 ShowWarningError(state, "SetupCommonPipes: detected variable speed pump on supply inlet of TwoWayCommonPipe plant loop");
    1310            0 :                 ShowContinueError(state, format("Occurs on plant loop name = {}", thisPlantLoop.Name));
    1311            0 :                 ShowContinueError(state, "The common pipe model does not support varying the flow rate on the primary/supply side");
    1312            0 :                 ShowContinueError(state, "The primary/supply side will operate as if constant speed, and the simulation continues");
    1313              :             }
    1314              :             // check type of pump on demand side inlet
    1315           10 :             if (first_demand_component_type == DataPlant::PlantEquipmentType::PumpConstantSpeed) {
    1316            0 :                 thisCommonPipe.DemandSideInletPumpType = FlowType::Constant;
    1317           10 :             } else if (first_demand_component_type == DataPlant::PlantEquipmentType::PumpVariableSpeed) {
    1318           10 :                 thisCommonPipe.DemandSideInletPumpType = FlowType::Variable;
    1319              :             }
    1320           10 :             break;
    1321            0 :         default:
    1322            0 :             assert(false);
    1323              :         }
    1324              :     }
    1325              : 
    1326           14 :     state.dataHVACInterfaceMgr->CommonPipeSetupFinished = true;
    1327           14 : }
    1328              : 
    1329              : } // namespace EnergyPlus::HVACInterfaceManager
        

Generated by: LCOV version 2.0-1