LCOV - code coverage report
Current view: top level - EnergyPlus - SurfaceGroundHeatExchanger.cc (source / functions) Coverage Total Hit
Test: lcov.output.filtered Lines: 79.7 % 533 425
Test Date: 2025-06-02 07:23:51 Functions: 94.1 % 17 16

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

Generated by: LCOV version 2.0-1