LCOV - code coverage report
Current view: top level - EnergyPlus - PondGroundHeatExchanger.cc (source / functions) Coverage Total Hit
Test: lcov.output.filtered Lines: 77.0 % 304 234
Test Date: 2025-06-02 07:23:51 Functions: 100.0 % 13 13

            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/Array.functions.hh>
      53              : #include <ObjexxFCL/Fmath.hh>
      54              : 
      55              : // EnergyPlus Headers
      56              : #include <EnergyPlus/BranchNodeConnections.hh>
      57              : #include <EnergyPlus/ConvectionCoefficients.hh>
      58              : #include <EnergyPlus/Data/EnergyPlusData.hh>
      59              : #include <EnergyPlus/DataEnvironment.hh>
      60              : #include <EnergyPlus/DataHVACGlobals.hh>
      61              : #include <EnergyPlus/DataHeatBalance.hh>
      62              : #include <EnergyPlus/DataIPShortCuts.hh>
      63              : #include <EnergyPlus/DataLoopNode.hh>
      64              : #include <EnergyPlus/DataPrecisionGlobals.hh>
      65              : #include <EnergyPlus/FluidProperties.hh>
      66              : #include <EnergyPlus/General.hh>
      67              : #include <EnergyPlus/InputProcessing/InputProcessor.hh>
      68              : #include <EnergyPlus/NodeInputManager.hh>
      69              : #include <EnergyPlus/OutputProcessor.hh>
      70              : #include <EnergyPlus/Plant/DataPlant.hh>
      71              : #include <EnergyPlus/PlantUtilities.hh>
      72              : #include <EnergyPlus/PondGroundHeatExchanger.hh>
      73              : #include <EnergyPlus/Psychrometrics.hh>
      74              : #include <EnergyPlus/UtilityRoutines.hh>
      75              : 
      76              : namespace EnergyPlus::PondGroundHeatExchanger {
      77              : 
      78              : // Module containing the routines dealing with pond ground heat exchangers
      79              : 
      80              : // MODULE INFORMATION:
      81              : //       AUTHOR         Simon Rees
      82              : //       DATE WRITTEN   September 2002
      83              : //       MODIFIED       Brent Griffith Sept 2010, plant upgrades
      84              : //       RE-ENGINEERED  na
      85              : 
      86              : // PURPOSE OF THIS MODULE:
      87              : // This model represents a shallow pond with submerged hydronic tubes through
      88              : // which the heat transfer fluid is circulated. The model represents a 'shallow'
      89              : // pond in that no attempt is made to model any stratification effects that may
      90              : // be present in deeper ponds. This type of heat rejector is intended to be
      91              : // connected in a condenser loop, with or without other forms of heat rejector.
      92              : // The pond model is a 'lumped parameter' model where the pond is represented
      93              : // by a single node with thermal mass. The pond surface temperature is the same
      94              : // as the temperature at this node, i.e. the surface temperature is the same as
      95              : // the bulk temperature. A first order differential equation is solved in the
      96              : // model to calculated the pond temperature at each time step. This type of heat
      97              : // rejector is modelled as several circuits connected in parallel.
      98              : 
      99              : // METHODOLOGY EMPLOYED:
     100              : // A heat balance is calculated at a single node that represents the pond.
     101              : // heat transfer takes palce by surface convection, long-wave radiation to the
     102              : // sky, absoption of solar energy, ground heat transfer and heat exchange with
     103              : // the fluid. A heat exchanger analogy is used to calculate the heat transfer
     104              : // between the heat transfer fluid and the pond. The differential equation
     105              : // defined by the heat balance is solved using a fourth order Runge-Kutta
     106              : // numerical integration method.
     107              : 
     108              : // REFERENCES:
     109              : // Chiasson, A. Advances in Modeling of Ground-Source Heat Pump Systems.
     110              : //   M.S. Thesis, Oklahoma State University, December 1999.
     111              : // Chiasson, A.D., J.D. Spitler, S.J. Rees, M.D. Smith.  2000.  A Model For
     112              : //   Simulating The Performance Of A Shallow Pond As A Supplemental Heat Rejecter
     113              : //   With Closed-Loop Ground-Source Heat Pump Systems.
     114              : //   ASHRAE Transactions.  106(2):107-121.
     115              : 
     116              : Real64 constexpr StefBoltzmann(5.6697e-08); // Stefan-Boltzmann constant
     117              : 
     118       123951 : void PondGroundHeatExchangerData::simulate(EnergyPlusData &state,
     119              :                                            [[maybe_unused]] const PlantLocation &calledFromLocation,
     120              :                                            bool const FirstHVACIteration,
     121              :                                            [[maybe_unused]] Real64 &CurLoad,
     122              :                                            [[maybe_unused]] bool const RunFlag)
     123              : {
     124       123951 :     this->InitPondGroundHeatExchanger(state, FirstHVACIteration);
     125       123951 :     this->CalcPondGroundHeatExchanger(state);
     126       123951 :     this->UpdatePondGroundHeatExchanger(state);
     127       123951 : }
     128              : 
     129            3 : PlantComponent *PondGroundHeatExchangerData::factory(EnergyPlusData &state, std::string const &objectName)
     130              : {
     131            3 :     if (state.dataPondGHE->GetInputFlag) {
     132            3 :         GetPondGroundHeatExchanger(state);
     133            3 :         state.dataPondGHE->GetInputFlag = false;
     134              :     }
     135            3 :     for (auto &ghx : state.dataPondGHE->PondGHE) {
     136            3 :         if (ghx.Name == objectName) {
     137            3 :             return &ghx;
     138              :         }
     139              :     }
     140              :     // If we didn't find it, fatal
     141            0 :     ShowFatalError(state, format("Pond Heat Exchanger Factory: Error getting inputs for GHX named: {}", objectName));
     142              :     // Shut up the compiler
     143            0 :     return nullptr;
     144              : }
     145              : 
     146           15 : void PondGroundHeatExchangerData::onInitLoopEquip(EnergyPlusData &state, [[maybe_unused]] const PlantLocation &calledFromLocation)
     147              : {
     148           15 :     this->InitPondGroundHeatExchanger(state, true);
     149           15 : }
     150              : 
     151           15 : void PondGroundHeatExchangerData::getDesignCapacities([[maybe_unused]] EnergyPlusData &state,
     152              :                                                       [[maybe_unused]] const PlantLocation &calledFromLocation,
     153              :                                                       Real64 &MaxLoad,
     154              :                                                       Real64 &MinLoad,
     155              :                                                       Real64 &OptLoad)
     156              : {
     157           15 :     MaxLoad = this->DesignCapacity;
     158           15 :     MinLoad = 0.0;
     159           15 :     OptLoad = this->DesignCapacity;
     160           15 : }
     161              : 
     162            3 : void GetPondGroundHeatExchanger(EnergyPlusData &state)
     163              : {
     164              : 
     165              :     // SUBROUTINE INFORMATION:
     166              :     //       AUTHOR         Simon Rees
     167              :     //       DATE WRITTEN   August 2002
     168              :     //       MODIFIED       na
     169              :     //       RE-ENGINEERED  na
     170              : 
     171              :     // PURPOSE OF THIS SUBROUTINE:
     172              :     // This subroutine reads the input for hydronic Pond Ground Heat Exchangers
     173              :     // from the user input file.  This will contain all of the information
     174              :     // needed to define and simulate the pond.
     175              : 
     176            3 :     bool ErrorsFound(false); // Set to true if errors in input,
     177              : 
     178              :     int IOStatus;   // Used in GetObjectItem
     179              :     int Item;       // Item to be "gotten"
     180              :     int NumAlphas;  // Number of Alphas for each GetObjectItem call
     181              :     int NumNumbers; // Number of Numbers for each GetObjectItem call
     182              : 
     183              :     // Initializations and allocations
     184            3 :     state.dataIPShortCut->cCurrentModuleObject = "GroundHeatExchanger:Pond";
     185            6 :     state.dataPondGHE->NumOfPondGHEs =
     186            3 :         state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, state.dataIPShortCut->cCurrentModuleObject);
     187              :     // allocate data structures
     188            3 :     if (allocated(state.dataPondGHE->PondGHE)) {
     189            0 :         state.dataPondGHE->PondGHE.deallocate();
     190              :     }
     191              : 
     192            3 :     state.dataPondGHE->PondGHE.allocate(state.dataPondGHE->NumOfPondGHEs);
     193              : 
     194              :     // Obtain all of the user data related to the ponds...
     195            6 :     for (Item = 1; Item <= state.dataPondGHE->NumOfPondGHEs; ++Item) {
     196              : 
     197              :         // get the input data
     198            9 :         state.dataInputProcessing->inputProcessor->getObjectItem(state,
     199            3 :                                                                  state.dataIPShortCut->cCurrentModuleObject,
     200              :                                                                  Item,
     201            3 :                                                                  state.dataIPShortCut->cAlphaArgs,
     202              :                                                                  NumAlphas,
     203            3 :                                                                  state.dataIPShortCut->rNumericArgs,
     204              :                                                                  NumNumbers,
     205              :                                                                  IOStatus,
     206              :                                                                  _,
     207              :                                                                  _,
     208            3 :                                                                  state.dataIPShortCut->cAlphaFieldNames,
     209            3 :                                                                  state.dataIPShortCut->cNumericFieldNames);
     210              : 
     211            3 :         if ((state.dataPondGHE->PondGHE(Item).water = Fluid::GetWater(state)) == nullptr) {
     212            0 :             ShowSevereError(state, "Fluid Properties for WATER not found");
     213            0 :             ErrorsFound = true;
     214              :         }
     215              : 
     216              :         // General user input data
     217            3 :         state.dataPondGHE->PondGHE(Item).Name = state.dataIPShortCut->cAlphaArgs(1);
     218              : 
     219              :         // get inlet node data
     220            3 :         state.dataPondGHE->PondGHE(Item).InletNode = state.dataIPShortCut->cAlphaArgs(2);
     221            3 :         state.dataPondGHE->PondGHE(Item).InletNodeNum =
     222            6 :             NodeInputManager::GetOnlySingleNode(state,
     223            3 :                                                 state.dataIPShortCut->cAlphaArgs(2),
     224              :                                                 ErrorsFound,
     225              :                                                 DataLoopNode::ConnectionObjectType::GroundHeatExchangerPond,
     226            3 :                                                 state.dataIPShortCut->cAlphaArgs(1),
     227              :                                                 DataLoopNode::NodeFluidType::Water,
     228              :                                                 DataLoopNode::ConnectionType::Inlet,
     229              :                                                 NodeInputManager::CompFluidStream::Primary,
     230              :                                                 DataLoopNode::ObjectIsNotParent);
     231            3 :         if (state.dataPondGHE->PondGHE(Item).InletNodeNum == 0) {
     232            0 :             ShowSevereError(state, format("Invalid {}={}", state.dataIPShortCut->cAlphaFieldNames(2), state.dataIPShortCut->cAlphaArgs(2)));
     233            0 :             ShowContinueError(state, format("Entered in {}={}", state.dataIPShortCut->cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)));
     234            0 :             ErrorsFound = true;
     235              :         }
     236              : 
     237              :         // get outlet node data
     238            3 :         state.dataPondGHE->PondGHE(Item).OutletNode = state.dataIPShortCut->cAlphaArgs(3);
     239            3 :         state.dataPondGHE->PondGHE(Item).OutletNodeNum =
     240            6 :             NodeInputManager::GetOnlySingleNode(state,
     241            3 :                                                 state.dataIPShortCut->cAlphaArgs(3),
     242              :                                                 ErrorsFound,
     243              :                                                 DataLoopNode::ConnectionObjectType::GroundHeatExchangerPond,
     244            3 :                                                 state.dataIPShortCut->cAlphaArgs(1),
     245              :                                                 DataLoopNode::NodeFluidType::Water,
     246              :                                                 DataLoopNode::ConnectionType::Outlet,
     247              :                                                 NodeInputManager::CompFluidStream::Primary,
     248              :                                                 DataLoopNode::ObjectIsNotParent);
     249            3 :         if (state.dataPondGHE->PondGHE(Item).OutletNodeNum == 0) {
     250            0 :             ShowSevereError(state, format("Invalid {}={}", state.dataIPShortCut->cAlphaFieldNames(3), state.dataIPShortCut->cAlphaArgs(3)));
     251            0 :             ShowContinueError(state, format("Entered in {}={}", state.dataIPShortCut->cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)));
     252            0 :             ErrorsFound = true;
     253              :         }
     254              : 
     255            6 :         BranchNodeConnections::TestCompSet(state,
     256            3 :                                            state.dataIPShortCut->cCurrentModuleObject,
     257            3 :                                            state.dataIPShortCut->cAlphaArgs(1),
     258            3 :                                            state.dataIPShortCut->cAlphaArgs(2),
     259            3 :                                            state.dataIPShortCut->cAlphaArgs(3),
     260              :                                            "Condenser Water Nodes");
     261              : 
     262              :         // pond geometry data
     263            3 :         state.dataPondGHE->PondGHE(Item).Depth = state.dataIPShortCut->rNumericArgs(1);
     264            3 :         state.dataPondGHE->PondGHE(Item).Area = state.dataIPShortCut->rNumericArgs(2);
     265            3 :         if (state.dataIPShortCut->rNumericArgs(1) <= 0.0) {
     266            0 :             ShowSevereError(state, format("Invalid {}={:.2R}", state.dataIPShortCut->cNumericFieldNames(1), state.dataIPShortCut->rNumericArgs(1)));
     267            0 :             ShowContinueError(state, format("Entered in {}={}", state.dataIPShortCut->cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)));
     268            0 :             ShowContinueError(state, "Value must be greater than 0.0");
     269            0 :             ErrorsFound = true;
     270              :         }
     271            3 :         if (state.dataIPShortCut->rNumericArgs(2) <= 0.0) {
     272            0 :             ShowSevereError(state, format("Invalid {}={:.2R}", state.dataIPShortCut->cNumericFieldNames(2), state.dataIPShortCut->rNumericArgs(2)));
     273            0 :             ShowContinueError(state, format("Entered in {}={}", state.dataIPShortCut->cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)));
     274            0 :             ShowContinueError(state, "Value must be greater than 0.0");
     275            0 :             ErrorsFound = true;
     276              :         }
     277              : 
     278              :         // tube data
     279            3 :         state.dataPondGHE->PondGHE(Item).TubeInDiameter = state.dataIPShortCut->rNumericArgs(3);
     280            3 :         state.dataPondGHE->PondGHE(Item).TubeOutDiameter = state.dataIPShortCut->rNumericArgs(4);
     281              : 
     282            3 :         if (state.dataIPShortCut->rNumericArgs(3) <= 0.0) {
     283            0 :             ShowSevereError(state, format("Invalid {}={:.2R}", state.dataIPShortCut->cNumericFieldNames(3), state.dataIPShortCut->rNumericArgs(3)));
     284            0 :             ShowContinueError(state, format("Entered in {}={}", state.dataIPShortCut->cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)));
     285            0 :             ShowContinueError(state, "Value must be greater than 0.0");
     286            0 :             ErrorsFound = true;
     287              :         }
     288            3 :         if (state.dataIPShortCut->rNumericArgs(4) <= 0.0) {
     289            0 :             ShowSevereError(state, format("Invalid {}={:.2R}", state.dataIPShortCut->cNumericFieldNames(4), state.dataIPShortCut->rNumericArgs(4)));
     290            0 :             ShowContinueError(state, format("Entered in {}={}", state.dataIPShortCut->cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)));
     291            0 :             ShowContinueError(state, "Value must be greater than 0.0");
     292            0 :             ErrorsFound = true;
     293              :         }
     294            3 :         if (state.dataIPShortCut->rNumericArgs(3) > state.dataIPShortCut->rNumericArgs(4)) { // error
     295            0 :             ShowSevereError(state, format("For {}: {}", state.dataIPShortCut->cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)));
     296            0 :             ShowContinueError(state,
     297            0 :                               format("{} [{:.2R}] > {} [{:.2R}]",
     298            0 :                                      state.dataIPShortCut->cNumericFieldNames(3),
     299            0 :                                      state.dataIPShortCut->rNumericArgs(3),
     300            0 :                                      state.dataIPShortCut->cNumericFieldNames(4),
     301            0 :                                      state.dataIPShortCut->rNumericArgs(4)));
     302            0 :             ErrorsFound = true;
     303              :         }
     304              : 
     305              :         // thermal conductivity data
     306            3 :         state.dataPondGHE->PondGHE(Item).TubeConductivity = state.dataIPShortCut->rNumericArgs(5);
     307            3 :         state.dataPondGHE->PondGHE(Item).GrndConductivity = state.dataIPShortCut->rNumericArgs(6);
     308              : 
     309            3 :         if (state.dataIPShortCut->rNumericArgs(5) <= 0.0) {
     310            0 :             ShowSevereError(state, format("Invalid {}={:.4R}", state.dataIPShortCut->cNumericFieldNames(5), state.dataIPShortCut->rNumericArgs(5)));
     311            0 :             ShowContinueError(state, format("Entered in {}={}", state.dataIPShortCut->cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)));
     312            0 :             ShowContinueError(state, "Value must be greater than 0.0");
     313            0 :             ErrorsFound = true;
     314              :         }
     315            3 :         if (state.dataIPShortCut->rNumericArgs(6) <= 0.0) {
     316            0 :             ShowSevereError(state, format("Invalid {}={:.4R}", state.dataIPShortCut->cNumericFieldNames(6), state.dataIPShortCut->rNumericArgs(6)));
     317            0 :             ShowContinueError(state, format("Entered in {}={}", state.dataIPShortCut->cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)));
     318            0 :             ShowContinueError(state, "Value must be greater than 0.0");
     319            0 :             ErrorsFound = true;
     320              :         }
     321              : 
     322              :         // circuits
     323            3 :         state.dataPondGHE->PondGHE(Item).NumCircuits = state.dataIPShortCut->rNumericArgs(7);
     324              : 
     325            3 :         if (state.dataIPShortCut->rNumericArgs(7) <= 0) {
     326            0 :             ShowSevereError(state, format("Invalid {}={:.2R}", state.dataIPShortCut->cNumericFieldNames(7), state.dataIPShortCut->rNumericArgs(7)));
     327            0 :             ShowContinueError(state, format("Entered in {}={}", state.dataIPShortCut->cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)));
     328            0 :             ShowContinueError(state, "Value must be greater than 0.0");
     329            0 :             ErrorsFound = true;
     330              :         }
     331            3 :         state.dataPondGHE->PondGHE(Item).CircuitLength = state.dataIPShortCut->rNumericArgs(8);
     332            3 :         if (state.dataIPShortCut->rNumericArgs(8) <= 0) {
     333            0 :             ShowSevereError(state, format("Invalid {}={:.2R}", state.dataIPShortCut->cNumericFieldNames(8), state.dataIPShortCut->rNumericArgs(8)));
     334            0 :             ShowContinueError(state, format("Entered in {}={}", state.dataIPShortCut->cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)));
     335            0 :             ShowContinueError(state, "Value must be greater than 0.0");
     336            0 :             ErrorsFound = true;
     337              :         }
     338              : 
     339              :     } // end of input loop
     340              : 
     341              :     // final error check
     342            3 :     if (ErrorsFound) {
     343            0 :         ShowFatalError(state, format("Errors found in processing input for {}", state.dataIPShortCut->cCurrentModuleObject));
     344              :     }
     345              : 
     346            3 :     if (!state.dataEnvrn->GroundTempInputs[(int)DataEnvironment::GroundTempType::Deep]) {
     347            0 :         ShowWarningError(state, "GetPondGroundHeatExchanger:  No \"Site:GroundTemperature:Deep\" were input.");
     348            0 :         ShowContinueError(state,
     349            0 :                           format("Defaults, constant throughout the year of ({:.1R}) will be used.",
     350            0 :                                  state.dataEnvrn->GroundTemp[(int)DataEnvironment::GroundTempType::Deep]));
     351              :     }
     352            3 : }
     353              : 
     354            3 : void PondGroundHeatExchangerData::setupOutputVars(EnergyPlusData &state)
     355              : {
     356            6 :     SetupOutputVariable(state,
     357              :                         "Pond Heat Exchanger Heat Transfer Rate",
     358              :                         Constant::Units::W,
     359            3 :                         this->HeatTransferRate,
     360              :                         OutputProcessor::TimeStepType::System,
     361              :                         OutputProcessor::StoreType::Average,
     362            3 :                         this->Name);
     363            6 :     SetupOutputVariable(state,
     364              :                         "Pond Heat Exchanger Heat Transfer Energy",
     365              :                         Constant::Units::J,
     366            3 :                         this->Energy,
     367              :                         OutputProcessor::TimeStepType::System,
     368              :                         OutputProcessor::StoreType::Sum,
     369            3 :                         this->Name);
     370            6 :     SetupOutputVariable(state,
     371              :                         "Pond Heat Exchanger Mass Flow Rate",
     372              :                         Constant::Units::kg_s,
     373            3 :                         this->MassFlowRate,
     374              :                         OutputProcessor::TimeStepType::System,
     375              :                         OutputProcessor::StoreType::Average,
     376            3 :                         this->Name);
     377            6 :     SetupOutputVariable(state,
     378              :                         "Pond Heat Exchanger Inlet Temperature",
     379              :                         Constant::Units::C,
     380            3 :                         this->InletTemp,
     381              :                         OutputProcessor::TimeStepType::System,
     382              :                         OutputProcessor::StoreType::Average,
     383            3 :                         this->Name);
     384            6 :     SetupOutputVariable(state,
     385              :                         "Pond Heat Exchanger Outlet Temperature",
     386              :                         Constant::Units::C,
     387            3 :                         this->OutletTemp,
     388              :                         OutputProcessor::TimeStepType::System,
     389              :                         OutputProcessor::StoreType::Average,
     390            3 :                         this->Name);
     391            6 :     SetupOutputVariable(state,
     392              :                         "Pond Heat Exchanger Bulk Temperature",
     393              :                         Constant::Units::C,
     394            3 :                         this->PondTemp,
     395              :                         OutputProcessor::TimeStepType::System,
     396              :                         OutputProcessor::StoreType::Average,
     397            3 :                         this->Name);
     398            3 : }
     399              : 
     400       123966 : void PondGroundHeatExchangerData::InitPondGroundHeatExchanger(EnergyPlusData &state,
     401              :                                                               bool const FirstHVACIteration // TRUE if 1st HVAC simulation of system timestep
     402              : )
     403              : {
     404              : 
     405              :     // SUBROUTINE INFORMATION:
     406              :     //       AUTHOR         Simon Rees
     407              :     //       DATE WRITTEN   August 2002
     408              :     //       MODIFIED       na
     409              :     //       RE-ENGINEERED  na
     410              : 
     411              :     // PURPOSE OF THIS SUBROUTINE:
     412              :     // This subroutine Resets the elements of the data structure as necessary
     413              :     // at the first HVAC iteration of each time step.
     414              : 
     415              :     // METHODOLOGY EMPLOYED:
     416              :     // One of the things done here is to update the record of the past pond
     417              :     // temperature. This is needed in order to solve the diff. eqn. to find
     418              :     // the temperature at the end of the next time step.
     419              :     // Also set module variables to data structure for this pond. Set flow rate
     420              :     // from node data and hypothetical design flow.
     421              : 
     422              :     // repeated warm up days tend to drive the initial pond temperature toward the drybulb temperature
     423              :     // For each environment start the pond midway between drybulb and ground temp.
     424              : 
     425       123966 :     this->oneTimeInit(state);
     426              : 
     427       123966 :     if (FirstHVACIteration && !state.dataHVACGlobal->ShortenTimeStepSys && this->firstTimeThrough) {
     428              :         // update past temperature
     429         6088 :         this->PastBulkTemperature = this->BulkTemperature;
     430         6088 :         this->firstTimeThrough = false;
     431       117878 :     } else if (!FirstHVACIteration) {
     432        61968 :         this->firstTimeThrough = true;
     433              :     }
     434              : 
     435       123966 :     this->InletTemp = state.dataLoopNodes->Node(InletNodeNum).Temp;
     436       123966 :     this->PondTemp = this->BulkTemperature;
     437              : 
     438              :     // Hypothetical design flow rate
     439       123966 :     Real64 DesignFlow = PlantUtilities::RegulateCondenserCompFlowReqOp(state, this->plantLoc, this->DesignMassFlowRate);
     440              : 
     441       123966 :     PlantUtilities::SetComponentFlowRate(state, DesignFlow, this->InletNodeNum, this->OutletNodeNum, this->plantLoc);
     442              : 
     443              :     // get the current flow rate - module variable
     444       123966 :     this->MassFlowRate = state.dataLoopNodes->Node(InletNodeNum).MassFlowRate;
     445       123966 : }
     446              : 
     447       123951 : void PondGroundHeatExchangerData::CalcPondGroundHeatExchanger(EnergyPlusData &state)
     448              : {
     449              : 
     450              :     //       AUTHOR         Simon Rees
     451              :     //       DATE WRITTEN   August 2002
     452              :     //       MODIFIED       na
     453              :     //       RE-ENGINEERED  na
     454              : 
     455              :     // PURPOSE OF THIS SUBROUTINE:
     456              :     // This subroutine does all of the stuff that is necessary to simulate
     457              :     // a pond ground heat exchanger.  Calls are made to appropriate subroutines
     458              :     // either in this module or outside of it.
     459              : 
     460              :     // METHODOLOGY EMPLOYED:
     461              :     // The differential equation defined by the heat balance is solved using
     462              :     // a fourth order Runge-Kutta numerical integration method. The differential
     463              :     // equation is:
     464              :     //            Mdot*Cp*dT/dt = Sum of fluxes.
     465              : 
     466              :     // REFERENCES:
     467              :     // Chiasson, A. Advances in Modeling of Ground-Source Heat Pump Systems.
     468              :     //   M.S. Thesis, Oklahoma State University, December 1999.
     469              :     // Chiasson, A.D., J.D. Spitler, S.J. Rees, M.D. Smith.  2000.  A Model For
     470              :     //   Simulating The Performance Of A Shallow Pond As A Supplemental Heat
     471              :     //   Rejecter With Closed-Loop Ground-Source Heat Pump Systems.
     472              :     //   ASHRAE Transactions.  106(2):107-121.
     473              : 
     474              :     static constexpr std::string_view RoutineName("CalcPondGroundHeatExchanger");
     475              : 
     476       123951 :     Real64 PondMass = this->Depth * this->Area * this->water->getDensity(state, max(this->PondTemp, 0.0), RoutineName);
     477       123951 :     Real64 SpecificHeat = this->water->getSpecificHeat(state, max(this->PondTemp, 0.0), RoutineName);
     478              : 
     479       123951 :     Real64 Flux = this->CalcTotalFLux(state, this->PondTemp);
     480              :     Real64 PondTempStar =
     481       123951 :         this->PastBulkTemperature + 0.5 * Constant::rSecsInHour * state.dataHVACGlobal->TimeStepSys * Flux / (SpecificHeat * PondMass);
     482              : 
     483       123951 :     Real64 FluxStar = this->CalcTotalFLux(state, PondTempStar);
     484              :     Real64 PondTempStarStar =
     485       123951 :         this->PastBulkTemperature + 0.5 * Constant::rSecsInHour * state.dataHVACGlobal->TimeStepSys * FluxStar / (SpecificHeat * PondMass);
     486              : 
     487       123951 :     Real64 FluxStarStar = this->CalcTotalFLux(state, PondTempStarStar);
     488              :     Real64 PondTempStarStarStar =
     489       123951 :         this->PastBulkTemperature + Constant::rSecsInHour * state.dataHVACGlobal->TimeStepSys * FluxStarStar / (SpecificHeat * PondMass);
     490              : 
     491       123951 :     this->PondTemp = this->PastBulkTemperature + Constant::rSecsInHour * state.dataHVACGlobal->TimeStepSys *
     492       123951 :                                                      (Flux + 2.0 * FluxStar + 2.0 * FluxStarStar + this->CalcTotalFLux(state, PondTempStarStarStar)) /
     493       123951 :                                                      (6.0 * SpecificHeat * PondMass);
     494       123951 : }
     495              : 
     496       495804 : Real64 PondGroundHeatExchangerData::CalcTotalFLux(EnergyPlusData &state, Real64 const PondBulkTemp // pond temp for this flux calculation
     497              : )
     498              : {
     499              :     //       AUTHOR         Simon Rees
     500              :     //       DATE WRITTEN   August 2002
     501              :     //       MODIFIED       na
     502              :     //       RE-ENGINEERED  na
     503              : 
     504              :     // PURPOSE OF THIS FUNCTION:
     505              :     // This calculates the summation of the heat fluxes on the pond for a
     506              :     // given pond temperature. The following heat fluxes are calculated:
     507              :     //   convection,
     508              :     //   long-wave radiation,
     509              :     //   solar gain,
     510              :     //   evaporation,
     511              :     //   ground conduction,
     512              :     //   along with heat exchange with the fluid
     513              : 
     514              :     // METHODOLOGY EMPLOYED:
     515              :     // Convection is calculated with the ASHRAE simple convection coefficients.
     516              :     // Evaporation is calculated assuming a fixed Lewis number - not as in
     517              :     // Chaisson model. Heat transfer with the fluid is calculated using a heat
     518              :     // exchanger Effectiveness-NTU method, where the pond is seen as a static
     519              :     // fluid - this is also different from Chaisson's original model (assumed
     520              :     // pond at average of inlet and outlet temps).
     521              : 
     522              :     // REFERENCES:
     523              :     // Chiasson, A. Advances in Modeling of Ground-Source Heat Pump Systems.
     524              :     //   M.S. Thesis, Oklahoma State University, December 1999.
     525              :     // Chiasson, A.D., J.D. Spitler, S.J. Rees, M.D. Smith.  2000.  A Model For
     526              :     //   Simulating The Performance Of A Shallow Pond As A Supplemental Heat
     527              :     //   Rejecter With Closed-Loop Ground-Source Heat Pump Systems.
     528              :     //   ASHRAE Transactions.  106(2):107-121.
     529              :     // Hull, J.R., K.V. Liu, W.T. Sha, J. Kamal, and C.E. Nielsen, 1984.
     530              :     //   Dependence of Ground Heat Losses Upon Solar Pond Size and Perimeter
     531              :     //   Insulation Calculated and Experimental Results. Solar Energy,33(1):25-33.
     532              : 
     533              :     Real64 CalcTotalFLux; // function return variable
     534              : 
     535       495804 :     Real64 constexpr PrandtlAir(0.71); // Prandtl number for air - assumed constant
     536       495804 :     Real64 constexpr SchmidtAir(0.6);  // Schmidt number for air - assumed constant
     537       495804 :     Real64 constexpr PondHeight(0.0);  // for now
     538              : 
     539              :     static constexpr std::string_view RoutineName("PondGroundHeatExchanger:CalcTotalFlux");
     540              : 
     541              :     // make a surface heat balance and solve for temperature
     542       495804 :     Real64 ThermalAbs = 0.9; // thermal absorptivity
     543              : 
     544              :     // set appropriate external temp
     545              :     // use height dependency --  if there was a height for this unit, it could be inserted.
     546              :     // parameter PondHeight=0.0 is used.
     547       495804 :     Real64 OutDryBulb = DataEnvironment::OutDryBulbTempAt(state, PondHeight);
     548       495804 :     Real64 OutWetBulb = DataEnvironment::OutWetBulbTempAt(state, PondHeight);
     549              : 
     550              :     Real64 ExternalTemp; // external environmental temp - drybulb or wetbulb
     551       495804 :     if (state.dataEnvrn->IsSnow || state.dataEnvrn->IsRain) {
     552            0 :         ExternalTemp = OutWetBulb;
     553              :     } else { // normal dry conditions
     554       495804 :         ExternalTemp = OutDryBulb;
     555              :     }
     556              : 
     557              :     // absolute temperatures
     558       495804 :     Real64 SurfTempAbs = PondBulkTemp + Constant::Kelvin;            // absolute value of surface temp
     559       495804 :     Real64 SkyTempAbs = state.dataEnvrn->SkyTemp + Constant::Kelvin; // absolute value of sky temp
     560              : 
     561              :     // ASHRAE simple convection coefficient model for external surfaces.
     562       495804 :     Real64 ConvCoef = Convect::CalcASHRAESimpExtConvCoeff(Material::SurfaceRoughness::VeryRough, DataEnvironment::WindSpeedAt(state, PondHeight));
     563              : 
     564              :     // convective flux
     565       495804 :     Real64 FluxConvect = ConvCoef * (PondBulkTemp - ExternalTemp);
     566              : 
     567              :     // long-wave radiation between pond and sky.
     568       495804 :     Real64 FluxLongwave = StefBoltzmann * ThermalAbs * (pow_4(SurfTempAbs) - pow_4(SkyTempAbs));
     569              : 
     570              :     // total absorbed solar using function - no ground solar
     571       495804 :     Real64 FluxSolAbsorbed = CalcSolarFlux(state);
     572              : 
     573              :     // specific heat from fluid prop routines
     574       495804 :     Real64 SpecHeat = this->plantLoc.loop->glycol->getSpecificHeat(state, max(this->InletTemp, 0.0), RoutineName);
     575              :     // heat transfer with fluid - heat exchanger analogy.
     576              : 
     577              :     // convective flux
     578       495804 :     Real64 effectiveness = this->CalcEffectiveness(state, this->InletTemp, PondBulkTemp, this->MassFlowRate);
     579       495804 :     Real64 Qfluid = this->MassFlowRate * SpecHeat * effectiveness * (this->InletTemp - PondBulkTemp);
     580              : 
     581              :     // evaporation flux
     582              :     // get air properties
     583       495804 :     Real64 HumRatioAir = Psychrometrics::PsyWFnTdbTwbPb(state, OutDryBulb, OutWetBulb, state.dataEnvrn->OutBaroPress);
     584              : 
     585              :     // humidity ratio at pond surface/film temperature
     586       495804 :     Real64 HumRatioFilm = Psychrometrics::PsyWFnTdbTwbPb(state, PondBulkTemp, PondBulkTemp, state.dataEnvrn->OutBaroPress);
     587       495804 :     Real64 SpecHeatAir = Psychrometrics::PsyCpAirFnW(HumRatioAir);
     588       495804 :     Real64 LatentHeatAir = Psychrometrics::PsyHfgAirFnWTdb(HumRatioAir, OutDryBulb);
     589              : 
     590              :     // evaporative heat flux
     591       495804 :     Real64 FluxEvap = pow_2(PrandtlAir / SchmidtAir) / 3.0 * ConvCoef / SpecHeatAir * (HumRatioFilm - HumRatioAir) * LatentHeatAir;
     592              : 
     593              :     // ground heat transfer flux
     594       495804 :     Real64 Perimeter = 4.0 * std::sqrt(this->Area); // pond perimeter -- square assumption
     595              : 
     596              :     // ground heat transfer coefficient
     597       495804 :     Real64 UvalueGround = 0.999 * (this->GrndConductivity / this->Depth) + 1.37 * (this->GrndConductivity * Perimeter / this->Area);
     598              : 
     599              :     // ground heat transfer flux
     600       495804 :     Real64 FluxGround = UvalueGround * (PondBulkTemp - state.dataEnvrn->GroundTemp[(int)DataEnvironment::GroundTempType::Deep]);
     601              : 
     602       495804 :     CalcTotalFLux = Qfluid + this->Area * (FluxSolAbsorbed - FluxConvect - FluxLongwave - FluxEvap - FluxGround);
     603              : 
     604       495804 :     return CalcTotalFLux;
     605              : }
     606              : 
     607       495804 : Real64 PondGroundHeatExchangerData::CalcSolarFlux(EnergyPlusData &state) const
     608              : {
     609              : 
     610              :     // FUNCTION INFORMATION:
     611              :     //       AUTHOR         Simon Rees
     612              :     //       DATE WRITTEN   August 2002
     613              :     //       MODIFIED       na
     614              :     //       RE-ENGINEERED  na
     615              : 
     616              :     // PURPOSE OF THIS SUBROUTINE:
     617              :     // This is used to calculate the net solar flux absorbed by the pond.
     618              : 
     619              :     // METHODOLOGY EMPLOYED:
     620              :     // This is calculated from basic optical formula using the extinction
     621              :     // coefficient of the pond as the main parameter. This can be in a
     622              :     // wide range: 0.13 - 7.5 in the literature depending on algae, suspended
     623              :     // solids etc. ??
     624              : 
     625              :     // REFERENCES:
     626              :     // Duffie, J.A. and W.A. Beckman, 1991. Solar Engineering of Thermal
     627              :     //  Processes, 2 nd Edition. John Wiley and Sons.
     628              :     // Chiasson, A. Advances in Modeling of Ground-Source Heat Pump Systems.
     629              :     //   M.S. Thesis, Oklahoma State University, December 1999.
     630              :     // Chiasson, A.D., J.D. Spitler, S.J. Rees, M.D. Smith.  2000.  A Model For
     631              :     //   Simulating The Performance Of A Shallow Pond As A Supplemental Heat
     632              :     //   Rejecter With Closed-Loop Ground-Source Heat Pump Systems.
     633              :     //   ASHRAE Transactions.  106(2):107-121.
     634              : 
     635              :     Real64 CalcSolarFlux; // Function return variable
     636              : 
     637       495804 :     Real64 constexpr WaterRefIndex(1.33); // refractive index of water
     638       495804 :     Real64 constexpr AirRefIndex(1.0003); // refractive index of air
     639       495804 :     Real64 constexpr PondExtCoef(0.3);    // extinction coefficient of water
     640              : 
     641              :     // check for sun up.
     642       495804 :     if (!state.dataEnvrn->SunIsUp) {
     643       249836 :         CalcSolarFlux = 0.0;
     644       249836 :         return CalcSolarFlux;
     645              :     }
     646              : 
     647              :     // get the incidence and reflection angles
     648       245968 :     Real64 IncidAngle = std::acos(state.dataEnvrn->SOLCOS(3));
     649       245968 :     Real64 RefractAngle = std::asin(std::sin(IncidAngle) * AirRefIndex / WaterRefIndex);
     650              : 
     651              :     // absorbed component: Tau_a
     652       245968 :     Real64 Absorbtance = std::exp(-PondExtCoef * this->Depth / std::cos(RefractAngle));
     653              : 
     654              :     // parallel and perpendicular components
     655       245968 :     Real64 ParallelRad = pow_2(std::tan(RefractAngle - IncidAngle)) / pow_2(std::tan(RefractAngle + IncidAngle));
     656       245968 :     Real64 PerpendRad = pow_2(std::sin(RefractAngle - IncidAngle)) / pow_2(std::sin(RefractAngle + IncidAngle));
     657              : 
     658              :     // transmittance: Tau
     659       245968 :     Real64 Transmitance = 0.5 * Absorbtance * ((1.0 - ParallelRad) / (1.0 + ParallelRad) + (1.0 - PerpendRad) / (1.0 + PerpendRad));
     660              : 
     661              :     // reflectance: Tau_a - Tau
     662       245968 :     Real64 Reflectance = Absorbtance - Transmitance;
     663              : 
     664              :     // apply reflectance to beam and diffuse solar to find flux
     665       245968 :     CalcSolarFlux = (1.0 - Reflectance) * (state.dataEnvrn->SOLCOS(3) * state.dataEnvrn->BeamSolarRad + state.dataEnvrn->DifSolarRad);
     666              : 
     667       245968 :     return CalcSolarFlux;
     668              : }
     669              : 
     670       619755 : Real64 PondGroundHeatExchangerData::CalcEffectiveness(EnergyPlusData &state,
     671              :                                                       Real64 const InsideTemperature, // Temperature of fluid in pipe circuit, in C
     672              :                                                       Real64 const PondTemperature,   // Temperature of pond water (i.e. outside the pipe), in C
     673              :                                                       Real64 const massFlowRate       // Mass flow rate, in kg/s
     674              : )
     675              : {
     676              : 
     677              :     // FUNCTION INFORMATION:
     678              :     //       AUTHOR         Simon Rees
     679              :     //       DATE WRITTEN   August 2002
     680              :     //       MODIFIED       na
     681              :     //       RE-ENGINEERED  na
     682              : 
     683              :     // PURPOSE OF THIS SUBROUTINE:
     684              :     // This subroutine calculates the "heat exchanger" effectiveness.
     685              :     // This routine is adapted from that in the low temp radiant pond model.
     686              : 
     687              :     // METHODOLOGY EMPLOYED:
     688              :     // The heat transfer coefficient is calculated at the pipe and
     689              :     // consists of inside and outside convection coefficients and conduction
     690              :     // through the pipe. The other assumptions are that the tube inside
     691              :     // surface temperature is equal to the "source location temperature"
     692              :     // and that it is a CONSTANT throughout the pond. External convection is
     693              :     // natural mode using Churchill and Chu correlation. Inside convection
     694              :     // calculated using the Dittus-Boelter equation.
     695              : 
     696              :     // REFERENCES:
     697              :     // Incropera, F.P. and D.P. DeWitt, 1996. Introduction to Heat Transfer,
     698              :     //   3 rd Edition. John Wiley & Sons.
     699              :     // Churchill, S.W. and H.H.S. Chu. 1975. Correlating Equations for
     700              :     //   Laminar and Turbulent Free Convection from a Horizontal Cylinder.
     701              :     //   International Journal of Heat and Mass Transfer, 18: 1049-1053.
     702              :     // See also RadiantSystemLowTemp module.
     703              : 
     704              :     Real64 CalcEffectiveness; // Function return variable
     705              : 
     706       619755 :     Real64 constexpr MaxLaminarRe(2300.0); // Maximum Reynolds number for laminar flow
     707       619755 :     Real64 constexpr GravConst(9.81);      // gravitational constant - should be fixed!
     708              :     static constexpr std::string_view CalledFrom("PondGroundHeatExchanger:CalcEffectiveness");
     709              : 
     710              :     // evaluate properties at pipe fluid temperature for given pipe fluid
     711              : 
     712       619755 :     Real64 SpecificHeat = this->plantLoc.loop->glycol->getSpecificHeat(state, InsideTemperature, CalledFrom);
     713       619755 :     Real64 Conductivity = this->plantLoc.loop->glycol->getConductivity(state, InsideTemperature, CalledFrom);
     714       619755 :     Real64 Viscosity = this->plantLoc.loop->glycol->getViscosity(state, InsideTemperature, CalledFrom);
     715              : 
     716              :     // Calculate the Reynold's number from RE=(4*Mdot)/(Pi*Mu*Diameter)
     717       619755 :     Real64 ReynoldsNum = 4.0 * massFlowRate / (Constant::Pi * Viscosity * this->TubeInDiameter * this->NumCircuits);
     718              : 
     719       619755 :     Real64 PrantlNum = Viscosity * SpecificHeat / Conductivity;
     720              : 
     721              :     Real64 NusseltNum; // Nusselt number (dimensionless)
     722              : 
     723              :     // Calculate the Nusselt number based on what flow regime one is in. h = (k)(Nu)/D
     724       619755 :     if (ReynoldsNum >= MaxLaminarRe) { // Turbulent flow --> use Dittus-Boelter equation
     725       500120 :         NusseltNum = 0.023 * std::pow(ReynoldsNum, 0.8) * std::pow(PrantlNum, 0.3);
     726              :     } else { // Laminar flow --> use constant surface temperature relation
     727       119635 :         NusseltNum = 3.66;
     728              :     }
     729              : 
     730              :     // inside convection resistance, from Nu
     731       619755 :     Real64 ConvCoefIn = Conductivity * NusseltNum / this->TubeInDiameter;
     732              : 
     733              :     // now find properties of pond water - always assume pond fluid is water
     734       619755 :     Real64 WaterSpecHeat = this->water->getSpecificHeat(state, max(PondTemperature, 0.0), CalledFrom);
     735       619755 :     Real64 WaterConductivity = this->water->getConductivity(state, max(PondTemperature, 0.0), CalledFrom);
     736       619755 :     Real64 WaterViscosity = this->water->getViscosity(state, max(PondTemperature, 0.0), CalledFrom);
     737       619755 :     Real64 WaterDensity = this->water->getDensity(state, max(PondTemperature, 0.0), CalledFrom);
     738              : 
     739              :     // derived properties for natural convection coefficient
     740              :     // expansion coef (Beta) = -1/Rho. dRho/dT
     741              :     // The following code includes some slight modifications from Simon's original code.
     742              :     // It guarantees that the delta T is 10C and also avoids the problems associated with
     743              :     // water hitting a maximum density at around 4C. (RKS)
     744       619755 :     Real64 ExpansionCoef = -(this->water->getDensity(state, max(PondTemperature, 10.0) + 5.0, CalledFrom) -
     745       619755 :                              this->water->getDensity(state, max(PondTemperature, 10.0) - 5.0, CalledFrom)) /
     746       619755 :                            (10.0 * WaterDensity);
     747              : 
     748       619755 :     Real64 ThermDiff = WaterConductivity / (WaterDensity * WaterSpecHeat);
     749       619755 :     PrantlNum = WaterViscosity * WaterSpecHeat / WaterConductivity;
     750              : 
     751       619755 :     Real64 RayleighNum = WaterDensity * GravConst * ExpansionCoef * std::abs(InsideTemperature - PondTemperature) * pow_3(TubeOutDiameter) /
     752       619755 :                          (WaterViscosity * ThermDiff);
     753              : 
     754              :     // Calculate the Nusselt number for natural convection at outside of pipe
     755       619755 :     NusseltNum = pow_2(0.6 + (0.387 * std::pow(RayleighNum, 1.0 / 6.0) / (std::pow(1.0 + 0.559 / std::pow(PrantlNum, 9.0 / 16.0), 8.0 / 27.0))));
     756              : 
     757              :     // outside convection resistance, from Nu
     758       619755 :     Real64 ConvCoefOut = WaterConductivity * NusseltNum / this->TubeOutDiameter;
     759              : 
     760              :     // conduction resistance of pipe
     761       619755 :     Real64 PipeResistance = this->TubeInDiameter / this->TubeConductivity * std::log(this->TubeOutDiameter / this->TubeInDiameter);
     762              : 
     763              :     // total pipe thermal resistance - conduction and convection
     764       619755 :     Real64 TotalResistance = PipeResistance + 1.0 / ConvCoefIn + this->TubeInDiameter / (this->TubeOutDiameter * ConvCoefOut);
     765              : 
     766              :     // Calculate the NTU parameter
     767              :     // NTU = UA/[(Mdot*Cp)min] = A/[Rtot*(Mdot*Cp)min]
     768              :     // where: Rtot = Ri,convection + Rconduction + Ro,conveciton
     769              :     //        A = Pi*D*TubeLength
     770              : 
     771              :     Real64 NTU; // Number of transfer units, non-dimensional
     772              : 
     773       619755 :     if (massFlowRate == 0.0) {
     774        55955 :         CalcEffectiveness = 1.0;
     775              :     } else {
     776       563800 :         NTU = Constant::Pi * TubeInDiameter * this->CircuitLength * this->NumCircuits / (TotalResistance * massFlowRate * SpecificHeat);
     777              :         // Calculate effectiveness - formula for static fluid
     778       563800 :         CalcEffectiveness = (1.0 - std::exp(-NTU));
     779              :     }
     780              : 
     781              :     // Check for frozen pond
     782       619755 :     if (PondTemperature < 0.0) {
     783            0 :         ++this->ConsecutiveFrozen;
     784            0 :         if (this->FrozenErrIndex == 0) {
     785            0 :             ShowWarningMessage(state,
     786            0 :                                format("GroundHeatExchanger:Pond=\"{}\", is frozen; Pond model not valid. Calculated Pond Temperature=[{:.2R}] C",
     787            0 :                                       this->Name,
     788              :                                       PondTemperature));
     789            0 :             ShowContinueErrorTimeStamp(state, "");
     790              :         }
     791            0 :         ShowRecurringWarningErrorAtEnd(state,
     792            0 :                                        "GroundHeatExchanger:Pond=\"" + this->Name + "\", is frozen",
     793            0 :                                        this->FrozenErrIndex,
     794              :                                        PondTemperature,
     795              :                                        PondTemperature,
     796              :                                        _,
     797              :                                        "[C]",
     798              :                                        "[C]");
     799            0 :         if (this->ConsecutiveFrozen >= state.dataGlobal->TimeStepsInHour * 30) {
     800            0 :             ShowFatalError(state,
     801            0 :                            format("GroundHeatExchanger:Pond=\"{}\" has been frozen for 30 consecutive hours.  Program terminates.", this->Name));
     802              :         }
     803              :     } else {
     804       619755 :         this->ConsecutiveFrozen = 0;
     805              :     }
     806              : 
     807       619755 :     return CalcEffectiveness;
     808              : }
     809              : 
     810       123951 : void PondGroundHeatExchangerData::UpdatePondGroundHeatExchanger(EnergyPlusData &state)
     811              : {
     812              : 
     813              :     // SUBROUTINE INFORMATION:
     814              :     //       AUTHOR         Simon Rees
     815              :     //       DATE WRITTEN   August 2002
     816              :     //       MODIFIED       na
     817              :     //       RE-ENGINEERED  na
     818              : 
     819              :     // PURPOSE OF THIS SUBROUTINE:
     820              :     // This subroutine does any updating that needs to be done for pond
     821              :     // ground heat exchangers.   This routine must also set the outlet water
     822              :     // conditions.
     823              : 
     824              :     static constexpr std::string_view RoutineName("PondGroundHeatExchanger:Update");
     825              : 
     826              :     // Calculate the water side outlet conditions and set the
     827              :     // appropriate conditions on the correct HVAC node.
     828       123951 :     Real64 CpFluid = this->plantLoc.loop->glycol->getSpecificHeat(state, this->InletTemp, RoutineName);
     829              : 
     830       123951 :     PlantUtilities::SafeCopyPlantNode(state, InletNodeNum, OutletNodeNum);
     831              : 
     832              :     // update outlet temp
     833       123951 :     if ((CpFluid > 0.0) && (this->MassFlowRate > 0.0)) {
     834       112760 :         this->OutletTemp = this->InletTemp - this->HeatTransferRate / (this->MassFlowRate * CpFluid);
     835              :     } else {
     836        11191 :         this->OutletTemp = this->InletTemp;
     837              :     }
     838              : 
     839              :     // update node
     840       123951 :     state.dataLoopNodes->Node(this->OutletNodeNum).Temp = this->OutletTemp;
     841       123951 :     state.dataLoopNodes->Node(this->OutletNodeNum).MassFlowRate = this->MassFlowRate;
     842              : 
     843              :     // update heat transfer rate
     844              :     // compute pond heat transfer
     845       123951 :     Real64 effectiveness = this->CalcEffectiveness(state, this->InletTemp, this->PondTemp, this->MassFlowRate);
     846       123951 :     this->HeatTransferRate = this->MassFlowRate * CpFluid * effectiveness * (this->InletTemp - this->PondTemp);
     847       123951 :     this->Energy = this->HeatTransferRate * state.dataHVACGlobal->TimeStepSysSec;
     848              : 
     849              :     // keep track of the bulk temperature
     850       123951 :     this->BulkTemperature = this->PondTemp;
     851       123951 : }
     852       123966 : void PondGroundHeatExchangerData::oneTimeInit(EnergyPlusData &state)
     853              : {
     854       123966 :     Real64 constexpr DesignVelocity(0.5); // Hypothetical design max pipe velocity [m/s]
     855       123966 :     Real64 constexpr PondHeight(0.0);     // for now
     856              : 
     857       123972 :     static std::string const RoutineName("InitPondGroundHeatExchanger");
     858              : 
     859       123966 :     if (this->setupOutputVarsFlag) {
     860            3 :         this->setupOutputVars(state);
     861            3 :         this->setupOutputVarsFlag = false;
     862              :     }
     863              : 
     864       123966 :     if (this->OneTimeFlag || state.dataGlobal->WarmupFlag) {
     865              :         // initialize pond temps to mean of drybulb and ground temps.
     866       106490 :         this->BulkTemperature = this->PastBulkTemperature =
     867       106490 :             0.5 * (DataEnvironment::OutDryBulbTempAt(state, PondHeight) + state.dataEnvrn->GroundTemp[(int)DataEnvironment::GroundTempType::Deep]);
     868       106490 :         this->OneTimeFlag = false;
     869              :     }
     870              : 
     871              :     // Init more variables
     872       123966 :     if (this->MyFlag) {
     873              :         // Locate the hx on the plant loops for later usage
     874            3 :         bool errFlag = false;
     875            6 :         PlantUtilities::ScanPlantLoopsForObject(
     876            3 :             state, this->Name, DataPlant::PlantEquipmentType::GrndHtExchgPond, this->plantLoc, errFlag, _, _, _, _, _);
     877            3 :         if (errFlag) {
     878            0 :             ShowFatalError(state, "InitPondGroundHeatExchanger: Program terminated due to previous condition(s).");
     879              :         }
     880            3 :         Real64 rho = this->plantLoc.loop->glycol->getDensity(state, 0.0, RoutineName);
     881            3 :         Real64 Cp = this->plantLoc.loop->glycol->getSpecificHeat(state, 0.0, RoutineName);
     882            3 :         this->DesignMassFlowRate = Constant::Pi / 4.0 * pow_2(this->TubeInDiameter) * DesignVelocity * rho * this->NumCircuits;
     883            3 :         this->DesignCapacity = this->DesignMassFlowRate * Cp * 10.0; // assume 10C delta T?
     884            3 :         PlantUtilities::InitComponentNodes(state, 0.0, this->DesignMassFlowRate, this->InletNodeNum, this->OutletNodeNum);
     885            3 :         PlantUtilities::RegisterPlantCompDesignFlow(state, this->InletNodeNum, this->DesignMassFlowRate / rho);
     886              : 
     887            3 :         this->MyFlag = false;
     888              :     }
     889       123966 : }
     890              : 
     891              : } // namespace EnergyPlus::PondGroundHeatExchanger
        

Generated by: LCOV version 2.0-1