LCOV - code coverage report
Current view: top level - EnergyPlus - PondGroundHeatExchanger.cc (source / functions) Hit Total Coverage
Test: lcov.output.filtered Lines: 251 318 78.9 %
Date: 2024-08-23 23:50:59 Functions: 13 13 100.0 %

          Line data    Source code
       1             : // EnergyPlus, Copyright (c) 1996-2024, 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             : constexpr std::string_view fluidNameWater = "WATER";
     118             : 
     119      123951 : 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      123951 :     this->InitPondGroundHeatExchanger(state, FirstHVACIteration);
     126      123951 :     this->CalcPondGroundHeatExchanger(state);
     127      123951 :     this->UpdatePondGroundHeatExchanger(state);
     128      123951 : }
     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, format("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           6 :     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           9 :         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::GetGlycolNum(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             :                                                 DataLoopNode::ObjectIsNotParent);
     227           3 :         if (state.dataPondGHE->PondGHE(Item).InletNodeNum == 0) {
     228           0 :             ShowSevereError(state, format("Invalid {}={}", state.dataIPShortCut->cAlphaFieldNames(2), state.dataIPShortCut->cAlphaArgs(2)));
     229           0 :             ShowContinueError(state, format("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             :                                                 DataLoopNode::ObjectIsNotParent);
     245           3 :         if (state.dataPondGHE->PondGHE(Item).OutletNodeNum == 0) {
     246           0 :             ShowSevereError(state, format("Invalid {}={}", state.dataIPShortCut->cAlphaFieldNames(3), state.dataIPShortCut->cAlphaArgs(3)));
     247           0 :             ShowContinueError(state, format("Entered in {}={}", state.dataIPShortCut->cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)));
     248           0 :             ErrorsFound = true;
     249             :         }
     250             : 
     251           6 :         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, format("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, format("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, format("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, format("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, format("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, format("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, format("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, format("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, format("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, format("Errors found in processing input for {}", state.dataIPShortCut->cCurrentModuleObject));
     340             :     }
     341             : 
     342           3 :     if (!state.dataEnvrn->GroundTempInputs[(int)DataEnvironment::GroundTempType::Deep]) {
     343           0 :         ShowWarningError(state, "GetPondGroundHeatExchanger:  No \"Site:GroundTemperature:Deep\" were input.");
     344           0 :         ShowContinueError(state,
     345           0 :                           format("Defaults, constant throughout the year of ({:.1R}) will be used.",
     346           0 :                                  state.dataEnvrn->GroundTemp[(int)DataEnvironment::GroundTempType::Deep]));
     347             :     }
     348           3 : }
     349             : 
     350           3 : void PondGroundHeatExchangerData::setupOutputVars(EnergyPlusData &state)
     351             : {
     352           6 :     SetupOutputVariable(state,
     353             :                         "Pond Heat Exchanger Heat Transfer Rate",
     354             :                         Constant::Units::W,
     355           3 :                         this->HeatTransferRate,
     356             :                         OutputProcessor::TimeStepType::System,
     357             :                         OutputProcessor::StoreType::Average,
     358           3 :                         this->Name);
     359           6 :     SetupOutputVariable(state,
     360             :                         "Pond Heat Exchanger Heat Transfer Energy",
     361             :                         Constant::Units::J,
     362           3 :                         this->Energy,
     363             :                         OutputProcessor::TimeStepType::System,
     364             :                         OutputProcessor::StoreType::Sum,
     365           3 :                         this->Name);
     366           6 :     SetupOutputVariable(state,
     367             :                         "Pond Heat Exchanger Mass Flow Rate",
     368             :                         Constant::Units::kg_s,
     369           3 :                         this->MassFlowRate,
     370             :                         OutputProcessor::TimeStepType::System,
     371             :                         OutputProcessor::StoreType::Average,
     372           3 :                         this->Name);
     373           6 :     SetupOutputVariable(state,
     374             :                         "Pond Heat Exchanger Inlet Temperature",
     375             :                         Constant::Units::C,
     376           3 :                         this->InletTemp,
     377             :                         OutputProcessor::TimeStepType::System,
     378             :                         OutputProcessor::StoreType::Average,
     379           3 :                         this->Name);
     380           6 :     SetupOutputVariable(state,
     381             :                         "Pond Heat Exchanger Outlet Temperature",
     382             :                         Constant::Units::C,
     383           3 :                         this->OutletTemp,
     384             :                         OutputProcessor::TimeStepType::System,
     385             :                         OutputProcessor::StoreType::Average,
     386           3 :                         this->Name);
     387           6 :     SetupOutputVariable(state,
     388             :                         "Pond Heat Exchanger Bulk Temperature",
     389             :                         Constant::Units::C,
     390           3 :                         this->PondTemp,
     391             :                         OutputProcessor::TimeStepType::System,
     392             :                         OutputProcessor::StoreType::Average,
     393           3 :                         this->Name);
     394           3 : }
     395             : 
     396      123966 : void PondGroundHeatExchangerData::InitPondGroundHeatExchanger(EnergyPlusData &state,
     397             :                                                               bool const FirstHVACIteration // TRUE if 1st HVAC simulation of system timestep
     398             : )
     399             : {
     400             : 
     401             :     // SUBROUTINE INFORMATION:
     402             :     //       AUTHOR         Simon Rees
     403             :     //       DATE WRITTEN   August 2002
     404             :     //       MODIFIED       na
     405             :     //       RE-ENGINEERED  na
     406             : 
     407             :     // PURPOSE OF THIS SUBROUTINE:
     408             :     // This subroutine Resets the elements of the data structure as necessary
     409             :     // at the first HVAC iteration of each time step.
     410             : 
     411             :     // METHODOLOGY EMPLOYED:
     412             :     // One of the things done here is to update the record of the past pond
     413             :     // temperature. This is needed in order to solve the diff. eqn. to find
     414             :     // the temperature at the end of the next time step.
     415             :     // Also set module variables to data structure for this pond. Set flow rate
     416             :     // from node data and hypothetical design flow.
     417             : 
     418             :     // repeated warm up days tend to drive the initial pond temperature toward the drybulb temperature
     419             :     // For each environment start the pond midway between drybulb and ground temp.
     420             : 
     421      123966 :     this->oneTimeInit(state);
     422             : 
     423      123966 :     if (FirstHVACIteration && !state.dataHVACGlobal->ShortenTimeStepSys && this->firstTimeThrough) {
     424             :         // update past temperature
     425        6088 :         this->PastBulkTemperature = this->BulkTemperature;
     426        6088 :         this->firstTimeThrough = false;
     427      117878 :     } else if (!FirstHVACIteration) {
     428       61968 :         this->firstTimeThrough = true;
     429             :     }
     430             : 
     431      123966 :     this->InletTemp = state.dataLoopNodes->Node(InletNodeNum).Temp;
     432      123966 :     this->PondTemp = this->BulkTemperature;
     433             : 
     434             :     // Hypothetical design flow rate
     435      123966 :     Real64 DesignFlow = PlantUtilities::RegulateCondenserCompFlowReqOp(state, this->plantLoc, this->DesignMassFlowRate);
     436             : 
     437      123966 :     PlantUtilities::SetComponentFlowRate(state, DesignFlow, this->InletNodeNum, this->OutletNodeNum, this->plantLoc);
     438             : 
     439             :     // get the current flow rate - module variable
     440      123966 :     this->MassFlowRate = state.dataLoopNodes->Node(InletNodeNum).MassFlowRate;
     441      123966 : }
     442             : 
     443      123951 : void PondGroundHeatExchangerData::CalcPondGroundHeatExchanger(EnergyPlusData &state)
     444             : {
     445             : 
     446             :     //       AUTHOR         Simon Rees
     447             :     //       DATE WRITTEN   August 2002
     448             :     //       MODIFIED       na
     449             :     //       RE-ENGINEERED  na
     450             : 
     451             :     // PURPOSE OF THIS SUBROUTINE:
     452             :     // This subroutine does all of the stuff that is necessary to simulate
     453             :     // a pond ground heat exchanger.  Calls are made to appropriate subroutines
     454             :     // either in this module or outside of it.
     455             : 
     456             :     // METHODOLOGY EMPLOYED:
     457             :     // The differential equation defined by the heat balance is solved using
     458             :     // a fourth order Runge-Kutta numerical integration method. The differential
     459             :     // equation is:
     460             :     //            Mdot*Cp*dT/dt = Sum of fluxes.
     461             : 
     462             :     // REFERENCES:
     463             :     // Chiasson, A. Advances in Modeling of Ground-Source Heat Pump Systems.
     464             :     //   M.S. Thesis, Oklahoma State University, December 1999.
     465             :     // Chiasson, A.D., J.D. Spitler, S.J. Rees, M.D. Smith.  2000.  A Model For
     466             :     //   Simulating The Performance Of A Shallow Pond As A Supplemental Heat
     467             :     //   Rejecter With Closed-Loop Ground-Source Heat Pump Systems.
     468             :     //   ASHRAE Transactions.  106(2):107-121.
     469             : 
     470             :     static constexpr std::string_view RoutineName("CalcPondGroundHeatExchanger");
     471             : 
     472      123951 :     Real64 PondMass = this->Depth * this->Area *
     473      123951 :                       FluidProperties::GetDensityGlycol(
     474      123951 :                           state, fluidNameWater, max(this->PondTemp, DataPrecisionGlobals::constant_zero), this->WaterIndex, RoutineName);
     475             : 
     476      123951 :     Real64 SpecificHeat = FluidProperties::GetSpecificHeatGlycol(
     477      123951 :         state, fluidNameWater, max(this->PondTemp, DataPrecisionGlobals::constant_zero), this->WaterIndex, RoutineName);
     478             : 
     479      123951 :     Real64 Flux = this->CalcTotalFLux(state, this->PondTemp);
     480             :     Real64 PondTempStar =
     481      123951 :         this->PastBulkTemperature + 0.5 * Constant::SecInHour * 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::SecInHour * state.dataHVACGlobal->TimeStepSys * FluxStar / (SpecificHeat * PondMass);
     486             : 
     487      123951 :     Real64 FluxStarStar = this->CalcTotalFLux(state, PondTempStarStar);
     488             :     Real64 PondTempStarStarStar =
     489      123951 :         this->PastBulkTemperature + Constant::SecInHour * state.dataHVACGlobal->TimeStepSys * FluxStarStar / (SpecificHeat * PondMass);
     490             : 
     491      123951 :     this->PondTemp = this->PastBulkTemperature + Constant::SecInHour * 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 = FluidProperties::GetSpecificHeatGlycol(state,
     575      495804 :                                                              state.dataPlnt->PlantLoop(this->plantLoc.loopNum).FluidName,
     576             :                                                              max(this->InletTemp, 0.0),
     577      495804 :                                                              state.dataPlnt->PlantLoop(this->plantLoc.loopNum).FluidIndex,
     578             :                                                              RoutineName);
     579             :     // heat transfer with fluid - heat exchanger analogy.
     580             : 
     581             :     // convective flux
     582      495804 :     Real64 effectiveness = this->CalcEffectiveness(state, this->InletTemp, PondBulkTemp, this->MassFlowRate);
     583      495804 :     Real64 Qfluid = this->MassFlowRate * SpecHeat * effectiveness * (this->InletTemp - PondBulkTemp);
     584             : 
     585             :     // evaporation flux
     586             :     // get air properties
     587      495804 :     Real64 HumRatioAir = Psychrometrics::PsyWFnTdbTwbPb(state, OutDryBulb, OutWetBulb, state.dataEnvrn->OutBaroPress);
     588             : 
     589             :     // humidity ratio at pond surface/film temperature
     590      495804 :     Real64 HumRatioFilm = Psychrometrics::PsyWFnTdbTwbPb(state, PondBulkTemp, PondBulkTemp, state.dataEnvrn->OutBaroPress);
     591      495804 :     Real64 SpecHeatAir = Psychrometrics::PsyCpAirFnW(HumRatioAir);
     592      495804 :     Real64 LatentHeatAir = Psychrometrics::PsyHfgAirFnWTdb(HumRatioAir, OutDryBulb);
     593             : 
     594             :     // evaporative heat flux
     595      495804 :     Real64 FluxEvap = pow_2(PrandtlAir / SchmidtAir) / 3.0 * ConvCoef / SpecHeatAir * (HumRatioFilm - HumRatioAir) * LatentHeatAir;
     596             : 
     597             :     // ground heat transfer flux
     598      495804 :     Real64 Perimeter = 4.0 * std::sqrt(this->Area); // pond perimeter -- square assumption
     599             : 
     600             :     // ground heat transfer coefficient
     601      495804 :     Real64 UvalueGround = 0.999 * (this->GrndConductivity / this->Depth) + 1.37 * (this->GrndConductivity * Perimeter / this->Area);
     602             : 
     603             :     // ground heat transfer flux
     604      495804 :     Real64 FluxGround = UvalueGround * (PondBulkTemp - state.dataEnvrn->GroundTemp[(int)DataEnvironment::GroundTempType::Deep]);
     605             : 
     606      495804 :     CalcTotalFLux = Qfluid + this->Area * (FluxSolAbsorbed - FluxConvect - FluxLongwave - FluxEvap - FluxGround);
     607             : 
     608      495804 :     return CalcTotalFLux;
     609             : }
     610             : 
     611      495804 : Real64 PondGroundHeatExchangerData::CalcSolarFlux(EnergyPlusData &state) const
     612             : {
     613             : 
     614             :     // FUNCTION INFORMATION:
     615             :     //       AUTHOR         Simon Rees
     616             :     //       DATE WRITTEN   August 2002
     617             :     //       MODIFIED       na
     618             :     //       RE-ENGINEERED  na
     619             : 
     620             :     // PURPOSE OF THIS SUBROUTINE:
     621             :     // This is used to calculate the net solar flux absorbed by the pond.
     622             : 
     623             :     // METHODOLOGY EMPLOYED:
     624             :     // This is calculated from basic optical formula using the extinction
     625             :     // coefficient of the pond as the main parameter. This can be in a
     626             :     // wide range: 0.13 - 7.5 in the literature depending on algae, suspended
     627             :     // solids etc. ??
     628             : 
     629             :     // REFERENCES:
     630             :     // Duffie, J.A. and W.A. Beckman, 1991. Solar Engineering of Thermal
     631             :     //  Processes, 2 nd Edition. John Wiley and Sons.
     632             :     // Chiasson, A. Advances in Modeling of Ground-Source Heat Pump Systems.
     633             :     //   M.S. Thesis, Oklahoma State University, December 1999.
     634             :     // Chiasson, A.D., J.D. Spitler, S.J. Rees, M.D. Smith.  2000.  A Model For
     635             :     //   Simulating The Performance Of A Shallow Pond As A Supplemental Heat
     636             :     //   Rejecter With Closed-Loop Ground-Source Heat Pump Systems.
     637             :     //   ASHRAE Transactions.  106(2):107-121.
     638             : 
     639             :     Real64 CalcSolarFlux; // Function return variable
     640             : 
     641      495804 :     Real64 constexpr WaterRefIndex(1.33); // refractive index of water
     642      495804 :     Real64 constexpr AirRefIndex(1.0003); // refractive index of air
     643      495804 :     Real64 constexpr PondExtCoef(0.3);    // extinction coefficient of water
     644             : 
     645             :     // check for sun up.
     646      495804 :     if (!state.dataEnvrn->SunIsUp) {
     647      249836 :         CalcSolarFlux = 0.0;
     648      249836 :         return CalcSolarFlux;
     649             :     }
     650             : 
     651             :     // get the incidence and reflection angles
     652      245968 :     Real64 IncidAngle = std::acos(state.dataEnvrn->SOLCOS(3));
     653      245968 :     Real64 RefractAngle = std::asin(std::sin(IncidAngle) * AirRefIndex / WaterRefIndex);
     654             : 
     655             :     // absorbed component: Tau_a
     656      245968 :     Real64 Absorbtance = std::exp(-PondExtCoef * this->Depth / std::cos(RefractAngle));
     657             : 
     658             :     // parallel and perpendicular components
     659      245968 :     Real64 ParallelRad = pow_2(std::tan(RefractAngle - IncidAngle)) / pow_2(std::tan(RefractAngle + IncidAngle));
     660      245968 :     Real64 PerpendRad = pow_2(std::sin(RefractAngle - IncidAngle)) / pow_2(std::sin(RefractAngle + IncidAngle));
     661             : 
     662             :     // transmittance: Tau
     663      245968 :     Real64 Transmitance = 0.5 * Absorbtance * ((1.0 - ParallelRad) / (1.0 + ParallelRad) + (1.0 - PerpendRad) / (1.0 + PerpendRad));
     664             : 
     665             :     // reflectance: Tau_a - Tau
     666      245968 :     Real64 Reflectance = Absorbtance - Transmitance;
     667             : 
     668             :     // apply reflectance to beam and diffuse solar to find flux
     669      245968 :     CalcSolarFlux = (1.0 - Reflectance) * (state.dataEnvrn->SOLCOS(3) * state.dataEnvrn->BeamSolarRad + state.dataEnvrn->DifSolarRad);
     670             : 
     671      245968 :     return CalcSolarFlux;
     672             : }
     673             : 
     674      619755 : Real64 PondGroundHeatExchangerData::CalcEffectiveness(EnergyPlusData &state,
     675             :                                                       Real64 const InsideTemperature, // Temperature of fluid in pipe circuit, in C
     676             :                                                       Real64 const PondTemperature,   // Temperature of pond water (i.e. outside the pipe), in C
     677             :                                                       Real64 const massFlowRate       // Mass flow rate, in kg/s
     678             : )
     679             : {
     680             : 
     681             :     // FUNCTION INFORMATION:
     682             :     //       AUTHOR         Simon Rees
     683             :     //       DATE WRITTEN   August 2002
     684             :     //       MODIFIED       na
     685             :     //       RE-ENGINEERED  na
     686             : 
     687             :     // PURPOSE OF THIS SUBROUTINE:
     688             :     // This subroutine calculates the "heat exchanger" effectiveness.
     689             :     // This routine is adapted from that in the low temp radiant pond model.
     690             : 
     691             :     // METHODOLOGY EMPLOYED:
     692             :     // The heat transfer coefficient is calculated at the pipe and
     693             :     // consists of inside and outside convection coefficients and conduction
     694             :     // through the pipe. The other assumptions are that the tube inside
     695             :     // surface temperature is equal to the "source location temperature"
     696             :     // and that it is a CONSTANT throughout the pond. External convection is
     697             :     // natural mode using Churchill and Chu correlation. Inside convection
     698             :     // calculated using the Dittus-Boelter equation.
     699             : 
     700             :     // REFERENCES:
     701             :     // Incropera, F.P. and D.P. DeWitt, 1996. Introduction to Heat Transfer,
     702             :     //   3 rd Edition. John Wiley & Sons.
     703             :     // Churchill, S.W. and H.H.S. Chu. 1975. Correlating Equations for
     704             :     //   Laminar and Turbulent Free Convection from a Horizontal Cylinder.
     705             :     //   International Journal of Heat and Mass Transfer, 18: 1049-1053.
     706             :     // See also RadiantSystemLowTemp module.
     707             : 
     708             :     Real64 CalcEffectiveness; // Function return variable
     709             : 
     710      619755 :     Real64 constexpr MaxLaminarRe(2300.0); // Maximum Reynolds number for laminar flow
     711      619755 :     Real64 constexpr GravConst(9.81);      // gravitational constant - should be fixed!
     712             :     static constexpr std::string_view CalledFrom("PondGroundHeatExchanger:CalcEffectiveness");
     713             : 
     714             :     // evaluate properties at pipe fluid temperature for given pipe fluid
     715             : 
     716      619755 :     Real64 SpecificHeat = FluidProperties::GetSpecificHeatGlycol(state,
     717      619755 :                                                                  state.dataPlnt->PlantLoop(this->plantLoc.loopNum).FluidName,
     718             :                                                                  InsideTemperature,
     719      619755 :                                                                  state.dataPlnt->PlantLoop(this->plantLoc.loopNum).FluidIndex,
     720             :                                                                  CalledFrom);
     721      619755 :     Real64 Conductivity = FluidProperties::GetConductivityGlycol(state,
     722      619755 :                                                                  state.dataPlnt->PlantLoop(this->plantLoc.loopNum).FluidName,
     723             :                                                                  InsideTemperature,
     724      619755 :                                                                  state.dataPlnt->PlantLoop(this->plantLoc.loopNum).FluidIndex,
     725             :                                                                  CalledFrom);
     726      619755 :     Real64 Viscosity = FluidProperties::GetViscosityGlycol(state,
     727      619755 :                                                            state.dataPlnt->PlantLoop(this->plantLoc.loopNum).FluidName,
     728             :                                                            InsideTemperature,
     729      619755 :                                                            state.dataPlnt->PlantLoop(this->plantLoc.loopNum).FluidIndex,
     730             :                                                            CalledFrom);
     731             : 
     732             :     // Calculate the Reynold's number from RE=(4*Mdot)/(Pi*Mu*Diameter)
     733      619755 :     Real64 ReynoldsNum = 4.0 * massFlowRate / (Constant::Pi * Viscosity * this->TubeInDiameter * this->NumCircuits);
     734             : 
     735      619755 :     Real64 PrantlNum = Viscosity * SpecificHeat / Conductivity;
     736             : 
     737             :     Real64 NusseltNum; // Nusselt number (dimensionless)
     738             : 
     739             :     // Calculate the Nusselt number based on what flow regime one is in. h = (k)(Nu)/D
     740      619755 :     if (ReynoldsNum >= MaxLaminarRe) { // Turbulent flow --> use Dittus-Boelter equation
     741      500120 :         NusseltNum = 0.023 * std::pow(ReynoldsNum, 0.8) * std::pow(PrantlNum, 0.3);
     742             :     } else { // Laminar flow --> use constant surface temperature relation
     743      119635 :         NusseltNum = 3.66;
     744             :     }
     745             : 
     746             :     // inside convection resistance, from Nu
     747      619755 :     Real64 ConvCoefIn = Conductivity * NusseltNum / this->TubeInDiameter;
     748             : 
     749             :     // now find properties of pond water - always assume pond fluid is water
     750      619755 :     Real64 WaterSpecHeat = FluidProperties::GetSpecificHeatGlycol(state, fluidNameWater, max(PondTemperature, 0.0), this->WaterIndex, CalledFrom);
     751      619755 :     Real64 WaterConductivity = FluidProperties::GetConductivityGlycol(state, fluidNameWater, max(PondTemperature, 0.0), this->WaterIndex, CalledFrom);
     752      619755 :     Real64 WaterViscosity = FluidProperties::GetViscosityGlycol(state, fluidNameWater, max(PondTemperature, 0.0), this->WaterIndex, CalledFrom);
     753      619755 :     Real64 WaterDensity = FluidProperties::GetDensityGlycol(state, fluidNameWater, max(PondTemperature, 0.0), this->WaterIndex, CalledFrom);
     754             : 
     755             :     // derived properties for natural convection coefficient
     756             :     // expansion coef (Beta) = -1/Rho. dRho/dT
     757             :     // The following code includes some slight modifications from Simon's original code.
     758             :     // It guarantees that the delta T is 10C and also avoids the problems associated with
     759             :     // water hitting a maximum density at around 4C. (RKS)
     760             :     Real64 ExpansionCoef =
     761      619755 :         -(FluidProperties::GetDensityGlycol(state, fluidNameWater, max(PondTemperature, 10.0) + 5.0, this->WaterIndex, CalledFrom) -
     762      619755 :           FluidProperties::GetDensityGlycol(state, fluidNameWater, max(PondTemperature, 10.0) - 5.0, this->WaterIndex, CalledFrom)) /
     763      619755 :         (10.0 * WaterDensity);
     764             : 
     765      619755 :     Real64 ThermDiff = WaterConductivity / (WaterDensity * WaterSpecHeat);
     766      619755 :     PrantlNum = WaterViscosity * WaterSpecHeat / WaterConductivity;
     767             : 
     768      619755 :     Real64 RayleighNum = WaterDensity * GravConst * ExpansionCoef * std::abs(InsideTemperature - PondTemperature) * pow_3(TubeOutDiameter) /
     769      619755 :                          (WaterViscosity * ThermDiff);
     770             : 
     771             :     // Calculate the Nusselt number for natural convection at outside of pipe
     772      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))));
     773             : 
     774             :     // outside convection resistance, from Nu
     775      619755 :     Real64 ConvCoefOut = WaterConductivity * NusseltNum / this->TubeOutDiameter;
     776             : 
     777             :     // conduction resistance of pipe
     778      619755 :     Real64 PipeResistance = this->TubeInDiameter / this->TubeConductivity * std::log(this->TubeOutDiameter / this->TubeInDiameter);
     779             : 
     780             :     // total pipe thermal resistance - conduction and convection
     781      619755 :     Real64 TotalResistance = PipeResistance + 1.0 / ConvCoefIn + this->TubeInDiameter / (this->TubeOutDiameter * ConvCoefOut);
     782             : 
     783             :     // Calculate the NTU parameter
     784             :     // NTU = UA/[(Mdot*Cp)min] = A/[Rtot*(Mdot*Cp)min]
     785             :     // where: Rtot = Ri,convection + Rconduction + Ro,conveciton
     786             :     //        A = Pi*D*TubeLength
     787             : 
     788             :     Real64 NTU; // Number of transfer units, non-dimensional
     789             : 
     790      619755 :     if (massFlowRate == 0.0) {
     791       55955 :         CalcEffectiveness = 1.0;
     792             :     } else {
     793      563800 :         NTU = Constant::Pi * TubeInDiameter * this->CircuitLength * this->NumCircuits / (TotalResistance * massFlowRate * SpecificHeat);
     794             :         // Calculate effectiveness - formula for static fluid
     795      563800 :         CalcEffectiveness = (1.0 - std::exp(-NTU));
     796             :     }
     797             : 
     798             :     // Check for frozen pond
     799      619755 :     if (PondTemperature < 0.0) {
     800           0 :         ++this->ConsecutiveFrozen;
     801           0 :         if (this->FrozenErrIndex == 0) {
     802           0 :             ShowWarningMessage(state,
     803           0 :                                format("GroundHeatExchanger:Pond=\"{}\", is frozen; Pond model not valid. Calculated Pond Temperature=[{:.2R}] C",
     804           0 :                                       this->Name,
     805             :                                       PondTemperature));
     806           0 :             ShowContinueErrorTimeStamp(state, "");
     807             :         }
     808           0 :         ShowRecurringWarningErrorAtEnd(state,
     809           0 :                                        "GroundHeatExchanger:Pond=\"" + this->Name + "\", is frozen",
     810           0 :                                        this->FrozenErrIndex,
     811             :                                        PondTemperature,
     812             :                                        PondTemperature,
     813             :                                        _,
     814             :                                        "[C]",
     815             :                                        "[C]");
     816           0 :         if (this->ConsecutiveFrozen >= state.dataGlobal->NumOfTimeStepInHour * 30) {
     817           0 :             ShowFatalError(state,
     818           0 :                            format("GroundHeatExchanger:Pond=\"{}\" has been frozen for 30 consecutive hours.  Program terminates.", this->Name));
     819             :         }
     820             :     } else {
     821      619755 :         this->ConsecutiveFrozen = 0;
     822             :     }
     823             : 
     824      619755 :     return CalcEffectiveness;
     825             : }
     826             : 
     827      123951 : void PondGroundHeatExchangerData::UpdatePondGroundHeatExchanger(EnergyPlusData &state)
     828             : {
     829             : 
     830             :     // SUBROUTINE INFORMATION:
     831             :     //       AUTHOR         Simon Rees
     832             :     //       DATE WRITTEN   August 2002
     833             :     //       MODIFIED       na
     834             :     //       RE-ENGINEERED  na
     835             : 
     836             :     // PURPOSE OF THIS SUBROUTINE:
     837             :     // This subroutine does any updating that needs to be done for pond
     838             :     // ground heat exchangers.   This routine must also set the outlet water
     839             :     // conditions.
     840             : 
     841             :     static constexpr std::string_view RoutineName("PondGroundHeatExchanger:Update");
     842             : 
     843             :     // Calculate the water side outlet conditions and set the
     844             :     // appropriate conditions on the correct HVAC node.
     845      123951 :     Real64 CpFluid = FluidProperties::GetSpecificHeatGlycol(state,
     846      123951 :                                                             state.dataPlnt->PlantLoop(this->plantLoc.loopNum).FluidName,
     847             :                                                             this->InletTemp,
     848      123951 :                                                             state.dataPlnt->PlantLoop(this->plantLoc.loopNum).FluidIndex,
     849             :                                                             RoutineName);
     850             : 
     851      123951 :     PlantUtilities::SafeCopyPlantNode(state, InletNodeNum, OutletNodeNum);
     852             : 
     853             :     // update outlet temp
     854      123951 :     if ((CpFluid > 0.0) && (this->MassFlowRate > 0.0)) {
     855      112760 :         this->OutletTemp = this->InletTemp - this->HeatTransferRate / (this->MassFlowRate * CpFluid);
     856             :     } else {
     857       11191 :         this->OutletTemp = this->InletTemp;
     858             :     }
     859             : 
     860             :     // update node
     861      123951 :     state.dataLoopNodes->Node(this->OutletNodeNum).Temp = this->OutletTemp;
     862      123951 :     state.dataLoopNodes->Node(this->OutletNodeNum).MassFlowRate = this->MassFlowRate;
     863             : 
     864             :     // update heat transfer rate
     865             :     // compute pond heat transfer
     866      123951 :     Real64 effectiveness = this->CalcEffectiveness(state, this->InletTemp, this->PondTemp, this->MassFlowRate);
     867      123951 :     this->HeatTransferRate = this->MassFlowRate * CpFluid * effectiveness * (this->InletTemp - this->PondTemp);
     868      123951 :     this->Energy = this->HeatTransferRate * state.dataHVACGlobal->TimeStepSysSec;
     869             : 
     870             :     // keep track of the bulk temperature
     871      123951 :     this->BulkTemperature = this->PondTemp;
     872      123951 : }
     873      123966 : void PondGroundHeatExchangerData::oneTimeInit(EnergyPlusData &state)
     874             : {
     875      123966 :     Real64 constexpr DesignVelocity(0.5); // Hypothetical design max pipe velocity [m/s]
     876      123966 :     Real64 constexpr PondHeight(0.0);     // for now
     877             : 
     878      123966 :     static std::string const RoutineName("InitPondGroundHeatExchanger");
     879             : 
     880      123966 :     if (this->setupOutputVarsFlag) {
     881           3 :         this->setupOutputVars(state);
     882           3 :         this->setupOutputVarsFlag = false;
     883             :     }
     884             : 
     885      123966 :     if (this->OneTimeFlag || state.dataGlobal->WarmupFlag) {
     886             :         // initialize pond temps to mean of drybulb and ground temps.
     887      106490 :         this->BulkTemperature = this->PastBulkTemperature =
     888      106490 :             0.5 * (DataEnvironment::OutDryBulbTempAt(state, PondHeight) + state.dataEnvrn->GroundTemp[(int)DataEnvironment::GroundTempType::Deep]);
     889      106490 :         this->OneTimeFlag = false;
     890             :     }
     891             : 
     892             :     // Init more variables
     893      123966 :     if (this->MyFlag) {
     894             :         // Locate the hx on the plant loops for later usage
     895           3 :         bool errFlag = false;
     896           6 :         PlantUtilities::ScanPlantLoopsForObject(
     897           3 :             state, this->Name, DataPlant::PlantEquipmentType::GrndHtExchgPond, this->plantLoc, errFlag, _, _, _, _, _);
     898           3 :         if (errFlag) {
     899           0 :             ShowFatalError(state, "InitPondGroundHeatExchanger: Program terminated due to previous condition(s).");
     900             :         }
     901           6 :         Real64 rho = FluidProperties::GetDensityGlycol(state,
     902           3 :                                                        state.dataPlnt->PlantLoop(this->plantLoc.loopNum).FluidName,
     903             :                                                        DataPrecisionGlobals::constant_zero,
     904           3 :                                                        state.dataPlnt->PlantLoop(this->plantLoc.loopNum).FluidIndex,
     905             :                                                        RoutineName);
     906           6 :         Real64 Cp = FluidProperties::GetSpecificHeatGlycol(state,
     907           3 :                                                            state.dataPlnt->PlantLoop(this->plantLoc.loopNum).FluidName,
     908             :                                                            DataPrecisionGlobals::constant_zero,
     909           3 :                                                            state.dataPlnt->PlantLoop(this->plantLoc.loopNum).FluidIndex,
     910             :                                                            RoutineName);
     911           3 :         this->DesignMassFlowRate = Constant::Pi / 4.0 * pow_2(this->TubeInDiameter) * DesignVelocity * rho * this->NumCircuits;
     912           3 :         this->DesignCapacity = this->DesignMassFlowRate * Cp * 10.0; // assume 10C delta T?
     913           3 :         PlantUtilities::InitComponentNodes(state, 0.0, this->DesignMassFlowRate, this->InletNodeNum, this->OutletNodeNum);
     914           3 :         PlantUtilities::RegisterPlantCompDesignFlow(state, this->InletNodeNum, this->DesignMassFlowRate / rho);
     915             : 
     916           3 :         this->MyFlag = false;
     917             :     }
     918      123966 : }
     919             : 
     920             : } // namespace EnergyPlus::PondGroundHeatExchanger

Generated by: LCOV version 1.14