LCOV - code coverage report
Current view: top level - EnergyPlus - SurfaceGroundHeatExchanger.cc (source / functions) Hit Total Coverage
Test: lcov.output.filtered Lines: 427 534 80.0 %
Date: 2024-08-23 23:50:59 Functions: 16 17 94.1 %

          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/Construction.hh>
      58             : #include <EnergyPlus/ConvectionCoefficients.hh>
      59             : #include <EnergyPlus/Data/EnergyPlusData.hh>
      60             : #include <EnergyPlus/DataEnvironment.hh>
      61             : #include <EnergyPlus/DataHVACGlobals.hh>
      62             : #include <EnergyPlus/DataHeatBalance.hh>
      63             : #include <EnergyPlus/DataIPShortCuts.hh>
      64             : #include <EnergyPlus/DataLoopNode.hh>
      65             : #include <EnergyPlus/DataPrecisionGlobals.hh>
      66             : #include <EnergyPlus/FluidProperties.hh>
      67             : #include <EnergyPlus/General.hh>
      68             : #include <EnergyPlus/InputProcessing/InputProcessor.hh>
      69             : #include <EnergyPlus/Material.hh>
      70             : #include <EnergyPlus/NodeInputManager.hh>
      71             : #include <EnergyPlus/OutputProcessor.hh>
      72             : #include <EnergyPlus/Plant/DataPlant.hh>
      73             : #include <EnergyPlus/PlantUtilities.hh>
      74             : #include <EnergyPlus/SurfaceGroundHeatExchanger.hh>
      75             : #include <EnergyPlus/UtilityRoutines.hh>
      76             : 
      77             : namespace EnergyPlus {
      78             : 
      79             : namespace SurfaceGroundHeatExchanger {
      80             : 
      81             :     // Module containing the routines dealing with surface/panel ground heat exchangers
      82             : 
      83             :     // MODULE INFORMATION:
      84             :     //       AUTHOR         Simon Rees
      85             :     //       DATE WRITTEN   August 2002
      86             :     //       MODIFIED       Brent Griffith, Sept 2010, plant upgrades
      87             :     //       RE-ENGINEERED  na
      88             : 
      89             :     // PURPOSE OF THIS MODULE:
      90             :     // The purpose of this module is to simulate hydronic Surface Ground Heat
      91             :     // Exchangers. This includes pavement surfaces with embedded pipes for snow-
      92             :     // melting or heat rejection from hybrid ground source heat pump systems.
      93             :     // The heat exchanger may be gound coupled or not. In the latter case the
      94             :     // bottom surface is exposed to the wind but not solar gains.
      95             : 
      96             :     // METHODOLOGY EMPLOYED:
      97             :     // This model is based on the QTF formulation of heat transfer through
      98             :     // building elements with embedded heat sources/sinks. The model uses
      99             :     // a heat exchanger analogy to relate the inlet fluid temperature to the
     100             :     // net heat transfer rate and consequently outlet temperature. The model
     101             :     // is entirely passive i.e. it does not set any flow rates or incorporate
     102             :     // any controls. In order to deal with the non-linear boundary conditions
     103             :     // at the top surface due to the presence of ice/snow fluxes have to be
     104             :     // calculated by the QTF model and temperature calculated from the surface
     105             :     // heat balance. This requires some iteration.
     106             :     // Note: top surface variables correspond to 'outside' variables in standard
     107             :     // CTF/QTF definition. Bottom surface variables correspond to 'inside' variables.
     108             : 
     109             :     // REFERENCES:
     110             :     // Strand, R.K. 1995. "Heat Source Transfer Functions and Their Application to
     111             :     //   Low Temperature Radiant Heating Systems", Ph.D. dissertation, University
     112             :     //   of Illinois at Urbana-Champaign, Department of Mechanical and Industrial
     113             :     //   Engineering.
     114             :     // Seem, J.E. 1986. "Heat Transfer in Buildings", Ph.D. dissertation, University
     115             :     //   of Wisconsin-Madison.
     116             : 
     117             :     // OTHER NOTES: none
     118             : 
     119             :     // USE STATEMENTS:
     120             :     // Use statements for data only modules
     121             :     // Using/Aliasing
     122             :     using namespace DataLoopNode;
     123             : 
     124             :     // Use statements for access to subroutines in other modules
     125             : 
     126             :     // Data
     127             :     // MODULE PARAMETER DEFINITIONS
     128             :     Real64 constexpr SmallNum(1.0e-30);         // Very small number to avoid div0 errors
     129             :     Real64 constexpr StefBoltzmann(5.6697e-08); // Stefan-Boltzmann constant
     130             :     Real64 constexpr SurfaceHXHeight(0.0);      // Surface Height above ground -- used in height dependent calcs.
     131             : 
     132             :     int constexpr SurfCond_Ground(1);
     133             :     int constexpr SurfCond_Exposed(2);
     134             : 
     135           1 :     PlantComponent *SurfaceGroundHeatExchangerData::factory(EnergyPlusData &state,
     136             :                                                             [[maybe_unused]] DataPlant::PlantEquipmentType objectType,
     137             :                                                             std::string const objectName)
     138             :     {
     139           1 :         if (state.dataSurfaceGroundHeatExchangers->GetInputFlag) {
     140           1 :             GetSurfaceGroundHeatExchanger(state);
     141           1 :             state.dataSurfaceGroundHeatExchangers->GetInputFlag = false;
     142             :         }
     143             :         // Now look for this particular pipe in the list
     144           1 :         for (auto &ghx : state.dataSurfaceGroundHeatExchangers->SurfaceGHE) {
     145           1 :             if (ghx.Name == objectName) {
     146           1 :                 return &ghx;
     147             :             }
     148             :         }
     149             :         // If we didn't find it, fatal
     150           0 :         ShowFatalError(state, format("Surface Ground Heat Exchanger: Error getting inputs for pipe named: {}", objectName));
     151             :         // Shut up the compiler
     152           0 :         return nullptr;
     153             :     }
     154             : 
     155       14549 :     void SurfaceGroundHeatExchangerData::simulate(EnergyPlusData &state,
     156             :                                                   [[maybe_unused]] const PlantLocation &calledFromLocation,
     157             :                                                   bool const FirstHVACIteration,
     158             :                                                   [[maybe_unused]] Real64 &CurLoad,
     159             :                                                   [[maybe_unused]] bool const RunFlag)
     160             :     {
     161       14549 :         this->InitSurfaceGroundHeatExchanger(state);
     162       14549 :         this->CalcSurfaceGroundHeatExchanger(state, FirstHVACIteration);
     163       14549 :         this->UpdateSurfaceGroundHeatExchngr(state);
     164       14549 :         this->ReportSurfaceGroundHeatExchngr(state);
     165       14549 :     }
     166             : 
     167           1 :     void GetSurfaceGroundHeatExchanger(EnergyPlusData &state)
     168             :     {
     169             : 
     170             :         // SUBROUTINE INFORMATION:
     171             :         //       AUTHOR         Simon Rees
     172             :         //       DATE WRITTEN   August 2002
     173             :         //       MODIFIED       na
     174             :         //       RE-ENGINEERED  na
     175             : 
     176             :         // PURPOSE OF THIS SUBROUTINE:
     177             :         // This subroutine reads the input for hydronic Surface Ground Heat Exchangers
     178             :         // from the user input file.  This will contain all of the information
     179             :         // needed to define and simulate the surface.
     180             : 
     181             :         // METHODOLOGY EMPLOYED:
     182             :         // Standard EnergyPlus methodology.
     183             : 
     184             :         // Using/Aliasing
     185             :         using BranchNodeConnections::TestCompSet;
     186             :         using FluidProperties::CheckFluidPropertyName;
     187             : 
     188             :         using NodeInputManager::GetOnlySingleNode;
     189             :         using namespace DataLoopNode;
     190             : 
     191             :         // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
     192             : 
     193           1 :         bool ErrorsFound(false); // Set to true if errors in input,
     194             :         // fatal at end of routine
     195             :         int IOStatus;   // Used in GetObjectItem
     196             :         int Item;       // Item to be "gotten"
     197             :         int NumAlphas;  // Number of Alphas for each GetObjectItem call
     198             :         int NumNumbers; // Number of Numbers for each GetObjectItem call
     199           1 :         auto &cCurrentModuleObject = state.dataIPShortCut->cCurrentModuleObject;
     200             :         // Initializations and allocations
     201           1 :         cCurrentModuleObject = "GroundHeatExchanger:Surface";
     202           1 :         int NumOfSurfaceGHEs = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, cCurrentModuleObject);
     203             :         // allocate data structures
     204           1 :         if (allocated(state.dataSurfaceGroundHeatExchangers->SurfaceGHE)) state.dataSurfaceGroundHeatExchangers->SurfaceGHE.deallocate();
     205             : 
     206           1 :         state.dataSurfaceGroundHeatExchangers->SurfaceGHE.allocate(NumOfSurfaceGHEs);
     207           1 :         state.dataSurfaceGroundHeatExchangers->CheckEquipName.dimension(NumOfSurfaceGHEs, true);
     208             : 
     209             :         // initialize data structures
     210             :         // surface data
     211             :         // Obtain all of the user data related to the surfaces...
     212           2 :         for (Item = 1; Item <= NumOfSurfaceGHEs; ++Item) {
     213             : 
     214             :             // get the input data
     215           3 :             state.dataInputProcessing->inputProcessor->getObjectItem(state,
     216             :                                                                      cCurrentModuleObject,
     217             :                                                                      Item,
     218           1 :                                                                      state.dataIPShortCut->cAlphaArgs,
     219             :                                                                      NumAlphas,
     220           1 :                                                                      state.dataIPShortCut->rNumericArgs,
     221             :                                                                      NumNumbers,
     222             :                                                                      IOStatus,
     223             :                                                                      _,
     224             :                                                                      _,
     225           1 :                                                                      state.dataIPShortCut->cAlphaFieldNames,
     226           1 :                                                                      state.dataIPShortCut->cNumericFieldNames);
     227             : 
     228             :             // General user input data
     229           1 :             state.dataSurfaceGroundHeatExchangers->SurfaceGHE(Item).Name = state.dataIPShortCut->cAlphaArgs(1);
     230           1 :             state.dataSurfaceGroundHeatExchangers->SurfaceGHE(Item).ConstructionName = state.dataIPShortCut->cAlphaArgs(2);
     231           1 :             state.dataSurfaceGroundHeatExchangers->SurfaceGHE(Item).ConstructionNum =
     232           1 :                 Util::FindItemInList(state.dataIPShortCut->cAlphaArgs(2), state.dataConstruction->Construct);
     233             : 
     234           1 :             if (state.dataSurfaceGroundHeatExchangers->SurfaceGHE(Item).ConstructionNum == 0) {
     235           0 :                 ShowSevereError(state, format("Invalid {}={}", state.dataIPShortCut->cAlphaFieldNames(2), state.dataIPShortCut->cAlphaArgs(2)));
     236           0 :                 ShowContinueError(state, format("Entered in {}={}", cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)));
     237           0 :                 ErrorsFound = true;
     238             :             }
     239             : 
     240             :             // Error checking for surfaces, zones, and construction information
     241           1 :             if (!state.dataConstruction->Construct(state.dataSurfaceGroundHeatExchangers->SurfaceGHE(Item).ConstructionNum).SourceSinkPresent) {
     242           0 :                 ShowSevereError(state, format("Invalid {}={}", state.dataIPShortCut->cAlphaFieldNames(2), state.dataIPShortCut->cAlphaArgs(2)));
     243           0 :                 ShowContinueError(state, format("Entered in {}={}", cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)));
     244           0 :                 ShowContinueError(
     245             :                     state, "Construction must have internal source/sink and be referenced by a ConstructionProperty:InternalHeatSource object");
     246           0 :                 ErrorsFound = true;
     247             :             }
     248             : 
     249             :             // get inlet node data
     250           1 :             state.dataSurfaceGroundHeatExchangers->SurfaceGHE(Item).InletNode = state.dataIPShortCut->cAlphaArgs(3);
     251           1 :             state.dataSurfaceGroundHeatExchangers->SurfaceGHE(Item).InletNodeNum =
     252           2 :                 GetOnlySingleNode(state,
     253           1 :                                   state.dataIPShortCut->cAlphaArgs(3),
     254             :                                   ErrorsFound,
     255             :                                   DataLoopNode::ConnectionObjectType::GroundHeatExchangerSurface,
     256           1 :                                   state.dataIPShortCut->cAlphaArgs(1),
     257             :                                   DataLoopNode::NodeFluidType::Water,
     258             :                                   DataLoopNode::ConnectionType::Inlet,
     259             :                                   NodeInputManager::CompFluidStream::Primary,
     260             :                                   ObjectIsNotParent);
     261           1 :             if (state.dataSurfaceGroundHeatExchangers->SurfaceGHE(Item).InletNodeNum == 0) {
     262           0 :                 ShowSevereError(state, format("Invalid {}={}", state.dataIPShortCut->cAlphaFieldNames(3), state.dataIPShortCut->cAlphaArgs(3)));
     263           0 :                 ShowContinueError(state, format("Entered in {}={}", cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)));
     264           0 :                 ErrorsFound = true;
     265             :             }
     266             : 
     267             :             // get outlet node data
     268           1 :             state.dataSurfaceGroundHeatExchangers->SurfaceGHE(Item).OutletNode = state.dataIPShortCut->cAlphaArgs(4);
     269           1 :             state.dataSurfaceGroundHeatExchangers->SurfaceGHE(Item).OutletNodeNum =
     270           2 :                 GetOnlySingleNode(state,
     271           1 :                                   state.dataIPShortCut->cAlphaArgs(4),
     272             :                                   ErrorsFound,
     273             :                                   DataLoopNode::ConnectionObjectType::GroundHeatExchangerSurface,
     274           1 :                                   state.dataIPShortCut->cAlphaArgs(1),
     275             :                                   DataLoopNode::NodeFluidType::Water,
     276             :                                   DataLoopNode::ConnectionType::Outlet,
     277             :                                   NodeInputManager::CompFluidStream::Primary,
     278             :                                   ObjectIsNotParent);
     279           1 :             if (state.dataSurfaceGroundHeatExchangers->SurfaceGHE(Item).OutletNodeNum == 0) {
     280           0 :                 ShowSevereError(state, format("Invalid {}={}", state.dataIPShortCut->cAlphaFieldNames(4), state.dataIPShortCut->cAlphaArgs(4)));
     281           0 :                 ShowContinueError(state, format("Entered in {}={}", cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)));
     282           0 :                 ErrorsFound = true;
     283             :             }
     284             : 
     285           2 :             TestCompSet(state,
     286             :                         cCurrentModuleObject,
     287           1 :                         state.dataIPShortCut->cAlphaArgs(1),
     288           1 :                         state.dataIPShortCut->cAlphaArgs(3),
     289           1 :                         state.dataIPShortCut->cAlphaArgs(4),
     290             :                         "Condenser Water Nodes");
     291             : 
     292             :             // tube data
     293           1 :             state.dataSurfaceGroundHeatExchangers->SurfaceGHE(Item).TubeDiameter = state.dataIPShortCut->rNumericArgs(1);
     294           1 :             state.dataSurfaceGroundHeatExchangers->SurfaceGHE(Item).TubeCircuits = state.dataIPShortCut->rNumericArgs(2);
     295           1 :             state.dataSurfaceGroundHeatExchangers->SurfaceGHE(Item).TubeSpacing = state.dataIPShortCut->rNumericArgs(3);
     296             : 
     297           1 :             if (state.dataIPShortCut->rNumericArgs(2) == 0) {
     298           0 :                 ShowSevereError(state,
     299           0 :                                 format("Invalid {}={:.2R}", state.dataIPShortCut->cNumericFieldNames(2), state.dataIPShortCut->rNumericArgs(2)));
     300           0 :                 ShowContinueError(state, format("Entered in {}={}", cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)));
     301           0 :                 ShowContinueError(state, "Value must be greater than 0.0");
     302           0 :                 ErrorsFound = true;
     303             :             }
     304           1 :             if (state.dataIPShortCut->rNumericArgs(3) == 0.0) {
     305           0 :                 ShowSevereError(state,
     306           0 :                                 format("Invalid {}={:.2R}", state.dataIPShortCut->cNumericFieldNames(3), state.dataIPShortCut->rNumericArgs(3)));
     307           0 :                 ShowContinueError(state, format("Entered in {}={}", cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)));
     308           0 :                 ShowContinueError(state, "Value must be greater than 0.0");
     309           0 :                 ErrorsFound = true;
     310             :             }
     311             : 
     312             :             // surface geometry data
     313           1 :             state.dataSurfaceGroundHeatExchangers->SurfaceGHE(Item).SurfaceLength = state.dataIPShortCut->rNumericArgs(4);
     314           1 :             state.dataSurfaceGroundHeatExchangers->SurfaceGHE(Item).SurfaceWidth = state.dataIPShortCut->rNumericArgs(5);
     315           1 :             if (state.dataIPShortCut->rNumericArgs(4) <= 0.0) {
     316           0 :                 ShowSevereError(state,
     317           0 :                                 format("Invalid {}={:.2R}", state.dataIPShortCut->cNumericFieldNames(4), state.dataIPShortCut->rNumericArgs(4)));
     318           0 :                 ShowContinueError(state, format("Entered in {}={}", cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)));
     319           0 :                 ShowContinueError(state, "Value must be greater than 0.0");
     320           0 :                 ErrorsFound = true;
     321             :             }
     322           1 :             if (state.dataIPShortCut->rNumericArgs(5) <= 0.0) {
     323           0 :                 ShowSevereError(state,
     324           0 :                                 format("Invalid {}={:.2R}", state.dataIPShortCut->cNumericFieldNames(5), state.dataIPShortCut->rNumericArgs(5)));
     325           0 :                 ShowContinueError(state, format("Entered in {}={}", cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)));
     326           0 :                 ShowContinueError(state, "Value must be greater than 0.0");
     327           0 :                 ErrorsFound = true;
     328             :             }
     329             : 
     330             :             // get lower b.c. type
     331           1 :             if (Util::SameString(state.dataIPShortCut->cAlphaArgs(5), "GROUND")) {
     332           1 :                 state.dataSurfaceGroundHeatExchangers->SurfaceGHE(Item).LowerSurfCond = SurfCond_Ground;
     333           0 :             } else if (Util::SameString(state.dataIPShortCut->cAlphaArgs(5), "EXPOSED")) {
     334           0 :                 state.dataSurfaceGroundHeatExchangers->SurfaceGHE(Item).LowerSurfCond = SurfCond_Exposed;
     335             :             } else {
     336           0 :                 ShowSevereError(state, format("Invalid {}={}", state.dataIPShortCut->cAlphaFieldNames(5), state.dataIPShortCut->cAlphaArgs(5)));
     337           0 :                 ShowContinueError(state, format("Entered in {}={}", cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)));
     338           0 :                 ShowContinueError(state, "Only \"Ground\" or \"Exposed\" is allowed.");
     339           0 :                 ErrorsFound = true;
     340             :             }
     341             : 
     342             :         } // end of input loop
     343             : 
     344             :         // final error check
     345           1 :         if (ErrorsFound) {
     346           0 :             ShowFatalError(state, format("Errors found in processing input for {}", cCurrentModuleObject));
     347             :         }
     348             : 
     349             :         // Set up the output variables
     350           2 :         for (Item = 1; Item <= NumOfSurfaceGHEs; ++Item) {
     351           2 :             SetupOutputVariable(state,
     352             :                                 "Ground Heat Exchanger Heat Transfer Rate",
     353             :                                 Constant::Units::W,
     354           1 :                                 state.dataSurfaceGroundHeatExchangers->SurfaceGHE(Item).HeatTransferRate,
     355             :                                 OutputProcessor::TimeStepType::System,
     356             :                                 OutputProcessor::StoreType::Average,
     357           1 :                                 state.dataSurfaceGroundHeatExchangers->SurfaceGHE(Item).Name);
     358           2 :             SetupOutputVariable(state,
     359             :                                 "Ground Heat Exchanger Surface Heat Transfer Rate",
     360             :                                 Constant::Units::W,
     361           1 :                                 state.dataSurfaceGroundHeatExchangers->SurfaceGHE(Item).SurfHeatTransferRate,
     362             :                                 OutputProcessor::TimeStepType::System,
     363             :                                 OutputProcessor::StoreType::Average,
     364           1 :                                 state.dataSurfaceGroundHeatExchangers->SurfaceGHE(Item).Name);
     365           2 :             SetupOutputVariable(state,
     366             :                                 "Ground Heat Exchanger Heat Transfer Energy",
     367             :                                 Constant::Units::J,
     368           1 :                                 state.dataSurfaceGroundHeatExchangers->SurfaceGHE(Item).Energy,
     369             :                                 OutputProcessor::TimeStepType::System,
     370             :                                 OutputProcessor::StoreType::Sum,
     371           1 :                                 state.dataSurfaceGroundHeatExchangers->SurfaceGHE(Item).Name);
     372           2 :             SetupOutputVariable(state,
     373             :                                 "Ground Heat Exchanger Mass Flow Rate",
     374             :                                 Constant::Units::kg_s,
     375           1 :                                 state.dataSurfaceGroundHeatExchangers->SurfaceGHE(Item).MassFlowRate,
     376             :                                 OutputProcessor::TimeStepType::System,
     377             :                                 OutputProcessor::StoreType::Average,
     378           1 :                                 state.dataSurfaceGroundHeatExchangers->SurfaceGHE(Item).Name);
     379           2 :             SetupOutputVariable(state,
     380             :                                 "Ground Heat Exchanger Inlet Temperature",
     381             :                                 Constant::Units::C,
     382           1 :                                 state.dataSurfaceGroundHeatExchangers->SurfaceGHE(Item).InletTemp,
     383             :                                 OutputProcessor::TimeStepType::System,
     384             :                                 OutputProcessor::StoreType::Average,
     385           1 :                                 state.dataSurfaceGroundHeatExchangers->SurfaceGHE(Item).Name);
     386           2 :             SetupOutputVariable(state,
     387             :                                 "Ground Heat Exchanger Outlet Temperature",
     388             :                                 Constant::Units::C,
     389           1 :                                 state.dataSurfaceGroundHeatExchangers->SurfaceGHE(Item).OutletTemp,
     390             :                                 OutputProcessor::TimeStepType::System,
     391             :                                 OutputProcessor::StoreType::Average,
     392           1 :                                 state.dataSurfaceGroundHeatExchangers->SurfaceGHE(Item).Name);
     393           2 :             SetupOutputVariable(state,
     394             :                                 "Ground Heat Exchanger Top Surface Temperature",
     395             :                                 Constant::Units::C,
     396           1 :                                 state.dataSurfaceGroundHeatExchangers->SurfaceGHE(Item).TopSurfaceTemp,
     397             :                                 OutputProcessor::TimeStepType::System,
     398             :                                 OutputProcessor::StoreType::Average,
     399           1 :                                 state.dataSurfaceGroundHeatExchangers->SurfaceGHE(Item).Name);
     400           2 :             SetupOutputVariable(state,
     401             :                                 "Ground Heat Exchanger Bottom Surface Temperature",
     402             :                                 Constant::Units::C,
     403           1 :                                 state.dataSurfaceGroundHeatExchangers->SurfaceGHE(Item).BtmSurfaceTemp,
     404             :                                 OutputProcessor::TimeStepType::System,
     405             :                                 OutputProcessor::StoreType::Average,
     406           1 :                                 state.dataSurfaceGroundHeatExchangers->SurfaceGHE(Item).Name);
     407           2 :             SetupOutputVariable(state,
     408             :                                 "Ground Heat Exchanger Top Surface Heat Transfer Energy per Area",
     409             :                                 Constant::Units::J_m2,
     410           1 :                                 state.dataSurfaceGroundHeatExchangers->SurfaceGHE(Item).TopSurfaceFlux,
     411             :                                 OutputProcessor::TimeStepType::System,
     412             :                                 OutputProcessor::StoreType::Average,
     413           1 :                                 state.dataSurfaceGroundHeatExchangers->SurfaceGHE(Item).Name);
     414           2 :             SetupOutputVariable(state,
     415             :                                 "Ground Heat Exchanger Bottom Surface Heat Transfer Energy per Area",
     416             :                                 Constant::Units::J_m2,
     417           1 :                                 state.dataSurfaceGroundHeatExchangers->SurfaceGHE(Item).BtmSurfaceFlux,
     418             :                                 OutputProcessor::TimeStepType::System,
     419             :                                 OutputProcessor::StoreType::Average,
     420           1 :                                 state.dataSurfaceGroundHeatExchangers->SurfaceGHE(Item).Name);
     421           2 :             SetupOutputVariable(state,
     422             :                                 "Ground Heat Exchanger Surface Heat Transfer Energy",
     423             :                                 Constant::Units::J,
     424           1 :                                 state.dataSurfaceGroundHeatExchangers->SurfaceGHE(Item).SurfEnergy,
     425             :                                 OutputProcessor::TimeStepType::System,
     426             :                                 OutputProcessor::StoreType::Sum,
     427           1 :                                 state.dataSurfaceGroundHeatExchangers->SurfaceGHE(Item).Name);
     428           2 :             SetupOutputVariable(state,
     429             :                                 "Ground Heat Exchanger Source Temperature",
     430             :                                 Constant::Units::C,
     431           1 :                                 state.dataSurfaceGroundHeatExchangers->SurfaceGHE(Item).SourceTemp,
     432             :                                 OutputProcessor::TimeStepType::System,
     433             :                                 OutputProcessor::StoreType::Average,
     434           1 :                                 state.dataSurfaceGroundHeatExchangers->SurfaceGHE(Item).Name);
     435             :         }
     436             : 
     437           1 :         if (state.dataSurfaceGroundHeatExchangers->NoSurfaceGroundTempObjWarning) {
     438           1 :             if (!state.dataEnvrn->GroundTempInputs[(int)DataEnvironment::GroundTempType::Shallow]) {
     439           0 :                 ShowWarningError(state, "GetSurfaceGroundHeatExchanger: No \"Site:GroundTemperature:Shallow\" were input.");
     440           0 :                 ShowContinueError(state,
     441           0 :                                   format("Defaults, constant throughout the year of ({:.1R}) will be used.",
     442           0 :                                          state.dataEnvrn->GroundTemp[(int)DataEnvironment::GroundTempType::Shallow]));
     443             :             }
     444           1 :             state.dataSurfaceGroundHeatExchangers->NoSurfaceGroundTempObjWarning = false;
     445             :         }
     446           1 :     }
     447             : 
     448       14549 :     void SurfaceGroundHeatExchangerData::InitSurfaceGroundHeatExchanger(EnergyPlusData &state)
     449             :     {
     450             : 
     451             :         // SUBROUTINE INFORMATION:
     452             :         //       AUTHOR         Simon Rees
     453             :         //       DATE WRITTEN   August 2002
     454             :         //       MODIFIED       na
     455             :         //       RE-ENGINEERED  na
     456             : 
     457             :         // PURPOSE OF THIS SUBROUTINE:
     458             :         // This subroutine Resets the elements of the data structure as necessary
     459             :         // at the first HVAC iteration of each time step. The weather and QTF data
     460             :         // is initialized once only.
     461             : 
     462             :         // METHODOLOGY EMPLOYED:
     463             :         // Check flags and update data structure
     464             : 
     465             :         // Using/Aliasing
     466             :         using namespace DataEnvironment;
     467             :         using PlantUtilities::RegulateCondenserCompFlowReqOp;
     468             :         using PlantUtilities::SetComponentFlowRate;
     469             : 
     470             :         // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
     471             : 
     472             :         Real64 DesignFlow; // Hypothetical design flow rate
     473             :         int Cons;          // construction counter
     474             :         int LayerNum;      // material layer number for bottom
     475             :         Real64 OutDryBulb; // Height Dependent dry bulb.
     476             : 
     477             :         // get QTF data - only once
     478       14549 :         if (this->InitQTF) {
     479           9 :             for (Cons = 1; Cons <= state.dataHeatBal->TotConstructs; ++Cons) {
     480           8 :                 if (Util::SameString(state.dataConstruction->Construct(Cons).Name, this->ConstructionName)) {
     481             :                     // some error checking ??
     482             :                     // CTF stuff
     483           1 :                     LayerNum = state.dataConstruction->Construct(Cons).TotLayers;
     484           1 :                     this->NumCTFTerms = state.dataConstruction->Construct(Cons).NumCTFTerms;
     485           1 :                     this->CTFin = state.dataConstruction->Construct(Cons).CTFInside;   // Z coefficents
     486           1 :                     this->CTFout = state.dataConstruction->Construct(Cons).CTFOutside; // X coefficents
     487           1 :                     this->CTFcross = state.dataConstruction->Construct(Cons).CTFCross; // Y coefficents
     488          19 :                     for (size_t i = 1; i < state.dataConstruction->Construct(Cons).CTFFlux.size(); i++) {
     489          18 :                         this->CTFflux[i] = state.dataConstruction->Construct(Cons).CTFFlux[i]; // F & f coefficents
     490             :                     }
     491             :                     // QTF stuff
     492           1 :                     this->CTFSourceIn = state.dataConstruction->Construct(Cons).CTFSourceIn;     // Wi coefficents
     493           1 :                     this->CTFSourceOut = state.dataConstruction->Construct(Cons).CTFSourceOut;   // Wo coefficents
     494           1 :                     this->CTFTSourceOut = state.dataConstruction->Construct(Cons).CTFTSourceOut; // y coefficents
     495           1 :                     this->CTFTSourceIn = state.dataConstruction->Construct(Cons).CTFTSourceIn;   // x coefficents
     496           1 :                     this->CTFTSourceQ = state.dataConstruction->Construct(Cons).CTFTSourceQ;     // w coefficents
     497           1 :                     this->ConstructionNum = Cons;
     498             :                     // surface properties
     499           1 :                     auto const *thisMaterialLayer = dynamic_cast<Material::MaterialChild *>(
     500           1 :                         state.dataMaterial->Material(state.dataConstruction->Construct(Cons).LayerPoint(LayerNum)));
     501           1 :                     assert(thisMaterialLayer != nullptr);
     502           1 :                     this->BtmRoughness = thisMaterialLayer->Roughness;
     503           1 :                     this->TopThermAbs = thisMaterialLayer->AbsorpThermal;
     504             :                     auto const *thisMaterial1 =
     505           1 :                         dynamic_cast<Material::MaterialChild *>(state.dataMaterial->Material(state.dataConstruction->Construct(Cons).LayerPoint(1)));
     506           1 :                     assert(thisMaterial1 != nullptr);
     507           1 :                     this->TopRoughness = thisMaterial1->Roughness;
     508           1 :                     this->TopThermAbs = thisMaterial1->AbsorpThermal;
     509           1 :                     this->TopSolarAbs = thisMaterial1->AbsorpSolar;
     510             :                 }
     511             :             }
     512             :             // set one-time flag
     513           1 :             this->InitQTF = false;
     514             :         }
     515             : 
     516       14549 :         if (this->MyEnvrnFlag && state.dataGlobal->BeginEnvrnFlag) {
     517           6 :             OutDryBulb = OutDryBulbTempAt(state, SurfaceHXHeight);
     518           6 :             this->CTFflux[0] = 0.0;
     519           6 :             this->TsrcHistory.fill(OutDryBulb);
     520           6 :             this->TbtmHistory.fill(OutDryBulb);
     521           6 :             this->TtopHistory.fill(OutDryBulb);
     522           6 :             this->TsrcHistory.fill(OutDryBulb);
     523           6 :             this->QbtmHistory.fill(0.0);
     524           6 :             this->QtopHistory.fill(0.0);
     525           6 :             this->QsrcHistory.fill(0.0);
     526           6 :             this->TsrcConstCoef = 0.0;
     527           6 :             this->TsrcVarCoef = 0.0;
     528           6 :             this->QbtmConstCoef = 0.0;
     529           6 :             this->QbtmVarCoef = 0.0;
     530           6 :             this->QtopConstCoef = 0.0;
     531           6 :             this->QtopVarCoef = 0.0;
     532           6 :             this->QSrc = 0.0;
     533           6 :             this->QSrcAvg = 0.0;
     534           6 :             this->LastQSrc = 0.0;
     535           6 :             this->LastSysTimeElapsed = 0.0;
     536           6 :             this->LastTimeStepSys = 0.0;
     537             :             // initialize past weather variables
     538           6 :             state.dataSurfaceGroundHeatExchangers->PastBeamSolarRad = state.dataEnvrn->BeamSolarRad;
     539           6 :             state.dataSurfaceGroundHeatExchangers->PastSolarDirCosVert = state.dataEnvrn->SOLCOS(3);
     540           6 :             state.dataSurfaceGroundHeatExchangers->PastDifSolarRad = state.dataEnvrn->DifSolarRad;
     541           6 :             state.dataSurfaceGroundHeatExchangers->PastGroundTemp = state.dataEnvrn->GroundTemp[(int)DataEnvironment::GroundTempType::Shallow];
     542           6 :             state.dataSurfaceGroundHeatExchangers->PastIsRain = state.dataEnvrn->IsRain;
     543           6 :             state.dataSurfaceGroundHeatExchangers->PastIsSnow = state.dataEnvrn->IsSnow;
     544           6 :             state.dataSurfaceGroundHeatExchangers->PastOutDryBulbTemp = OutDryBulbTempAt(state, SurfaceHXHeight);
     545           6 :             state.dataSurfaceGroundHeatExchangers->PastOutWetBulbTemp = OutWetBulbTempAt(state, SurfaceHXHeight);
     546           6 :             state.dataSurfaceGroundHeatExchangers->PastSkyTemp = state.dataEnvrn->SkyTemp;
     547           6 :             state.dataSurfaceGroundHeatExchangers->PastWindSpeed = DataEnvironment::WindSpeedAt(state, SurfaceHXHeight);
     548           6 :             this->MyEnvrnFlag = false;
     549             :         }
     550             : 
     551       14549 :         if (!state.dataGlobal->BeginEnvrnFlag) this->MyEnvrnFlag = true;
     552             : 
     553             :         // always initialize - module variables
     554       14549 :         this->SurfaceArea = this->SurfaceLength * this->SurfaceWidth;
     555             : 
     556             :         // If loop operation is controlled by an environmental variable (DBtemp, WBtemp, etc)
     557             :         // then shut branch down when equipment is not scheduled to run.
     558       14549 :         DesignFlow = RegulateCondenserCompFlowReqOp(state, this->plantLoc, this->DesignMassFlowRate);
     559             : 
     560       14549 :         SetComponentFlowRate(state, DesignFlow, this->InletNodeNum, this->OutletNodeNum, this->plantLoc);
     561             : 
     562             :         // get the current flow rate - module variable
     563       14549 :         state.dataSurfaceGroundHeatExchangers->FlowRate = state.dataLoopNodes->Node(this->InletNodeNum).MassFlowRate;
     564       14549 :     }
     565             : 
     566       14549 :     void SurfaceGroundHeatExchangerData::CalcSurfaceGroundHeatExchanger(
     567             :         EnergyPlusData &state, bool const FirstHVACIteration // TRUE if 1st HVAC simulation of system timestep
     568             :     )
     569             :     {
     570             : 
     571             :         //       AUTHOR         Simon Rees
     572             :         //       DATE WRITTEN   August 2002
     573             :         //       MODIFIED       na
     574             :         //       RE-ENGINEERED  na
     575             : 
     576             :         // PURPOSE OF THIS SUBROUTINE:
     577             :         // This subroutine does all of the stuff that is necessary to simulate
     578             :         // a surface ground heat exchanger.  Calls are made to appropriate subroutines
     579             :         // either in this module or outside of it.
     580             : 
     581             :         // METHODOLOGY EMPLOYED:
     582             :         // To update temperature and flux histories it is necessary to make a surface
     583             :         // flux/temperature calculation at the begining of each zone time step using the
     584             :         // weather data from the previous step, and using the average source flux.
     585             :         // Once this has been done a new source flux, and current surface temperatures,
     586             :         // are calculated using the current weather data. These surface temperatures and
     587             :         // fluxes are used for the rest of the system time steps. During subsequent system
     588             :         // time steps only the source flux is updated.
     589             : 
     590             :         // Surface fluxes are calculated from the QTF equations using assumed surface
     591             :         // temperatures. Surface fluxes are then dependant only on source flux. Constant
     592             :         // and terms and terms that multiply the source flux from the QTF equations, are
     593             :         // grouped together for convenience. These are calculated in "CalcBottomFluxCoefficents"
     594             :         // etc. It is necessary to iterate on these equations, updating the current surface
     595             :         // temperatures at each step.
     596             : 
     597             :         // REFERENCES:
     598             :         // See 'LowTempRadiantSystem' module
     599             :         // IBLAST-QTF research program, completed in January 1995 (unreleased)
     600             :         // Strand, R.K. 1995. "Heat Source Transfer Functions and Their Application to
     601             :         //   Low Temperature Radiant Heating Systems", Ph.D. dissertation, University
     602             :         //   of Illinois at Urbana-Champaign, Department of Mechanical and Industrial
     603             :         //   Engineering.
     604             :         // Seem, J.E. 1986. "Heat Transfer in Buildings", Ph.D. dissertation, University
     605             :         //   of Wisconsin-Madison.
     606             : 
     607             :         // Using/Aliasing
     608             :         using namespace DataEnvironment;
     609             : 
     610       14549 :         Real64 constexpr SurfFluxTol(0.001); // tolerance on the surface fluxes
     611       14549 :         Real64 constexpr SrcFluxTol(0.001);  // tolerance on the source flux
     612       14549 :         Real64 constexpr RelaxT(0.1);        // temperature relaxation factor
     613       14549 :         int constexpr Maxiter(100);
     614       14549 :         int constexpr Maxiter1(100);
     615             : 
     616             :         // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
     617             :         Real64 PastFluxTop;    // top surface flux - past value
     618             :         Real64 PastFluxBtm;    // bottom surface flux - past value
     619             :         Real64 PastTempBtm;    // bottom surface temp - past value
     620             :         Real64 PastTempTop;    // top surface temp - past value
     621             :         Real64 OldPastFluxTop; // top surface flux - past value used during iteration
     622             :         Real64 OldPastFluxBtm; // bottom surface flux - past value used during iteration
     623             :         // variables used with current environmental conditions
     624       14549 :         auto &FluxTop = state.dataSurfaceGroundHeatExchangers->FluxTop; // top surface flux
     625       14549 :         auto &FluxBtm = state.dataSurfaceGroundHeatExchangers->FluxBtm; // bottom surface flux
     626       14549 :         auto &TempBtm = state.dataSurfaceGroundHeatExchangers->TempBtm; // bottom surface temp
     627       14549 :         auto &TempTop = state.dataSurfaceGroundHeatExchangers->TempTop; // top surface temp
     628             :         Real64 TempT;                                                   // top surface temp - used in underrelaxation
     629             :         Real64 TempB;                                                   // bottom surface temp - used in underrelaxation
     630             :         Real64 OldFluxTop;                                              // top surface flux - value used during iteration
     631             :         Real64 OldFluxBtm;                                              // bottom surface flux - value used during iteration
     632             :         Real64 OldSourceFlux;                                           // previous value of source flux - used during iteration
     633             :         int iter;
     634             :         int iter1;
     635             : 
     636             :         // check if we are in very first call for this zone time step
     637       14549 :         if (FirstHVACIteration && !state.dataHVACGlobal->ShortenTimeStepSys && this->firstTimeThrough) {
     638        1604 :             this->firstTimeThrough = false;
     639             :             // calc temps and fluxes with past env. conditions and average source flux
     640        1604 :             state.dataSurfaceGroundHeatExchangers->SourceFlux = this->QSrcAvg;
     641             :             // starting values for the surface temps
     642        1604 :             PastTempBtm = this->TbtmHistory[1];
     643        1604 :             PastTempTop = this->TtopHistory[1];
     644        1604 :             OldPastFluxTop = 1.0e+30;
     645        1604 :             OldPastFluxBtm = 1.0e+30;
     646        1604 :             TempB = 0.0;
     647        1604 :             TempT = 0.0;
     648        1604 :             iter = 0;
     649             :             while (true) { // iterate to find surface heat balances
     650             :                 // update coefficients
     651             : 
     652       11288 :                 ++iter;
     653       11288 :                 CalcTopFluxCoefficents(PastTempBtm, PastTempTop);
     654             :                 // calc top surface flux
     655       11288 :                 PastFluxTop = this->QtopConstCoef + this->QtopVarCoef * state.dataSurfaceGroundHeatExchangers->SourceFlux;
     656             : 
     657             :                 // calc new top surface temp
     658       11288 :                 CalcTopSurfTemp(-PastFluxTop,
     659             :                                 TempT,
     660       11288 :                                 state.dataSurfaceGroundHeatExchangers->PastOutDryBulbTemp,
     661       11288 :                                 state.dataSurfaceGroundHeatExchangers->PastOutWetBulbTemp,
     662       11288 :                                 state.dataSurfaceGroundHeatExchangers->PastSkyTemp,
     663       11288 :                                 state.dataSurfaceGroundHeatExchangers->PastBeamSolarRad,
     664       11288 :                                 state.dataSurfaceGroundHeatExchangers->PastDifSolarRad,
     665       11288 :                                 state.dataSurfaceGroundHeatExchangers->PastSolarDirCosVert,
     666       11288 :                                 state.dataSurfaceGroundHeatExchangers->PastWindSpeed,
     667       11288 :                                 state.dataSurfaceGroundHeatExchangers->PastIsRain,
     668       11288 :                                 state.dataSurfaceGroundHeatExchangers->PastIsSnow);
     669             :                 // under relax
     670       11288 :                 PastTempTop = PastTempTop * (1.0 - RelaxT) + RelaxT * TempT;
     671             : 
     672             :                 // update coefficients
     673       11288 :                 CalcBottomFluxCoefficents(PastTempBtm, PastTempTop);
     674       11288 :                 PastFluxBtm = this->QbtmConstCoef + this->QbtmVarCoef * state.dataSurfaceGroundHeatExchangers->SourceFlux;
     675             : 
     676       13099 :                 if (std::abs((OldPastFluxTop - PastFluxTop) / OldPastFluxTop) <= SurfFluxTol &&
     677        1811 :                     std::abs((OldPastFluxBtm - PastFluxBtm) / OldPastFluxBtm) <= SurfFluxTol)
     678        1604 :                     break;
     679             : 
     680             :                 // calc new surface temps
     681        9684 :                 CalcBottomSurfTemp(PastFluxBtm,
     682             :                                    TempB,
     683        9684 :                                    state.dataSurfaceGroundHeatExchangers->PastOutDryBulbTemp,
     684        9684 :                                    state.dataSurfaceGroundHeatExchangers->PastWindSpeed,
     685        9684 :                                    state.dataSurfaceGroundHeatExchangers->PastGroundTemp);
     686             :                 // underrelax
     687        9684 :                 PastTempBtm = PastTempBtm * (1.0 - RelaxT) + RelaxT * TempB;
     688             :                 // update flux record
     689        9684 :                 OldPastFluxTop = PastFluxTop;
     690        9684 :                 OldPastFluxBtm = PastFluxBtm;
     691             : 
     692             :                 // Check for non-convergence
     693        9684 :                 if (iter > Maxiter) {
     694           0 :                     if (this->ConvErrIndex1 == 0) {
     695           0 :                         ShowWarningMessage(
     696           0 :                             state, format("CalcSurfaceGroundHeatExchanger=\"{}\", Did not converge (part 1), Iterations={}", this->Name, Maxiter));
     697           0 :                         ShowContinueErrorTimeStamp(state, "");
     698             :                     }
     699           0 :                     ShowRecurringWarningErrorAtEnd(
     700           0 :                         state, "CalcSurfaceGroundHeatExchanger=\"" + this->Name + "\", Did not converge (part 1)", this->ConvErrIndex1);
     701           0 :                     break;
     702             :                 }
     703             :             }
     704             : 
     705        1604 :             if (!state.dataSurfaceGroundHeatExchangers->InitializeTempTop) {
     706           1 :                 TempTop = TempT;
     707           1 :                 TempBtm = TempB;
     708           1 :                 FluxTop = PastFluxTop;
     709           1 :                 FluxBtm = PastFluxBtm;
     710           1 :                 state.dataSurfaceGroundHeatExchangers->InitializeTempTop = true;
     711             :             }
     712             : 
     713             :             // update module variables
     714        1604 :             state.dataSurfaceGroundHeatExchangers->TopSurfTemp = TempTop;
     715        1604 :             state.dataSurfaceGroundHeatExchangers->BtmSurfTemp = TempBtm;
     716        1604 :             state.dataSurfaceGroundHeatExchangers->TopSurfFlux = -FluxTop;
     717        1604 :             state.dataSurfaceGroundHeatExchangers->BtmSurfFlux = FluxBtm;
     718             : 
     719             :             // get source temp for output
     720        1604 :             CalcSourceTempCoefficents(PastTempBtm, PastTempTop);
     721        1604 :             this->SourceTemp = this->TsrcConstCoef + this->TsrcVarCoef * state.dataSurfaceGroundHeatExchangers->SourceFlux;
     722             :             // update histories
     723        1604 :             UpdateHistories(PastFluxTop, PastFluxBtm, state.dataSurfaceGroundHeatExchangers->SourceFlux, this->SourceTemp);
     724             : 
     725             :             // At the beginning of a time step, reset to zero so average calculation can start again
     726        1604 :             this->QSrcAvg = 0.0;
     727        1604 :             this->LastSysTimeElapsed = 0.0;
     728        1604 :             this->LastTimeStepSys = 0.0;
     729             : 
     730             :             // get current env. conditions
     731        1604 :             state.dataSurfaceGroundHeatExchangers->PastBeamSolarRad = state.dataEnvrn->BeamSolarRad;
     732        1604 :             state.dataSurfaceGroundHeatExchangers->PastSolarDirCosVert = state.dataEnvrn->SOLCOS(3);
     733        1604 :             state.dataSurfaceGroundHeatExchangers->PastDifSolarRad = state.dataEnvrn->DifSolarRad;
     734        1604 :             state.dataSurfaceGroundHeatExchangers->PastGroundTemp = state.dataEnvrn->GroundTemp[(int)DataEnvironment::GroundTempType::Shallow];
     735        1604 :             state.dataSurfaceGroundHeatExchangers->PastIsRain = state.dataEnvrn->IsRain;
     736        1604 :             state.dataSurfaceGroundHeatExchangers->PastIsSnow = state.dataEnvrn->IsSnow;
     737        1604 :             state.dataSurfaceGroundHeatExchangers->PastOutDryBulbTemp = OutDryBulbTempAt(state, SurfaceHXHeight);
     738        1604 :             state.dataSurfaceGroundHeatExchangers->PastOutWetBulbTemp = OutWetBulbTempAt(state, SurfaceHXHeight);
     739        1604 :             state.dataSurfaceGroundHeatExchangers->PastSkyTemp = state.dataEnvrn->SkyTemp;
     740        1604 :             state.dataSurfaceGroundHeatExchangers->PastWindSpeed = DataEnvironment::WindSpeedAt(state, SurfaceHXHeight);
     741             : 
     742        1604 :             TempBtm = this->TbtmHistory[1];
     743        1604 :             TempTop = this->TtopHistory[1];
     744        1604 :             OldFluxTop = 1.0e+30;
     745        1604 :             OldFluxBtm = 1.0e+30;
     746        1604 :             OldSourceFlux = 1.0e+30;
     747        1604 :             state.dataSurfaceGroundHeatExchangers->SourceFlux = CalcSourceFlux(state);
     748        1604 :             iter = 0;
     749             :             while (true) { // iterate to find source flux
     750        3208 :                 ++iter;
     751        3208 :                 iter1 = 0;
     752             :                 while (true) { // iterate to find surface heat balances
     753       13336 :                     ++iter1;
     754             :                     // update top coefficients
     755       13336 :                     CalcTopFluxCoefficents(TempBtm, TempTop);
     756             :                     // calc top surface flux
     757       13336 :                     FluxTop = this->QtopConstCoef + this->QtopVarCoef * state.dataSurfaceGroundHeatExchangers->SourceFlux;
     758             :                     // calc new surface temps
     759       13336 :                     CalcTopSurfTemp(-FluxTop,
     760             :                                     TempT,
     761       13336 :                                     state.dataSurfaceGroundHeatExchangers->PastOutDryBulbTemp,
     762       13336 :                                     state.dataSurfaceGroundHeatExchangers->PastOutWetBulbTemp,
     763       13336 :                                     state.dataSurfaceGroundHeatExchangers->PastSkyTemp,
     764       13336 :                                     state.dataSurfaceGroundHeatExchangers->PastBeamSolarRad,
     765       13336 :                                     state.dataSurfaceGroundHeatExchangers->PastDifSolarRad,
     766       13336 :                                     state.dataSurfaceGroundHeatExchangers->PastSolarDirCosVert,
     767       13336 :                                     state.dataSurfaceGroundHeatExchangers->PastWindSpeed,
     768       13336 :                                     state.dataSurfaceGroundHeatExchangers->PastIsRain,
     769       13336 :                                     state.dataSurfaceGroundHeatExchangers->PastIsSnow);
     770             :                     // under-relax
     771       13336 :                     TempTop = TempTop * (1.0 - RelaxT) + RelaxT * TempT;
     772             :                     // update bottom coefficients
     773       13336 :                     CalcBottomFluxCoefficents(TempBtm, TempTop);
     774       13336 :                     FluxBtm = this->QbtmConstCoef + this->QbtmVarCoef * state.dataSurfaceGroundHeatExchangers->SourceFlux;
     775             :                     // convergence test on surface fluxes
     776       13336 :                     if (std::abs((OldFluxTop - FluxTop) / OldFluxTop) <= SurfFluxTol && std::abs((OldFluxBtm - FluxBtm) / OldFluxBtm) <= SurfFluxTol)
     777        3208 :                         break;
     778             : 
     779             :                     // calc new surface temps
     780       10128 :                     CalcBottomSurfTemp(FluxBtm,
     781             :                                        TempB,
     782       10128 :                                        state.dataSurfaceGroundHeatExchangers->PastOutDryBulbTemp,
     783       10128 :                                        state.dataSurfaceGroundHeatExchangers->PastOutDryBulbTemp,
     784       10128 :                                        state.dataEnvrn->GroundTemp[(int)DataEnvironment::GroundTempType::Shallow]);
     785             :                     // under-relax
     786       10128 :                     TempBtm = TempBtm * (1.0 - RelaxT) + RelaxT * TempB;
     787             :                     // update flux record
     788       10128 :                     OldFluxBtm = FluxBtm;
     789       10128 :                     OldFluxTop = FluxTop;
     790             : 
     791             :                     // Check for non-convergence
     792       10128 :                     if (iter1 > Maxiter1) {
     793           0 :                         if (this->ConvErrIndex2 == 0) {
     794           0 :                             ShowWarningMessage(
     795             :                                 state,
     796           0 :                                 format("CalcSurfaceGroundHeatExchanger=\"{}\", Did not converge (part 2), Iterations={}", this->Name, Maxiter));
     797           0 :                             ShowContinueErrorTimeStamp(state, "");
     798             :                         }
     799           0 :                         ShowRecurringWarningErrorAtEnd(
     800           0 :                             state, "CalcSurfaceGroundHeatExchanger=\"" + this->Name + "\", Did not converge (part 2)", this->ConvErrIndex2);
     801           0 :                         break;
     802             :                     }
     803             :                 }
     804             :                 // update the source temp coefficients and update the source flux
     805        3208 :                 CalcSourceTempCoefficents(TempBtm, TempTop);
     806        3208 :                 state.dataSurfaceGroundHeatExchangers->SourceFlux = CalcSourceFlux(state);
     807             :                 // check source flux convergence
     808        3208 :                 if (std::abs((OldSourceFlux - state.dataSurfaceGroundHeatExchangers->SourceFlux) / (1.0e-20 + OldSourceFlux)) <= SrcFluxTol) break;
     809        1604 :                 OldSourceFlux = state.dataSurfaceGroundHeatExchangers->SourceFlux;
     810             : 
     811             :                 // Check for non-convergence
     812        1604 :                 if (iter > Maxiter) {
     813           0 :                     if (this->ConvErrIndex3 == 0) {
     814           0 :                         ShowWarningMessage(
     815           0 :                             state, format("CalcSurfaceGroundHeatExchanger=\"{}\", Did not converge (part 3), Iterations={}", this->Name, Maxiter));
     816           0 :                         ShowContinueErrorTimeStamp(state, "");
     817             :                     }
     818           0 :                     ShowRecurringWarningErrorAtEnd(
     819           0 :                         state, "CalcSurfaceGroundHeatExchanger=\"" + this->Name + "\", Did not converge (part 3)", this->ConvErrIndex3);
     820           0 :                     break;
     821             :                 }
     822             :             } // end surface heat balance iteration
     823             : 
     824       12945 :         } else if (!FirstHVACIteration) { // end source flux iteration
     825             :             // For the rest of the system time steps ...
     826             :             // update source flux from Twi
     827        7272 :             this->firstTimeThrough = true;
     828        7272 :             state.dataSurfaceGroundHeatExchangers->SourceFlux = this->CalcSourceFlux(state);
     829             :         }
     830       14549 :     }
     831             : 
     832       24624 :     void SurfaceGroundHeatExchangerData::CalcBottomFluxCoefficents(Real64 const Tbottom, // current bottom (lower) surface temperature
     833             :                                                                    Real64 const Ttop     // current top (upper) surface temperature
     834             :     )
     835             :     {
     836             : 
     837             :         //       AUTHOR         Simon Rees
     838             :         //       DATE WRITTEN   August 2002
     839             :         //       MODIFIED       na
     840             :         //       RE-ENGINEERED  na
     841             : 
     842             :         // PURPOSE OF THIS SUBROUTINE:
     843             :         // Calculates current version of constant variable parts of QTF equations.
     844             : 
     845             :         // METHODOLOGY EMPLOYED:
     846             :         // For given current surface temperatures the terms of the QTF equations can be
     847             :         // grouped into constant terms, and those depending on the current source flux.
     848             :         // This routine calculates the current coefficient values for the bottom flux
     849             :         // equation.
     850             : 
     851             :         // REFERENCES:
     852             :         // Strand, R.K. 1995. "Heat Source Transfer Functions and Their Application to
     853             :         //   Low Temperature Radiant Heating Systems", Ph.D. dissertation, University
     854             :         //   of Illinois at Urbana-Champaign, Department of Mechanical and Industrial
     855             :         //   Engineering.
     856             : 
     857             :         // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
     858             :         int Term;
     859             : 
     860             :         // add current surface temperatures to history data
     861       24624 :         this->TbtmHistory[0] = Tbottom;
     862       24624 :         this->TtopHistory[0] = Ttop;
     863             : 
     864             :         // Bottom Surface Coefficients
     865       24624 :         this->QbtmConstCoef = 0.0;
     866      246240 :         for (Term = 0; Term <= this->NumCTFTerms - 1; ++Term) {
     867             : 
     868      221616 :             this->QbtmConstCoef += (-this->CTFin[Term] * this->TbtmHistory[Term]) + (this->CTFcross[Term] * this->TtopHistory[Term]) +
     869      221616 :                                    (this->CTFflux[Term] * this->QbtmHistory[Term]) + (this->CTFSourceIn[Term] * this->QsrcHistory[Term]);
     870             :         }
     871             : 
     872             :         // correct for extra bottom surface flux term
     873       24624 :         this->QbtmConstCoef -= this->CTFSourceIn[0] * this->QsrcHistory[0];
     874             :         // source flux current coefficient
     875       24624 :         this->QbtmVarCoef = this->CTFSourceIn[0];
     876       24624 :     }
     877             : 
     878       24624 :     void SurfaceGroundHeatExchangerData::CalcTopFluxCoefficents(Real64 const Tbottom, // current bottom (lower) surface temperature
     879             :                                                                 Real64 const Ttop     // current top (upper) surface temperature
     880             :     )
     881             :     {
     882             : 
     883             :         //       AUTHOR         Simon Rees
     884             :         //       DATE WRITTEN   August 2002
     885             :         //       MODIFIED       na
     886             :         //       RE-ENGINEERED  na
     887             : 
     888             :         // PURPOSE OF THIS SUBROUTINE:
     889             :         // Calculates current version of constant variable parts of QTF equations.
     890             : 
     891             :         // METHODOLOGY EMPLOYED:
     892             :         // For given current surface temperatures the terms of the QTF equations can be
     893             :         // grouped into constant terms, and those depending on the current source flux.
     894             :         // This routine calculates the current coefficient values for the top flux
     895             :         // equation.
     896             : 
     897             :         // REFERENCES:
     898             :         // Strand, R.K. 1995. "Heat Source Transfer Functions and Their Application to
     899             :         //   Low Temperature Radiant Heating Systems", Ph.D. dissertation, University
     900             :         //   of Illinois at Urbana-Champaign, Department of Mechanical and Industrial
     901             :         //   Engineering.
     902             : 
     903             :         // add current surface temperatures to history data
     904       24624 :         this->TbtmHistory[0] = Tbottom;
     905       24624 :         this->TtopHistory[0] = Ttop;
     906             : 
     907             :         // Top Surface Coefficients
     908       24624 :         this->QtopConstCoef = 0.0;
     909      246240 :         for (int Term = 0; Term <= this->NumCTFTerms - 1; ++Term) {
     910             : 
     911      221616 :             this->QtopConstCoef += (this->CTFout[Term] * this->TtopHistory[Term]) - (this->CTFcross[Term] * this->TbtmHistory[Term]) +
     912      221616 :                                    (this->CTFflux[Term] * this->QtopHistory[Term]) + (this->CTFSourceOut[Term] * this->QsrcHistory[Term]);
     913             :         }
     914             : 
     915             :         // correct for extra top surface flux term
     916       24624 :         this->QtopConstCoef -= (this->CTFSourceOut[0] * this->QsrcHistory[0]);
     917             :         // surface flux current coefficient
     918       24624 :         this->QtopVarCoef = this->CTFSourceOut[0];
     919       24624 :     }
     920             : 
     921        4812 :     void SurfaceGroundHeatExchangerData::CalcSourceTempCoefficents(Real64 const Tbottom, // current bottom (lower) surface temperature
     922             :                                                                    Real64 const Ttop     // current top (upper) surface temperature
     923             :     )
     924             :     {
     925             : 
     926             :         //       AUTHOR         Simon Rees
     927             :         //       DATE WRITTEN   August 2002
     928             :         //       MODIFIED       na
     929             :         //       RE-ENGINEERED  na
     930             : 
     931             :         // PURPOSE OF THIS SUBROUTINE:
     932             :         // Calculates current version of constant variable parts of QTF equations.
     933             : 
     934             :         // METHODOLOGY EMPLOYED:
     935             :         // For given current surface temperatures the terms of the QTF equations can be
     936             :         // grouped into constant terms, and those depending on the current source flux.
     937             :         // This routine calculates the current coefficient values for the source temperature
     938             :         // equation.
     939             : 
     940             :         // REFERENCES:
     941             :         // Strand, R.K. 1995. "Heat Source Transfer Functions and Their Application to
     942             :         //   Low Temperature Radiant Heating Systems", Ph.D. dissertation, University
     943             :         //   of Illinois at Urbana-Champaign, Department of Mechanical and Industrial
     944             :         //   Engineering.
     945             : 
     946             :         // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
     947             :         int Term;
     948             : 
     949             :         // add current surface temperatures to history data
     950        4812 :         this->TbtmHistory[0] = Tbottom;
     951        4812 :         this->TtopHistory[0] = Ttop;
     952             : 
     953        4812 :         this->TsrcConstCoef = 0.0;
     954       48120 :         for (Term = 0; Term <= this->NumCTFTerms - 1; ++Term) {
     955             : 
     956       43308 :             this->TsrcConstCoef += (this->CTFTSourceIn[Term] * this->TbtmHistory[Term]) + (this->CTFTSourceOut[Term] * this->TtopHistory[Term]) +
     957       43308 :                                    (this->CTFflux[Term] * this->TsrcHistory[Term]) + (this->CTFTSourceQ[Term] * this->QsrcHistory[Term]);
     958             :         }
     959             : 
     960             :         // correct for extra source flux term
     961        4812 :         this->TsrcConstCoef -= this->CTFTSourceQ[0] * this->QsrcHistory[0];
     962             :         // source flux current coefficient
     963        4812 :         this->TsrcVarCoef = this->CTFTSourceQ[0];
     964        4812 :     }
     965             : 
     966       12084 :     Real64 SurfaceGroundHeatExchangerData::CalcSourceFlux(EnergyPlusData &state) // component number
     967             :     {
     968             : 
     969             :         //       AUTHOR         Simon Rees
     970             :         //       DATE WRITTEN   August 2002
     971             :         //       MODIFIED       na
     972             :         //       RE-ENGINEERED  na
     973             : 
     974             :         // PURPOSE OF THIS SUBROUTINE:
     975             :         // This calculates the source flux given the inlet fluid temperature. A
     976             :         // heat exchanger analogy is used, with the surface as a 'Fixed' fluid.
     977             : 
     978             :         // METHODOLOGY EMPLOYED:
     979             : 
     980             :         // REFERENCES:
     981             :         // Strand, R.K. 1995. "Heat Source Transfer Functions and Their Application to
     982             :         //   Low Temperature Radiant Heating Systems", Ph.D. dissertation, University
     983             :         //   of Illinois at Urbana-Champaign, Department of Mechanical and Industrial
     984             :         //   Engineering.
     985             : 
     986             :         // Return value
     987             :         Real64 CalcSourceFlux;
     988             : 
     989             :         // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
     990             :         Real64 EpsMdotCp; // Epsilon (heat exchanger terminology) times water mass flow rate times water specific heat
     991             : 
     992             :         // Effectiveness * Modot * specific heat
     993       12084 :         if (state.dataSurfaceGroundHeatExchangers->FlowRate > 0.0) {
     994        7173 :             EpsMdotCp = CalcHXEffectTerm(state, this->InletTemp, state.dataSurfaceGroundHeatExchangers->FlowRate);
     995             :             // calc flux
     996        7173 :             CalcSourceFlux = (this->InletTemp - this->TsrcConstCoef) / (this->SurfaceArea / EpsMdotCp + this->TsrcVarCoef);
     997             :         } else {
     998        4911 :             CalcSourceFlux = 0.0;
     999             :         }
    1000             : 
    1001       12084 :         return CalcSourceFlux;
    1002             :     }
    1003             : 
    1004        1604 :     void SurfaceGroundHeatExchangerData::UpdateHistories(Real64 const TopFlux,    // current top (top) surface flux
    1005             :                                                          Real64 const BottomFlux, // current bottom (bottom) surface flux
    1006             :                                                          Real64 const sourceFlux, // current source surface flux
    1007             :                                                          Real64 const sourceTemp  // current source temperature
    1008             :     )
    1009             :     {
    1010             : 
    1011             :         //       AUTHOR         Simon Rees
    1012             :         //       DATE WRITTEN   August 2002
    1013             :         //       MODIFIED       na
    1014             :         //       RE-ENGINEERED  na
    1015             : 
    1016             :         // PURPOSE OF THIS SUBROUTINE:
    1017             :         // This is used to update the temperature and flux records for the QTF
    1018             :         // calculations. This is called at the start of each zone timestep.
    1019             : 
    1020             :         // METHODOLOGY EMPLOYED:
    1021             :         // Just shift along and replace zero index element with current value.
    1022             : 
    1023             :         // update top surface temps
    1024        1604 :         this->TtopHistory = eoshiftArray(this->TtopHistory, -1, 0.0);
    1025             : 
    1026             :         // update bottom surface temps
    1027        1604 :         this->TbtmHistory = eoshiftArray(this->TbtmHistory, -1, 0.0);
    1028             : 
    1029             :         // update bottom surface temps
    1030        1604 :         this->TsrcHistory = eoshiftArray(this->TsrcHistory, -1, 0.0);
    1031        1604 :         this->TsrcHistory[1] = sourceTemp;
    1032             : 
    1033             :         // update bottom surface fluxes
    1034        1604 :         this->QbtmHistory = eoshiftArray(this->QbtmHistory, -1, 0.0);
    1035        1604 :         this->QbtmHistory[1] = BottomFlux;
    1036             : 
    1037             :         // update bottom surface fluxes
    1038        1604 :         this->QtopHistory = eoshiftArray(this->QtopHistory, -1, 0.0);
    1039        1604 :         this->QtopHistory[1] = TopFlux;
    1040             : 
    1041             :         // update bottom surface fluxes
    1042        1604 :         this->QsrcHistory = eoshiftArray(this->QsrcHistory, -1, 0.0);
    1043        1604 :         this->QsrcHistory[1] = sourceFlux;
    1044        1604 :     }
    1045             : 
    1046        7173 :     Real64 SurfaceGroundHeatExchangerData::CalcHXEffectTerm(EnergyPlusData &state,
    1047             :                                                             Real64 const Temperature,  // Temperature of water entering the surface, in C
    1048             :                                                             Real64 const WaterMassFlow // Mass flow rate, in kg/s
    1049             :     )
    1050             :     {
    1051             : 
    1052             :         // SUBROUTINE INFORMATION:
    1053             :         //       AUTHOR         Rick Strand
    1054             :         //       DATE WRITTEN   December 2000
    1055             :         //       MODIFIED       Simon Rees, August 2002
    1056             :         //       RE-ENGINEERED  na
    1057             : 
    1058             :         // PURPOSE OF THIS SUBROUTINE:
    1059             :         // This subroutine calculates the "heat exchanger"
    1060             :         // effectiveness term.  This is equal to the mass flow rate of water
    1061             :         // times the specific heat of water times the effectiveness of
    1062             :         // the surface heat exchanger. This routine is adapted from that in
    1063             :         // the low temp radiant surface model.
    1064             : 
    1065             :         // METHODOLOGY EMPLOYED:
    1066             :         // Assumes that the only REAL(r64) heat transfer term that we have to
    1067             :         // deal with is the convection from the water to the tube.  The
    1068             :         // other assumptions are that the tube bottom surface temperature
    1069             :         // is equal to the "source location temperature" and that it is
    1070             :         // a CONSTANT throughout the surface.
    1071             : 
    1072             :         // REFERENCES:
    1073             :         // See RadiantSystemLowTemp module.
    1074             :         // Property data for water shown below as parameters taken from
    1075             :         //   Incropera and DeWitt, Introduction to Heat Transfer, Table A.6.
    1076             :         // Heat exchanger information also from Incropera and DeWitt.
    1077             :         // Code based loosely on code from IBLAST program (research version)
    1078             : 
    1079             :         // Using/Aliasing
    1080             :         using FluidProperties::GetSpecificHeatGlycol;
    1081             : 
    1082             :         // Return value
    1083             :         Real64 CalcHXEffectTerm;
    1084             : 
    1085        7173 :         Real64 constexpr MaxLaminarRe(2300.0); // Maximum Reynolds number for laminar flow
    1086        7173 :         int constexpr NumOfPropDivisions(13);  // intervals in property correlation
    1087             :         static constexpr std::array<Real64, NumOfPropDivisions> Temps = {
    1088             :             1.85, 6.85, 11.85, 16.85, 21.85, 26.85, 31.85, 36.85, 41.85, 46.85, 51.85, 56.85, 61.85}; // Temperature, in C
    1089             :         static constexpr std::array<Real64, NumOfPropDivisions> Mu = {0.001652,
    1090             :                                                                       0.001422,
    1091             :                                                                       0.001225,
    1092             :                                                                       0.00108,
    1093             :                                                                       0.000959,
    1094             :                                                                       0.000855,
    1095             :                                                                       0.000769,
    1096             :                                                                       0.000695,
    1097             :                                                                       0.000631,
    1098             :                                                                       0.000577,
    1099             :                                                                       0.000528,
    1100             :                                                                       0.000489,
    1101             :                                                                       0.000453}; // Viscosity, in Ns/m2
    1102             :         static constexpr std::array<Real64, NumOfPropDivisions> Conductivity = {
    1103             :             0.574, 0.582, 0.590, 0.598, 0.606, 0.613, 0.620, 0.628, 0.634, 0.640, 0.645, 0.650, 0.656}; // Conductivity, in W/mK
    1104             :         static constexpr std::array<Real64, NumOfPropDivisions> Pr = {
    1105             :             12.22, 10.26, 8.81, 7.56, 6.62, 5.83, 5.20, 4.62, 4.16, 3.77, 3.42, 3.15, 2.88}; // Prandtl number (dimensionless)
    1106        7173 :         int constexpr WaterIndex(1);
    1107             :         static constexpr std::string_view RoutineName("SurfaceGroundHeatExchanger:CalcHXEffectTerm");
    1108             : 
    1109             :         // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    1110             :         int Index;
    1111             :         Real64 InterpFrac;
    1112             :         Real64 NuD;
    1113             :         Real64 ReD;
    1114             :         Real64 NTU;
    1115             :         Real64 CpWater;
    1116             :         Real64 Kactual;
    1117             :         Real64 MUactual;
    1118             :         Real64 PRactual;
    1119             :         Real64 PipeLength;
    1120             : 
    1121             :         // First find out where we are in the range of temperatures
    1122        7173 :         Index = 0;
    1123       50548 :         while (Index < NumOfPropDivisions) {
    1124       50548 :             if (Temperature < Temps[Index]) break; // DO loop
    1125       43375 :             ++Index;
    1126             :         }
    1127             : 
    1128             :         // Initialize thermal properties of water
    1129        7173 :         if (Index == 0) {
    1130           0 :             MUactual = Mu[Index];
    1131           0 :             Kactual = Conductivity[Index];
    1132           0 :             PRactual = Pr[Index];
    1133        7173 :         } else if (Index > NumOfPropDivisions - 1) {
    1134           0 :             Index = NumOfPropDivisions - 1;
    1135           0 :             MUactual = Mu[Index];
    1136           0 :             Kactual = Conductivity[Index];
    1137           0 :             PRactual = Pr[Index];
    1138             :         } else {
    1139        7173 :             InterpFrac = (Temperature - Temps[Index - 1]) / (Temps[Index] - Temps[Index - 1]);
    1140        7173 :             MUactual = Mu[Index - 1] + InterpFrac * (Mu[Index] - Mu[Index - 1]);
    1141        7173 :             Kactual = Conductivity[Index - 1] + InterpFrac * (Conductivity[Index] - Conductivity[Index - 1]);
    1142        7173 :             PRactual = Pr[Index - 1] + InterpFrac * (Pr[Index] - Pr[Index - 1]);
    1143             :         }
    1144             :         // arguments are glycol name, temperature, and concentration
    1145        7173 :         if (Temperature < 0.0) { // check if fluid is water and would be freezing
    1146           0 :             if (state.dataPlnt->PlantLoop(this->plantLoc.loopNum).FluidIndex == WaterIndex) {
    1147           0 :                 if (this->FrozenErrIndex1 == 0) {
    1148           0 :                     ShowWarningMessage(
    1149             :                         state,
    1150           0 :                         format("GroundHeatExchanger:Surface=\"{}\", water is frozen; Model not valid. Calculated Water Temperature=[{:.2R}] C",
    1151           0 :                                this->Name,
    1152           0 :                                this->InletTemp));
    1153           0 :                     ShowContinueErrorTimeStamp(state, "");
    1154             :                 }
    1155           0 :                 ShowRecurringWarningErrorAtEnd(state,
    1156           0 :                                                "GroundHeatExchanger:Surface=\"" + this->Name + "\", water is frozen",
    1157           0 :                                                this->FrozenErrIndex1,
    1158           0 :                                                this->InletTemp,
    1159           0 :                                                this->InletTemp,
    1160             :                                                _,
    1161             :                                                "[C]",
    1162             :                                                "[C]");
    1163           0 :                 this->InletTemp = max(this->InletTemp, 0.0);
    1164             :             }
    1165             :         }
    1166        7173 :         CpWater = GetSpecificHeatGlycol(state,
    1167        7173 :                                         state.dataPlnt->PlantLoop(this->plantLoc.loopNum).FluidName,
    1168             :                                         Temperature,
    1169        7173 :                                         state.dataPlnt->PlantLoop(this->plantLoc.loopNum).FluidIndex,
    1170             :                                         RoutineName);
    1171             : 
    1172             :         // Calculate the Reynold's number from RE=(4*Mdot)/(Pi*Mu*Diameter)
    1173        7173 :         ReD = 4.0 * WaterMassFlow / (Constant::Pi * MUactual * this->TubeDiameter * this->TubeCircuits);
    1174             : 
    1175             :         // Calculate the Nusselt number based on what flow regime one is in
    1176        7173 :         if (ReD >= MaxLaminarRe) { // Turbulent flow --> use Colburn equation
    1177        7173 :             NuD = 0.023 * std::pow(ReD, 0.8) * std::pow(PRactual, 1.0 / 3.0);
    1178             :         } else { // Laminar flow --> use constant surface temperature relation
    1179           0 :             NuD = 3.66;
    1180             :         }
    1181             :         // Calculate the NTU parameter
    1182             :         // NTU = UA/[(Mdot*Cp)min]
    1183             :         // where: U = h (convection coefficient) and h = (k)(Nu)/D
    1184             :         //        A = Pi*D*TubeLength
    1185             :         //  NTU = PI * Kactual * NuD * SurfaceGHE(SurfaceGHENum)%TubeLength / (WaterMassFlow * CpWater)
    1186             : 
    1187        7173 :         PipeLength = this->SurfaceLength * this->SurfaceWidth / this->TubeSpacing;
    1188             : 
    1189        7173 :         NTU = Constant::Pi * Kactual * NuD * PipeLength / (WaterMassFlow * CpWater);
    1190             :         // Calculate Epsilon*MassFlowRate*Cp
    1191        7173 :         if (-NTU >= DataPrecisionGlobals::EXP_LowerLimit) {
    1192           0 :             CalcHXEffectTerm = (1.0 - std::exp(-NTU)) * WaterMassFlow * CpWater;
    1193             :         } else {
    1194        7173 :             CalcHXEffectTerm = 1.0 * WaterMassFlow * CpWater;
    1195             :         }
    1196             : 
    1197        7173 :         return CalcHXEffectTerm;
    1198             :     }
    1199             : 
    1200       24624 :     void SurfaceGroundHeatExchangerData::CalcTopSurfTemp(Real64 const FluxTop,             // top surface flux
    1201             :                                                          Real64 &TempTop,                  // top surface temperature
    1202             :                                                          Real64 const ThisDryBulb,         // dry bulb temperature
    1203             :                                                          Real64 const ThisWetBulb,         // wet bulb temperature
    1204             :                                                          Real64 const ThisSkyTemp,         // sky temperature
    1205             :                                                          Real64 const ThisBeamSolarRad,    // beam solar radiation
    1206             :                                                          Real64 const ThisDifSolarRad,     // diffuse solar radiation
    1207             :                                                          Real64 const ThisSolarDirCosVert, // vertical component of solar normal
    1208             :                                                          Real64 const ThisWindSpeed,       // wind speed
    1209             :                                                          bool const ThisIsRain,            // rain flag
    1210             :                                                          bool const ThisIsSnow             // snow flag
    1211             :     )
    1212             :     {
    1213             : 
    1214             :         //       AUTHOR         Simon Rees
    1215             :         //       DATE WRITTEN   August 2002
    1216             :         //       MODIFIED       na
    1217             :         //       RE-ENGINEERED  na
    1218             : 
    1219             :         // PURPOSE OF THIS SUBROUTINE:
    1220             :         // This subroutine is used to calculate the top surface
    1221             :         // temperature for the given surface flux.
    1222             : 
    1223             :         // METHODOLOGY EMPLOYED:
    1224             :         // calc surface heat balance
    1225             : 
    1226             :         // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    1227             :         Real64 ConvCoef;     // convection coefficient
    1228             :         Real64 RadCoef;      // radiation coefficient
    1229             :         Real64 ExternalTemp; // external environmental temp - drybulb or wetbulb
    1230             :         Real64 OldSurfTemp;  // previous surface temperature
    1231             :         Real64 QSolAbsorbed; // absorbed solar flux
    1232             :         Real64 SurfTempAbs;  // absolute value of surface temp
    1233             :         Real64 SkyTempAbs;   // absolute value of sky temp
    1234             : 
    1235             :         // make a surface heat balance and solve for temperature
    1236             : 
    1237             :         // set appropriate external temp
    1238       24624 :         if (ThisIsSnow || ThisIsRain) {
    1239           0 :             ExternalTemp = ThisWetBulb;
    1240             :         } else { // normal dry conditions
    1241       24624 :             ExternalTemp = ThisDryBulb;
    1242             :         }
    1243             : 
    1244             :         // set previous surface temp
    1245       24624 :         OldSurfTemp = this->TtopHistory[1];
    1246             :         // absolute temperatures
    1247       24624 :         SurfTempAbs = OldSurfTemp + Constant::Kelvin;
    1248       24624 :         SkyTempAbs = ThisSkyTemp + Constant::Kelvin;
    1249             : 
    1250             :         // ASHRAE simple convection coefficient model for external surfaces.
    1251       24624 :         ConvCoef = Convect::CalcASHRAESimpExtConvCoeff(this->TopRoughness, ThisWindSpeed);
    1252             :         // radiation coefficient using surf temp from past time step
    1253       24624 :         if (std::abs(SurfTempAbs - SkyTempAbs) > SmallNum) {
    1254       24624 :             RadCoef = StefBoltzmann * this->TopThermAbs * (pow_4(SurfTempAbs) - pow_4(SkyTempAbs)) / (SurfTempAbs - SkyTempAbs);
    1255             :         } else {
    1256           0 :             RadCoef = 0.0;
    1257             :         }
    1258             : 
    1259             :         // total absorbed solar - no ground solar
    1260       24624 :         QSolAbsorbed = this->TopSolarAbs * (max(ThisSolarDirCosVert, 0.0) * ThisBeamSolarRad + ThisDifSolarRad);
    1261             : 
    1262             :         // solve for temperature
    1263       24624 :         TempTop = (FluxTop + ConvCoef * ExternalTemp + RadCoef * ThisSkyTemp + QSolAbsorbed) / (ConvCoef + RadCoef);
    1264       24624 :     }
    1265             : 
    1266       19812 :     void SurfaceGroundHeatExchangerData::CalcBottomSurfTemp(Real64 const FluxBtm,       // bottom surface flux
    1267             :                                                             Real64 &TempBtm,            // bottom surface temperature
    1268             :                                                             Real64 const ThisDryBulb,   // dry bulb temperature
    1269             :                                                             Real64 const ThisWindSpeed, // wind speed
    1270             :                                                             Real64 const ThisGroundTemp // ground temperature
    1271             :     )
    1272             :     {
    1273             : 
    1274             :         //       AUTHOR         Simon Rees
    1275             :         //       DATE WRITTEN   August 2002
    1276             :         //       MODIFIED       na
    1277             :         //       RE-ENGINEERED  na
    1278             : 
    1279             :         // PURPOSE OF THIS SUBROUTINE:
    1280             :         // This subroutine is used to calculate the bottom surface
    1281             :         // temperature for the given surface flux.
    1282             : 
    1283             :         // METHODOLOGY EMPLOYED:
    1284             :         // calc surface heat balances
    1285             : 
    1286             :         // Using/Aliasing
    1287             : 
    1288             :         Real64 ConvCoef;    // convection coefficient
    1289             :         Real64 RadCoef;     // radiation coefficient
    1290             :         Real64 OldSurfTemp; // previous surface temperature
    1291             :         Real64 SurfTempAbs; // absolute value of surface temp
    1292             :         Real64 ExtTempAbs;  // absolute value of sky temp
    1293             : 
    1294       19812 :         if (this->LowerSurfCond == SurfCond_Exposed) {
    1295             : 
    1296             :             // make a surface heat balance and solve for temperature
    1297           0 :             OldSurfTemp = this->TbtmHistory[1];
    1298             :             // absolute temperatures
    1299           0 :             SurfTempAbs = OldSurfTemp + Constant::Kelvin;
    1300           0 :             ExtTempAbs = ThisDryBulb + Constant::Kelvin;
    1301             : 
    1302             :             // ASHRAE simple convection coefficient model for external surfaces.
    1303           0 :             ConvCoef = Convect::CalcASHRAESimpExtConvCoeff(this->TopRoughness, ThisWindSpeed);
    1304             : 
    1305             :             // radiation coefficient using surf temp from past time step
    1306           0 :             if (std::abs(SurfTempAbs - ExtTempAbs) > SmallNum) {
    1307           0 :                 RadCoef = StefBoltzmann * this->TopThermAbs * (pow_4(SurfTempAbs) - pow_4(ExtTempAbs)) / (SurfTempAbs - ExtTempAbs);
    1308             :             } else {
    1309           0 :                 RadCoef = 0.0;
    1310             :             }
    1311             : 
    1312             :             // total absorbed solar - no ground solar
    1313           0 :             TempBtm = (FluxBtm + ConvCoef * ThisDryBulb + RadCoef * ThisDryBulb) / (ConvCoef + RadCoef);
    1314             : 
    1315             :         } else { // ground coupled
    1316             :             // just use the supplied ground temperature
    1317       19812 :             TempBtm = ThisGroundTemp;
    1318             :         }
    1319       19812 :     }
    1320             : 
    1321       14549 :     void SurfaceGroundHeatExchangerData::UpdateSurfaceGroundHeatExchngr(EnergyPlusData &state) // Index for the surface
    1322             :     {
    1323             : 
    1324             :         // SUBROUTINE INFORMATION:
    1325             :         //       AUTHOR         Simon Rees
    1326             :         //       DATE WRITTEN   August 2002
    1327             :         //       MODIFIED       na
    1328             :         //       RE-ENGINEERED  na
    1329             : 
    1330             :         // PURPOSE OF THIS SUBROUTINE:
    1331             :         // This subroutine does any updating that needs to be done for surface
    1332             :         // ground heat exchangers.  One of the most important functions of
    1333             :         // this routine is to update the average heat source/sink for a
    1334             :         // particular system over the various system time steps that make up
    1335             :         // the zone time step. This routine must also set the outlet water conditions.
    1336             : 
    1337             :         // METHODOLOGY EMPLOYED:
    1338             :         // For the source/sink average update, if the system time step elapsed
    1339             :         // is still what it used to be, then either we are still iterating or
    1340             :         // we had to go back and shorten the time step.  As a result, we have
    1341             :         // to subtract out the previous value that we added.  If the system
    1342             :         // time step elapsed is different, then we just need to add the new
    1343             :         // values to the running average.
    1344             : 
    1345             :         // Using/Aliasing
    1346       14549 :         Real64 SysTimeElapsed = state.dataHVACGlobal->SysTimeElapsed;
    1347       14549 :         Real64 TimeStepSys = state.dataHVACGlobal->TimeStepSys;
    1348             :         using FluidProperties::GetSpecificHeatGlycol;
    1349             :         using PlantUtilities::SafeCopyPlantNode;
    1350             : 
    1351             :         // SUBROUTINE PARAMETER DEFINITIONS:
    1352             :         static constexpr std::string_view RoutineName("SurfaceGroundHeatExchanger:Update");
    1353             : 
    1354             :         // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    1355             :         Real64 CpFluid; // Specific heat of working fluid
    1356             : 
    1357             :         // update flux
    1358       14549 :         this->QSrc = state.dataSurfaceGroundHeatExchangers->SourceFlux;
    1359             : 
    1360       14549 :         if (this->LastSysTimeElapsed == SysTimeElapsed) { // only update in normal mode
    1361             :             // Still iterating or reducing system time step, so subtract old values which were not valid
    1362       12565 :             this->QSrcAvg -= this->LastQSrc * this->LastTimeStepSys / state.dataGlobal->TimeStepZone;
    1363             : 
    1364             :             // Update the running average and the "last" values with the current values of the appropriate variables
    1365       12565 :             this->QSrcAvg += this->QSrc * TimeStepSys / state.dataGlobal->TimeStepZone;
    1366             : 
    1367       12565 :             this->LastQSrc = state.dataSurfaceGroundHeatExchangers->SourceFlux;
    1368       12565 :             this->LastSysTimeElapsed = SysTimeElapsed;
    1369       12565 :             this->LastTimeStepSys = TimeStepSys;
    1370             :         }
    1371             : 
    1372             :         // Calculate the water side outlet conditions and set the
    1373             :         // appropriate conditions on the correct HVAC node.
    1374       14549 :         if (state.dataPlnt->PlantLoop(this->plantLoc.loopNum).FluidName == "WATER") {
    1375       14549 :             if (InletTemp < 0.0) {
    1376           0 :                 ShowRecurringWarningErrorAtEnd(state,
    1377           0 :                                                "UpdateSurfaceGroundHeatExchngr: Water is frozen in Surf HX=" + this->Name,
    1378           0 :                                                this->FrozenErrIndex2,
    1379           0 :                                                this->InletTemp,
    1380           0 :                                                this->InletTemp);
    1381             :             }
    1382       14549 :             this->InletTemp = max(this->InletTemp, 0.0);
    1383             :         }
    1384             : 
    1385       14549 :         CpFluid = GetSpecificHeatGlycol(state,
    1386       14549 :                                         state.dataPlnt->PlantLoop(this->plantLoc.loopNum).FluidName,
    1387             :                                         this->InletTemp,
    1388       14549 :                                         state.dataPlnt->PlantLoop(this->plantLoc.loopNum).FluidIndex,
    1389             :                                         RoutineName);
    1390             : 
    1391       14549 :         SafeCopyPlantNode(state, this->InletNodeNum, this->OutletNodeNum);
    1392             :         // check for flow
    1393       14549 :         if ((CpFluid > 0.0) && (state.dataSurfaceGroundHeatExchangers->FlowRate > 0.0)) {
    1394        8904 :             state.dataLoopNodes->Node(this->OutletNodeNum).Temp = this->InletTemp - this->SurfaceArea *
    1395        8904 :                                                                                         state.dataSurfaceGroundHeatExchangers->SourceFlux /
    1396        8904 :                                                                                         (state.dataSurfaceGroundHeatExchangers->FlowRate * CpFluid);
    1397        8904 :             state.dataLoopNodes->Node(this->OutletNodeNum).Enthalpy = state.dataLoopNodes->Node(this->OutletNodeNum).Temp * CpFluid;
    1398             :         }
    1399       14549 :     }
    1400             : 
    1401       14549 :     void SurfaceGroundHeatExchangerData::ReportSurfaceGroundHeatExchngr(EnergyPlusData &state) // Index for the surface under consideration
    1402             :     {
    1403             : 
    1404             :         // SUBROUTINE INFORMATION:
    1405             :         //       AUTHOR         Simon Rees
    1406             :         //       DATE WRITTEN   August 2002
    1407             :         //       MODIFIED       na
    1408             :         //       RE-ENGINEERED  na
    1409             : 
    1410             :         // PURPOSE OF THIS SUBROUTINE:
    1411             :         // This subroutine simply produces output for Surface ground heat exchangers
    1412             : 
    1413             :         // Using/Aliasing
    1414       14549 :         Real64 TimeStepSysSec = state.dataHVACGlobal->TimeStepSysSec;
    1415             : 
    1416             :         // update flows and temps from node data
    1417       14549 :         this->InletTemp = state.dataLoopNodes->Node(this->InletNodeNum).Temp;
    1418       14549 :         this->OutletTemp = state.dataLoopNodes->Node(this->OutletNodeNum).Temp;
    1419       14549 :         this->MassFlowRate = state.dataLoopNodes->Node(this->InletNodeNum).MassFlowRate;
    1420             : 
    1421             :         // update other variables from module variables
    1422       14549 :         this->HeatTransferRate = state.dataSurfaceGroundHeatExchangers->SourceFlux * this->SurfaceArea;
    1423       14549 :         this->SurfHeatTransferRate =
    1424       14549 :             this->SurfaceArea * (state.dataSurfaceGroundHeatExchangers->TopSurfFlux + state.dataSurfaceGroundHeatExchangers->BtmSurfFlux);
    1425       14549 :         this->Energy = state.dataSurfaceGroundHeatExchangers->SourceFlux * this->SurfaceArea * TimeStepSysSec;
    1426       14549 :         this->TopSurfaceTemp = state.dataSurfaceGroundHeatExchangers->TopSurfTemp;
    1427       14549 :         this->BtmSurfaceTemp = state.dataSurfaceGroundHeatExchangers->BtmSurfTemp;
    1428       14549 :         this->TopSurfaceFlux = state.dataSurfaceGroundHeatExchangers->TopSurfFlux;
    1429       14549 :         this->BtmSurfaceFlux = state.dataSurfaceGroundHeatExchangers->BtmSurfFlux;
    1430       14549 :         this->SurfEnergy =
    1431       14549 :             SurfaceArea * (state.dataSurfaceGroundHeatExchangers->TopSurfFlux + state.dataSurfaceGroundHeatExchangers->BtmSurfFlux) * TimeStepSysSec;
    1432       14549 :     }
    1433           1 :     void SurfaceGroundHeatExchangerData::oneTimeInit_new(EnergyPlusData &state)
    1434             :     {
    1435             : 
    1436             :         using FluidProperties::GetDensityGlycol;
    1437             :         using PlantUtilities::InitComponentNodes;
    1438             :         using PlantUtilities::RegisterPlantCompDesignFlow;
    1439             :         using PlantUtilities::ScanPlantLoopsForObject;
    1440             : 
    1441             :         // SUBROUTINE PARAMETER DEFINITIONS:
    1442           1 :         Real64 constexpr DesignVelocity(0.5); // Hypothetical design max pipe velocity [m/s]
    1443             :         Real64 rho;                           // local fluid density
    1444             :         bool errFlag;
    1445           1 :         static std::string const RoutineName("InitSurfaceGroundHeatExchanger");
    1446             : 
    1447             :         // Locate the hx on the plant loops for later usage
    1448           1 :         errFlag = false;
    1449           1 :         ScanPlantLoopsForObject(state, this->Name, DataPlant::PlantEquipmentType::GrndHtExchgSurface, this->plantLoc, errFlag, _, _, _, _, _);
    1450             : 
    1451           1 :         if (errFlag) {
    1452           0 :             ShowFatalError(state, "InitSurfaceGroundHeatExchanger: Program terminated due to previous condition(s).");
    1453             :         }
    1454           2 :         rho = GetDensityGlycol(state,
    1455           1 :                                state.dataPlnt->PlantLoop(this->plantLoc.loopNum).FluidName,
    1456             :                                DataPrecisionGlobals::constant_zero,
    1457           1 :                                state.dataPlnt->PlantLoop(this->plantLoc.loopNum).FluidIndex,
    1458             :                                RoutineName);
    1459           1 :         this->DesignMassFlowRate = Constant::Pi / 4.0 * pow_2(this->TubeDiameter) * DesignVelocity * rho * this->TubeCircuits;
    1460           1 :         InitComponentNodes(state, 0.0, this->DesignMassFlowRate, this->InletNodeNum, this->OutletNodeNum);
    1461           1 :         RegisterPlantCompDesignFlow(state, this->InletNodeNum, this->DesignMassFlowRate / rho);
    1462           1 :     }
    1463           0 :     void SurfaceGroundHeatExchangerData::oneTimeInit([[maybe_unused]] EnergyPlusData &state)
    1464             :     {
    1465           0 :     }
    1466             : 
    1467             : } // namespace SurfaceGroundHeatExchanger
    1468             : 
    1469             : } // namespace EnergyPlus

Generated by: LCOV version 1.14