LCOV - code coverage report
Current view: top level - EnergyPlus - PondGroundHeatExchanger.cc (source / functions) Hit Total Coverage
Test: lcov.output.filtered Lines: 255 318 80.2 %
Date: 2023-01-17 19:17:23 Functions: 15 15 100.0 %

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

Generated by: LCOV version 1.13