LCOV - code coverage report
Current view: top level - EnergyPlus - EarthTube.cc (source / functions) Coverage Total Hit
Test: lcov.output.filtered Lines: 80.5 % 595 479
Test Date: 2025-06-02 07:23:51 Functions: 100.0 % 10 10

            Line data    Source code
       1              : // EnergyPlus, Copyright (c) 1996-2025, The Board of Trustees of the University of Illinois,
       2              : // The Regents of the University of California, through Lawrence Berkeley National Laboratory
       3              : // (subject to receipt of any required approvals from the U.S. Dept. of Energy), Oak Ridge
       4              : // National Laboratory, managed by UT-Battelle, Alliance for Sustainable Energy, LLC, and other
       5              : // contributors. All rights reserved.
       6              : //
       7              : // NOTICE: This Software was developed under funding from the U.S. Department of Energy and the
       8              : // U.S. Government consequently retains certain rights. As such, the U.S. Government has been
       9              : // granted for itself and others acting on its behalf a paid-up, nonexclusive, irrevocable,
      10              : // worldwide license in the Software to reproduce, distribute copies to the public, prepare
      11              : // derivative works, and perform publicly and display publicly, and to permit others to do so.
      12              : //
      13              : // Redistribution and use in source and binary forms, with or without modification, are permitted
      14              : // provided that the following conditions are met:
      15              : //
      16              : // (1) Redistributions of source code must retain the above copyright notice, this list of
      17              : //     conditions and the following disclaimer.
      18              : //
      19              : // (2) Redistributions in binary form must reproduce the above copyright notice, this list of
      20              : //     conditions and the following disclaimer in the documentation and/or other materials
      21              : //     provided with the distribution.
      22              : //
      23              : // (3) Neither the name of the University of California, Lawrence Berkeley National Laboratory,
      24              : //     the University of Illinois, U.S. Dept. of Energy nor the names of its contributors may be
      25              : //     used to endorse or promote products derived from this software without specific prior
      26              : //     written permission.
      27              : //
      28              : // (4) Use of EnergyPlus(TM) Name. If Licensee (i) distributes the software in stand-alone form
      29              : //     without changes from the version obtained under this License, or (ii) Licensee makes a
      30              : //     reference solely to the software portion of its product, Licensee must refer to the
      31              : //     software as "EnergyPlus version X" software, where "X" is the version number Licensee
      32              : //     obtained under this License and may not use a different name for the software. Except as
      33              : //     specifically required in this Section (4), Licensee shall not use in a company name, a
      34              : //     product name, in advertising, publicity, or other promotional activities any name, trade
      35              : //     name, trademark, logo, or other designation of "EnergyPlus", "E+", "e+" or confusingly
      36              : //     similar designation, without the U.S. Department of Energy's prior written consent.
      37              : //
      38              : // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
      39              : // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
      40              : // AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
      41              : // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
      42              : // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
      43              : // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
      44              : // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
      45              : // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
      46              : // POSSIBILITY OF SUCH DAMAGE.
      47              : 
      48              : // C++ Headers
      49              : #include <cmath>
      50              : 
      51              : // ObjexxFCL Headers
      52              : #include <ObjexxFCL/Array.functions.hh>
      53              : #include <ObjexxFCL/Fmath.hh>
      54              : 
      55              : // EnergyPlus Headers
      56              : #include <EnergyPlus/Data/EnergyPlusData.hh>
      57              : #include <EnergyPlus/DataEnvironment.hh>
      58              : #include <EnergyPlus/DataHVACGlobals.hh>
      59              : #include <EnergyPlus/DataHeatBalance.hh>
      60              : #include <EnergyPlus/DataIPShortCuts.hh>
      61              : #include <EnergyPlus/EarthTube.hh>
      62              : #include <EnergyPlus/InputProcessing/InputProcessor.hh>
      63              : #include <EnergyPlus/OutputProcessor.hh>
      64              : #include <EnergyPlus/Psychrometrics.hh>
      65              : #include <EnergyPlus/ScheduleManager.hh>
      66              : #include <EnergyPlus/UtilityRoutines.hh>
      67              : #include <EnergyPlus/ZoneTempPredictorCorrector.hh>
      68              : 
      69              : namespace EnergyPlus::EarthTube {
      70              : // Module containing the data for Earth Tube system
      71              : 
      72              : // MODULE INFORMATION:
      73              : //       AUTHOR         Kwang Ho Lee
      74              : //       DATE WRITTEN   November 2005
      75              : 
      76              : // PURPOSE OF THIS MODULE:
      77              : // To encapsulate the data and algorithyms required to manage the EarthTube System Component
      78              : 
      79              : // REFERENCES:
      80              : // 1. M. Krarti, "Analytical Model to Predict Annual Soil Surface Temperature Variation",
      81              : // Journal of Solar Energy Engineering 117, 1995, pp 91-99
      82              : // 2. K. Labs In: J. Cook, editor, "Passive Cooling",
      83              : // Cambridge Massachusetts, MIT Press, 1989, pp 206-212
      84              : 
      85              : // This is an interesting one.  The actual members of the enum are never explicitly used
      86              : // The enum is used in a getEnumValue call to determine what was found in GetInput
      87              : // The value is then used as an array index to lookup thermal conductivity and such from some std::arrays
      88              : // So the IDE thinks these are unused, and I'm not sure the best way to hint that they sorta aren't
      89              : enum class SoilType
      90              : {
      91              :     Invalid = -1,
      92              :     HeavyAndSat,
      93              :     HeavyAndDamp,
      94              :     HeavyAndDry,
      95              :     LightAndDry,
      96              :     Num
      97              : };
      98              : 
      99              : int totEarthTube = 0;
     100              : 
     101              : constexpr std::array<std::string_view, static_cast<int>(Ventilation::Num)> ventilationNamesUC = {"NATURAL", "INTAKE", "EXHAUST"};
     102              : constexpr std::array<std::string_view, static_cast<int>(SoilType::Num)> soilTypeNamesUC = {
     103              :     "HEAVYANDSATURATED", "HEAVYANDDAMP", "HEAVYANDDRY", "LIGHTANDDRY"};
     104              : constexpr std::array<std::string_view, static_cast<int>(EarthTubeModelType::Num)> solutionTypeNamesUC = {"BASIC", "VERTICAL"};
     105              : 
     106      3757673 : void ManageEarthTube(EnergyPlusData &state)
     107              : {
     108              : 
     109              :     // SUBROUTINE INFORMATION:
     110              :     //       AUTHOR         Kwang Ho Lee
     111              :     //       DATE WRITTEN   November 2005
     112              : 
     113              :     // PURPOSE OF THIS SUBROUTINE:
     114              :     // This subroutine manages the simulation of EarthTube unit.
     115              :     // This driver manages the calls to all of
     116              :     // the other drivers and simulation algorithms.
     117              : 
     118              :     // Obtains and Allocates heat balance related parameters from input file
     119      3757673 :     if (state.dataEarthTube->GetInputFlag) {
     120          768 :         bool ErrorsFound = false;
     121          768 :         GetEarthTube(state, ErrorsFound);
     122          768 :         state.dataEarthTube->GetInputFlag = false;
     123              :     }
     124              : 
     125      3757673 :     if (state.dataEarthTube->EarthTubeSys.empty()) {
     126      3753377 :         return;
     127              :     }
     128              : 
     129         4296 :     initEarthTubeVertical(state);
     130              : 
     131         4296 :     CalcEarthTube(state);
     132              : 
     133         4296 :     ReportEarthTube(state);
     134              : }
     135              : 
     136          768 : void GetEarthTube(EnergyPlusData &state, bool &ErrorsFound) // If errors found in input
     137              : {
     138              : 
     139              :     // SUBROUTINE INFORMATION:
     140              :     //       AUTHOR         Kwang Ho Lee
     141              :     //       DATE WRITTEN   November 2005
     142              : 
     143              :     // PURPOSE OF THIS SUBROUTINE:
     144              :     // This subroutine obtains input data for EarthTube units and
     145              :     // stores it in the EarthTube data structure.
     146              : 
     147              :     static constexpr std::string_view routineName = "GetEarthTube";
     148              : 
     149              :     // SUBROUTINE PARAMETER DEFINITIONS:
     150          768 :     Real64 constexpr EarthTubeTempLimit(100.0); // degrees Celsius
     151              : 
     152              :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
     153              :     int NumAlpha;
     154              :     int NumNumber;
     155              :     int IOStat;
     156              :     int Loop;
     157          768 :     Array1D_bool RepVarSet;
     158              : 
     159          768 :     RepVarSet.dimension(state.dataGlobal->NumOfZones, true);
     160              : 
     161              :     // Following used for reporting
     162          768 :     state.dataEarthTube->ZnRptET.allocate(state.dataGlobal->NumOfZones);
     163              : 
     164          768 :     auto &s_ipsc = state.dataIPShortCut;
     165              : 
     166          768 :     s_ipsc->cCurrentModuleObject = "ZoneEarthtube:Parameters";
     167              : 
     168          768 :     int totEarthTubePars = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, s_ipsc->cCurrentModuleObject);
     169              : 
     170          768 :     state.dataEarthTube->EarthTubePars.allocate(totEarthTubePars);
     171              : 
     172          770 :     for (Loop = 1; Loop <= totEarthTubePars; ++Loop) {
     173            2 :         auto &thisEarthTubePars = state.dataEarthTube->EarthTubePars(Loop);
     174            4 :         state.dataInputProcessing->inputProcessor->getObjectItem(state,
     175            2 :                                                                  s_ipsc->cCurrentModuleObject,
     176              :                                                                  Loop,
     177            2 :                                                                  s_ipsc->cAlphaArgs,
     178              :                                                                  NumAlpha,
     179            2 :                                                                  s_ipsc->rNumericArgs,
     180              :                                                                  NumNumber,
     181              :                                                                  IOStat,
     182            2 :                                                                  s_ipsc->lNumericFieldBlanks,
     183            2 :                                                                  s_ipsc->lAlphaFieldBlanks,
     184            2 :                                                                  s_ipsc->cAlphaFieldNames,
     185            2 :                                                                  s_ipsc->cNumericFieldNames);
     186              : 
     187            2 :         thisEarthTubePars.nameParameters = s_ipsc->cAlphaArgs(1);
     188              :         // Check to make sure name is unique
     189            3 :         for (int otherParams = 1; otherParams < Loop; ++otherParams) {
     190            1 :             if (Util::SameString(thisEarthTubePars.nameParameters, state.dataEarthTube->EarthTubePars(otherParams).nameParameters)) {
     191            0 :                 ShowSevereError(
     192              :                     state,
     193            0 :                     format("{}: {} = {} is not a unique name.", s_ipsc->cCurrentModuleObject, s_ipsc->cAlphaFieldNames(1), s_ipsc->cAlphaArgs(1)));
     194            0 :                 ShowContinueError(state, format("Check the other {} names for a duplicate.", s_ipsc->cCurrentModuleObject));
     195            0 :                 ErrorsFound = true;
     196              :             }
     197              :         }
     198              : 
     199            2 :         thisEarthTubePars.numNodesAbove = s_ipsc->rNumericArgs(1);
     200            2 :         thisEarthTubePars.numNodesBelow = s_ipsc->rNumericArgs(2);
     201            2 :         thisEarthTubePars.dimBoundAbove = s_ipsc->rNumericArgs(3);
     202            2 :         thisEarthTubePars.dimBoundBelow = s_ipsc->rNumericArgs(4);
     203            2 :         thisEarthTubePars.width = s_ipsc->rNumericArgs(5);
     204              :     }
     205              : 
     206          768 :     s_ipsc->cCurrentModuleObject = "ZoneEarthtube";
     207          768 :     totEarthTube = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, s_ipsc->cCurrentModuleObject);
     208              : 
     209          768 :     state.dataEarthTube->EarthTubeSys.allocate(totEarthTube);
     210              : 
     211          774 :     for (Loop = 1; Loop <= totEarthTube; ++Loop) {
     212            6 :         auto &thisEarthTube = state.dataEarthTube->EarthTubeSys(Loop);
     213           12 :         state.dataInputProcessing->inputProcessor->getObjectItem(state,
     214            6 :                                                                  s_ipsc->cCurrentModuleObject,
     215              :                                                                  Loop,
     216            6 :                                                                  s_ipsc->cAlphaArgs,
     217              :                                                                  NumAlpha,
     218            6 :                                                                  s_ipsc->rNumericArgs,
     219              :                                                                  NumNumber,
     220              :                                                                  IOStat,
     221            6 :                                                                  s_ipsc->lNumericFieldBlanks,
     222            6 :                                                                  s_ipsc->lAlphaFieldBlanks,
     223            6 :                                                                  s_ipsc->cAlphaFieldNames,
     224            6 :                                                                  s_ipsc->cNumericFieldNames);
     225              : 
     226            6 :         ErrorObjectHeader eoh{routineName, s_ipsc->cCurrentModuleObject, s_ipsc->cAlphaArgs(1)};
     227              : 
     228              :         // First Alpha is Zone Name
     229            6 :         thisEarthTube.ZonePtr = Util::FindItemInList(s_ipsc->cAlphaArgs(1), state.dataHeatBal->Zone);
     230            6 :         if (thisEarthTube.ZonePtr == 0) {
     231            0 :             ShowSevereError(state, format("{}: {} not found={}", s_ipsc->cCurrentModuleObject, s_ipsc->cAlphaFieldNames(1), s_ipsc->cAlphaArgs(1)));
     232            0 :             ErrorsFound = true;
     233              :         }
     234              : 
     235              :         // Second Alpha is Schedule Name
     236            6 :         if (s_ipsc->lAlphaFieldBlanks(2)) {
     237            0 :             ShowSevereEmptyField(state, eoh, s_ipsc->cAlphaFieldNames(2));
     238            0 :             ErrorsFound = true;
     239            6 :         } else if ((thisEarthTube.availSched = Sched::GetSchedule(state, s_ipsc->cAlphaArgs(2))) == nullptr) {
     240            0 :             ShowSevereItemNotFound(state, eoh, s_ipsc->cAlphaFieldNames(2), s_ipsc->cAlphaArgs(2));
     241            0 :             ErrorsFound = true;
     242              :         }
     243              : 
     244              :         // Overall parameters and their limits
     245            6 :         thisEarthTube.DesignLevel = s_ipsc->rNumericArgs(1);
     246              : 
     247            6 :         thisEarthTube.MinTemperature = s_ipsc->rNumericArgs(2);
     248            6 :         if ((thisEarthTube.MinTemperature < -EarthTubeTempLimit) || (thisEarthTube.MinTemperature > EarthTubeTempLimit)) {
     249            0 :             ShowSevereError(state,
     250            0 :                             format("{}: {}={} must have a minimum temperature between -{:.0R}C and {:.0R}C",
     251            0 :                                    s_ipsc->cCurrentModuleObject,
     252            0 :                                    s_ipsc->cAlphaFieldNames(1),
     253            0 :                                    s_ipsc->cAlphaArgs(1),
     254              :                                    EarthTubeTempLimit,
     255              :                                    EarthTubeTempLimit));
     256            0 :             ShowContinueError(state, format("Entered value={:.0R}", thisEarthTube.MinTemperature));
     257            0 :             ErrorsFound = true;
     258              :         }
     259              : 
     260            6 :         thisEarthTube.MaxTemperature = s_ipsc->rNumericArgs(3);
     261            6 :         if ((thisEarthTube.MaxTemperature < -EarthTubeTempLimit) || (thisEarthTube.MaxTemperature > EarthTubeTempLimit)) {
     262            0 :             ShowSevereError(state,
     263            0 :                             format("{}: {}={} must have a maximum temperature between -{:.0R}C and {:.0R}C",
     264            0 :                                    s_ipsc->cCurrentModuleObject,
     265            0 :                                    s_ipsc->cAlphaFieldNames(1),
     266            0 :                                    s_ipsc->cAlphaArgs(1),
     267              :                                    EarthTubeTempLimit,
     268              :                                    EarthTubeTempLimit));
     269            0 :             ShowContinueError(state, format("Entered value={:.0R}", thisEarthTube.MaxTemperature));
     270            0 :             ErrorsFound = true;
     271              :         }
     272              : 
     273            6 :         thisEarthTube.DelTemperature = s_ipsc->rNumericArgs(4); //  3/12/03  Negative del temp now allowed COP
     274              : 
     275              :         // if we have a blank, then just set it to the Natural type, otherwise, search on it
     276            6 :         if (s_ipsc->cAlphaArgs(3).empty()) {
     277            0 :             thisEarthTube.FanType = Ventilation::Natural;
     278              :         } else {
     279            6 :             thisEarthTube.FanType = static_cast<Ventilation>(getEnumValue(ventilationNamesUC, s_ipsc->cAlphaArgs(3)));
     280            6 :             if (thisEarthTube.FanType == Ventilation::Invalid) {
     281            0 :                 ShowSevereInvalidKey(state, eoh, s_ipsc->cAlphaFieldNames(3), s_ipsc->cAlphaArgs(3));
     282            0 :                 ErrorsFound = true;
     283              :             }
     284              :         }
     285              : 
     286            6 :         thisEarthTube.FanPressure = s_ipsc->rNumericArgs(5);
     287            6 :         if (thisEarthTube.FanPressure < 0.0) {
     288            0 :             ShowSevereError(state,
     289            0 :                             format("{}: {}={}, {} must be positive, entered value={:.2R}",
     290            0 :                                    s_ipsc->cCurrentModuleObject,
     291            0 :                                    s_ipsc->cAlphaFieldNames(1),
     292            0 :                                    s_ipsc->cAlphaArgs(1),
     293            0 :                                    s_ipsc->cNumericFieldNames(5),
     294            0 :                                    thisEarthTube.FanPressure));
     295            0 :             ErrorsFound = true;
     296              :         }
     297              : 
     298            6 :         thisEarthTube.FanEfficiency = s_ipsc->rNumericArgs(6);
     299            6 :         if ((thisEarthTube.FanEfficiency <= 0.0) || (thisEarthTube.FanEfficiency > 1.0)) {
     300            0 :             ShowSevereError(state,
     301            0 :                             format("{}: {}={}, {} must be greater than zero and less than or equal to one, entered value={:.2R}",
     302            0 :                                    s_ipsc->cCurrentModuleObject,
     303            0 :                                    s_ipsc->cAlphaFieldNames(1),
     304            0 :                                    s_ipsc->cAlphaArgs(1),
     305            0 :                                    s_ipsc->cNumericFieldNames(6),
     306            0 :                                    thisEarthTube.FanEfficiency));
     307            0 :             ErrorsFound = true;
     308              :         }
     309              : 
     310            6 :         thisEarthTube.r1 = s_ipsc->rNumericArgs(7);
     311            6 :         if (thisEarthTube.r1 <= 0.0) {
     312            0 :             ShowSevereError(state,
     313            0 :                             format("{}: {}={}, {} must be positive, entered value={:.2R}",
     314            0 :                                    s_ipsc->cCurrentModuleObject,
     315            0 :                                    s_ipsc->cAlphaFieldNames(1),
     316            0 :                                    s_ipsc->cAlphaArgs(1),
     317            0 :                                    s_ipsc->cNumericFieldNames(7),
     318            0 :                                    thisEarthTube.r1));
     319            0 :             ErrorsFound = true;
     320              :         }
     321              : 
     322            6 :         thisEarthTube.r2 = s_ipsc->rNumericArgs(8);
     323            6 :         if (thisEarthTube.r2 <= 0.0) {
     324            0 :             ShowSevereError(state,
     325            0 :                             format("{}: {}={}, {} must be positive, entered value={:.2R}",
     326            0 :                                    s_ipsc->cCurrentModuleObject,
     327            0 :                                    s_ipsc->cAlphaFieldNames(1),
     328            0 :                                    s_ipsc->cAlphaArgs(1),
     329            0 :                                    s_ipsc->cNumericFieldNames(8),
     330            0 :                                    thisEarthTube.r2));
     331            0 :             ErrorsFound = true;
     332              :         }
     333              : 
     334            6 :         thisEarthTube.r3 = 2.0 * thisEarthTube.r1;
     335              : 
     336            6 :         thisEarthTube.PipeLength = s_ipsc->rNumericArgs(9);
     337            6 :         if (thisEarthTube.PipeLength <= 0.0) {
     338            0 :             ShowSevereError(state,
     339            0 :                             format("{}: {}={}, {} must be positive, entered value={:.2R}",
     340            0 :                                    s_ipsc->cCurrentModuleObject,
     341            0 :                                    s_ipsc->cAlphaFieldNames(1),
     342            0 :                                    s_ipsc->cAlphaArgs(1),
     343            0 :                                    s_ipsc->cNumericFieldNames(9),
     344            0 :                                    thisEarthTube.PipeLength));
     345            0 :             ErrorsFound = true;
     346              :         }
     347              : 
     348            6 :         thisEarthTube.PipeThermCond = s_ipsc->rNumericArgs(10);
     349            6 :         if (thisEarthTube.PipeThermCond <= 0.0) {
     350            0 :             ShowSevereError(state,
     351            0 :                             format("{}: {}={}, {} must be positive, entered value={:.2R}",
     352            0 :                                    s_ipsc->cCurrentModuleObject,
     353            0 :                                    s_ipsc->cAlphaFieldNames(1),
     354            0 :                                    s_ipsc->cAlphaArgs(1),
     355            0 :                                    s_ipsc->cNumericFieldNames(10),
     356            0 :                                    thisEarthTube.PipeThermCond));
     357            0 :             ErrorsFound = true;
     358              :         }
     359              : 
     360            6 :         thisEarthTube.z = s_ipsc->rNumericArgs(11);
     361            6 :         if (thisEarthTube.z <= 0.0) {
     362            0 :             ShowSevereError(state,
     363            0 :                             format("{}: {}={}, {} must be positive, entered value={:.2R}",
     364            0 :                                    s_ipsc->cCurrentModuleObject,
     365            0 :                                    s_ipsc->cAlphaFieldNames(1),
     366            0 :                                    s_ipsc->cAlphaArgs(1),
     367            0 :                                    s_ipsc->cNumericFieldNames(11),
     368            0 :                                    thisEarthTube.z));
     369            0 :             ErrorsFound = true;
     370              :         }
     371            6 :         if (thisEarthTube.z <= (thisEarthTube.r1 + thisEarthTube.r2 + thisEarthTube.r3)) {
     372              :             // Note that code in initEarthTubeVertical assumes that this check remains in place--if this ever gets changed,
     373              :             // code in initEarthTubeVertical must be modified
     374            0 :             ShowSevereError(state,
     375            0 :                             format("{}: {}={}, {} must be greater than 3*{} + {} entered value={:.2R} ref sum={:.2R}",
     376            0 :                                    s_ipsc->cCurrentModuleObject,
     377            0 :                                    s_ipsc->cAlphaFieldNames(1),
     378            0 :                                    s_ipsc->cAlphaArgs(1),
     379            0 :                                    s_ipsc->cNumericFieldNames(11),
     380            0 :                                    s_ipsc->cNumericFieldNames(7),
     381            0 :                                    s_ipsc->cNumericFieldNames(8),
     382            0 :                                    thisEarthTube.z,
     383            0 :                                    thisEarthTube.r1 + thisEarthTube.r2 + thisEarthTube.r3));
     384            0 :             ErrorsFound = true;
     385              :         }
     386              : 
     387            6 :         SoilType soilType = static_cast<SoilType>(getEnumValue(soilTypeNamesUC, s_ipsc->cAlphaArgs(4)));
     388            6 :         constexpr std::array<Real64, static_cast<int>(SoilType::Num)> thermalDiffusivity = {0.0781056, 0.055728, 0.0445824, 0.024192};
     389            6 :         constexpr std::array<Real64, static_cast<int>(SoilType::Num)> thermalConductivity = {2.42, 1.3, 0.865, 0.346};
     390            6 :         if (soilType == SoilType::Invalid) {
     391            0 :             ShowSevereInvalidKey(state, eoh, s_ipsc->cAlphaFieldNames(4), s_ipsc->cAlphaArgs(4));
     392            0 :             ErrorsFound = true;
     393              :         } else {
     394            6 :             thisEarthTube.SoilThermDiff = thermalDiffusivity[static_cast<int>(soilType)];
     395            6 :             thisEarthTube.SoilThermCond = thermalConductivity[static_cast<int>(soilType)];
     396              :         }
     397              : 
     398            6 :         thisEarthTube.AverSoilSurTemp = s_ipsc->rNumericArgs(12);
     399            6 :         thisEarthTube.ApmlSoilSurTemp = s_ipsc->rNumericArgs(13);
     400            6 :         thisEarthTube.SoilSurPhaseConst = int(s_ipsc->rNumericArgs(14));
     401              : 
     402              :         // Override any user input for cases where natural ventilation is being used
     403            6 :         if (thisEarthTube.FanType == Ventilation::Natural) {
     404            2 :             thisEarthTube.FanPressure = 0.0;
     405            2 :             thisEarthTube.FanEfficiency = 1.0;
     406              :         }
     407              : 
     408            6 :         thisEarthTube.ConstantTermCoef = s_ipsc->rNumericArgs(15);
     409            6 :         thisEarthTube.TemperatureTermCoef = s_ipsc->rNumericArgs(16);
     410            6 :         thisEarthTube.VelocityTermCoef = s_ipsc->rNumericArgs(17);
     411            6 :         thisEarthTube.VelocitySQTermCoef = s_ipsc->rNumericArgs(18);
     412              : 
     413              :         // cAlphaArgs(5)--Model type: basic or vertical
     414              :         // only process cAlphaArgs(6) if cAlphaArgs(5) is "Vertical"
     415            6 :         if (s_ipsc->cAlphaArgs(5).empty()) {
     416            0 :             thisEarthTube.ModelType = EarthTubeModelType::Basic;
     417              :         } else {
     418            6 :             thisEarthTube.ModelType = static_cast<EarthTubeModelType>(getEnumValue(solutionTypeNamesUC, s_ipsc->cAlphaArgs(5)));
     419            6 :             if (thisEarthTube.ModelType == EarthTubeModelType::Invalid) {
     420            0 :                 ShowSevereInvalidKey(state, eoh, s_ipsc->cAlphaFieldNames(5), s_ipsc->cAlphaArgs(5));
     421            0 :                 ErrorsFound = true;
     422              :             }
     423              :         }
     424              : 
     425            6 :         if (thisEarthTube.ModelType == EarthTubeModelType::Vertical) {
     426            2 :             thisEarthTube.r3 = 0.0; // Vertical model does not use this parameter--reset to zero (keep because r3=0 necessary so Rs=0 in calc routine)
     427              :             // Process the parameters based on the name (link via index)
     428            2 :             thisEarthTube.vertParametersPtr = 0;
     429            3 :             for (int parIndex = 1; parIndex <= totEarthTubePars; ++parIndex) {
     430            3 :                 if (Util::SameString(s_ipsc->cAlphaArgs(6), state.dataEarthTube->EarthTubePars(parIndex).nameParameters)) {
     431            2 :                     thisEarthTube.vertParametersPtr = parIndex;
     432            2 :                     break;
     433              :                 }
     434              :             }
     435            2 :             if (thisEarthTube.vertParametersPtr == 0) { // didn't find a match
     436            0 :                 ShowSevereItemNotFound(state, eoh, s_ipsc->cAlphaFieldNames(6), s_ipsc->cAlphaArgs(6));
     437            0 :                 ErrorsFound = true;
     438              :             }
     439              :         }
     440              : 
     441            6 :         if (thisEarthTube.ZonePtr > 0) {
     442            6 :             if (RepVarSet(thisEarthTube.ZonePtr)) {
     443            6 :                 RepVarSet(thisEarthTube.ZonePtr) = false;
     444            6 :                 auto &zone = state.dataHeatBal->Zone(thisEarthTube.ZonePtr);
     445            6 :                 auto &thisZnRptET = state.dataEarthTube->ZnRptET(thisEarthTube.ZonePtr);
     446              : 
     447           12 :                 SetupOutputVariable(state,
     448              :                                     "Earth Tube Zone Sensible Cooling Energy",
     449              :                                     Constant::Units::J,
     450            6 :                                     thisZnRptET.EarthTubeHeatLoss,
     451              :                                     OutputProcessor::TimeStepType::System,
     452              :                                     OutputProcessor::StoreType::Sum,
     453            6 :                                     zone.Name);
     454           12 :                 SetupOutputVariable(state,
     455              :                                     "Earth Tube Zone Sensible Cooling Rate",
     456              :                                     Constant::Units::W,
     457            6 :                                     thisZnRptET.EarthTubeHeatLossRate,
     458              :                                     OutputProcessor::TimeStepType::System,
     459              :                                     OutputProcessor::StoreType::Average,
     460            6 :                                     zone.Name);
     461           12 :                 SetupOutputVariable(state,
     462              :                                     "Earth Tube Zone Sensible Heating Energy",
     463              :                                     Constant::Units::J,
     464            6 :                                     thisZnRptET.EarthTubeHeatGain,
     465              :                                     OutputProcessor::TimeStepType::System,
     466              :                                     OutputProcessor::StoreType::Sum,
     467            6 :                                     zone.Name);
     468           12 :                 SetupOutputVariable(state,
     469              :                                     "Earth Tube Zone Sensible Heating Rate",
     470              :                                     Constant::Units::W,
     471            6 :                                     thisZnRptET.EarthTubeHeatGainRate,
     472              :                                     OutputProcessor::TimeStepType::System,
     473              :                                     OutputProcessor::StoreType::Average,
     474            6 :                                     zone.Name);
     475           12 :                 SetupOutputVariable(state,
     476              :                                     "Earth Tube Air Flow Volume",
     477              :                                     Constant::Units::m3,
     478            6 :                                     thisZnRptET.EarthTubeVolume,
     479              :                                     OutputProcessor::TimeStepType::System,
     480              :                                     OutputProcessor::StoreType::Sum,
     481            6 :                                     zone.Name);
     482           12 :                 SetupOutputVariable(state,
     483              :                                     "Earth Tube Current Density Air Volume Flow Rate",
     484              :                                     Constant::Units::m3_s,
     485            6 :                                     thisZnRptET.EarthTubeVolFlowRate,
     486              :                                     OutputProcessor::TimeStepType::System,
     487              :                                     OutputProcessor::StoreType::Average,
     488            6 :                                     zone.Name);
     489           12 :                 SetupOutputVariable(state,
     490              :                                     "Earth Tube Standard Density Air Volume Flow Rate",
     491              :                                     Constant::Units::m3_s,
     492            6 :                                     thisZnRptET.EarthTubeVolFlowRateStd,
     493              :                                     OutputProcessor::TimeStepType::System,
     494              :                                     OutputProcessor::StoreType::Average,
     495            6 :                                     zone.Name);
     496           12 :                 SetupOutputVariable(state,
     497              :                                     "Earth Tube Air Flow Mass",
     498              :                                     Constant::Units::kg,
     499            6 :                                     thisZnRptET.EarthTubeMass,
     500              :                                     OutputProcessor::TimeStepType::System,
     501              :                                     OutputProcessor::StoreType::Sum,
     502            6 :                                     zone.Name);
     503           12 :                 SetupOutputVariable(state,
     504              :                                     "Earth Tube Air Mass Flow Rate",
     505              :                                     Constant::Units::kg_s,
     506            6 :                                     thisZnRptET.EarthTubeMassFlowRate,
     507              :                                     OutputProcessor::TimeStepType::System,
     508              :                                     OutputProcessor::StoreType::Average,
     509            6 :                                     zone.Name);
     510           12 :                 SetupOutputVariable(state,
     511              :                                     "Earth Tube Water Mass Flow Rate",
     512              :                                     Constant::Units::kg_s,
     513            6 :                                     thisZnRptET.EarthTubeWaterMassFlowRate,
     514              :                                     OutputProcessor::TimeStepType::System,
     515              :                                     OutputProcessor::StoreType::Average,
     516            6 :                                     zone.Name);
     517           12 :                 SetupOutputVariable(state,
     518              :                                     "Earth Tube Fan Electricity Energy",
     519              :                                     Constant::Units::J,
     520            6 :                                     thisZnRptET.EarthTubeFanElec,
     521              :                                     OutputProcessor::TimeStepType::System,
     522              :                                     OutputProcessor::StoreType::Sum,
     523            6 :                                     zone.Name,
     524              :                                     Constant::eResource::Electricity,
     525              :                                     OutputProcessor::Group::Building);
     526           12 :                 SetupOutputVariable(state,
     527              :                                     "Earth Tube Fan Electricity Rate",
     528              :                                     Constant::Units::W,
     529            6 :                                     thisZnRptET.EarthTubeFanElecPower,
     530              :                                     OutputProcessor::TimeStepType::System,
     531              :                                     OutputProcessor::StoreType::Average,
     532            6 :                                     zone.Name);
     533           12 :                 SetupOutputVariable(state,
     534              :                                     "Earth Tube Zone Inlet Air Temperature",
     535              :                                     Constant::Units::C,
     536            6 :                                     thisZnRptET.EarthTubeAirTemp,
     537              :                                     OutputProcessor::TimeStepType::System,
     538              :                                     OutputProcessor::StoreType::Average,
     539            6 :                                     zone.Name);
     540           12 :                 SetupOutputVariable(state,
     541              :                                     "Earth Tube Ground Interface Temperature",
     542              :                                     Constant::Units::C,
     543            6 :                                     thisEarthTube.GroundTempt,
     544              :                                     OutputProcessor::TimeStepType::System,
     545              :                                     OutputProcessor::StoreType::Average,
     546            6 :                                     zone.Name);
     547           12 :                 SetupOutputVariable(state,
     548              :                                     "Earth Tube Outdoor Air Heat Transfer Rate",
     549              :                                     Constant::Units::W,
     550            6 :                                     thisZnRptET.EarthTubeOATreatmentPower,
     551              :                                     OutputProcessor::TimeStepType::System,
     552              :                                     OutputProcessor::StoreType::Average,
     553            6 :                                     zone.Name);
     554           12 :                 SetupOutputVariable(state,
     555              :                                     "Earth Tube Zone Inlet Wet Bulb Temperature",
     556              :                                     Constant::Units::C,
     557            6 :                                     thisZnRptET.EarthTubeWetBulbTemp,
     558              :                                     OutputProcessor::TimeStepType::System,
     559              :                                     OutputProcessor::StoreType::Average,
     560            6 :                                     zone.Name);
     561           12 :                 SetupOutputVariable(state,
     562              :                                     "Earth Tube Zone Inlet Humidity Ratio",
     563              :                                     Constant::Units::kgWater_kgDryAir,
     564            6 :                                     thisZnRptET.EarthTubeHumRat,
     565              :                                     OutputProcessor::TimeStepType::System,
     566              :                                     OutputProcessor::StoreType::Average,
     567            6 :                                     zone.Name);
     568              :             }
     569              :         }
     570              :     }
     571              : 
     572          768 :     CheckEarthTubesInZones(state, s_ipsc->cAlphaArgs(1), s_ipsc->cCurrentModuleObject, ErrorsFound);
     573              : 
     574          768 :     if (ErrorsFound) {
     575            0 :         ShowFatalError(state, format("{}: Errors getting input.  Program terminates.", s_ipsc->cCurrentModuleObject));
     576              :     }
     577          768 : }
     578              : 
     579          768 : void CheckEarthTubesInZones(EnergyPlusData &state,
     580              :                             std::string const &ZoneName, // name of zone for error reporting
     581              :                             std::string_view FieldName,  // name of earth tube in input
     582              :                             bool &ErrorsFound            // Found a problem
     583              : )
     584              : {
     585              :     // Check to make sure there is only one earth tube statement per zone
     586          768 :     int numEarthTubes = (int)state.dataEarthTube->EarthTubeSys.size();
     587          772 :     for (int Loop = 1; Loop <= numEarthTubes - 1; ++Loop) {
     588           10 :         for (int Loop1 = Loop + 1; Loop1 <= numEarthTubes; ++Loop1) {
     589            6 :             if (state.dataEarthTube->EarthTubeSys(Loop).ZonePtr == state.dataEarthTube->EarthTubeSys(Loop1).ZonePtr) {
     590            0 :                 ShowSevereError(state, format("{} has more than one {} associated with it.", ZoneName, FieldName));
     591            0 :                 ShowContinueError(state, format("Only one {} is allowed per zone.  Check the definitions of {}", FieldName, FieldName));
     592            0 :                 ShowContinueError(state, "in your input file and make sure that there is only one defined for each zone.");
     593            0 :                 ErrorsFound = true;
     594              :             }
     595              :         }
     596              :     }
     597          768 : }
     598              : 
     599         4296 : void initEarthTubeVertical(EnergyPlusData &state)
     600              : {
     601         4296 :     if (state.dataEarthTube->initFirstTime) {
     602            2 :         state.dataEarthTube->initFirstTime = false;
     603            8 :         for (int etNum = 1; etNum <= totEarthTube; ++etNum) {
     604            6 :             auto &thisEarthTube = state.dataEarthTube->EarthTubeSys(etNum);
     605            6 :             if (thisEarthTube.ModelType != EarthTubeModelType::Vertical) {
     606            4 :                 continue; // Skip earth tubes that do not use vertical solution
     607              :             }
     608            2 :             auto &thisEarthTubeParams = state.dataEarthTube->EarthTubePars(thisEarthTube.vertParametersPtr);
     609            2 :             thisEarthTube.totNodes = thisEarthTubeParams.numNodesAbove + thisEarthTubeParams.numNodesBelow + 1;
     610            2 :             thisEarthTube.aCoeff.resize(thisEarthTube.totNodes);
     611            2 :             thisEarthTube.bCoeff.resize(thisEarthTube.totNodes);
     612            2 :             thisEarthTube.cCoeff.resize(thisEarthTube.totNodes);
     613            2 :             thisEarthTube.cCoeff0.resize(thisEarthTube.totNodes);
     614            2 :             thisEarthTube.dCoeff.resize(thisEarthTube.totNodes);
     615            2 :             thisEarthTube.cPrime.resize(thisEarthTube.totNodes);
     616            2 :             thisEarthTube.dPrime.resize(thisEarthTube.totNodes);
     617            2 :             thisEarthTube.cPrime0.resize(thisEarthTube.totNodes);
     618            2 :             thisEarthTube.tCurrent.resize(thisEarthTube.totNodes);
     619            2 :             thisEarthTube.tLast.resize(thisEarthTube.totNodes);
     620            2 :             thisEarthTube.depthNode.resize(thisEarthTube.totNodes);
     621            2 :             thisEarthTube.tUndist.resize(thisEarthTube.totNodes);
     622            2 :             Real64 thickBase = (thisEarthTube.z - 3.0 * thisEarthTube.r1);
     623            2 :             Real64 thickTop = thickBase * thisEarthTubeParams.dimBoundAbove / float(thisEarthTubeParams.numNodesAbove);
     624            2 :             Real64 thickBottom = thickBase * thisEarthTubeParams.dimBoundBelow / float(thisEarthTubeParams.numNodesBelow);
     625            2 :             Real64 thickEarthTube = 4.0 * thisEarthTube.r1;
     626            2 :             Real64 deltat = state.dataGlobal->TimeStepZone;
     627            2 :             Real64 thermDiff = thisEarthTube.SoilThermDiff / Constant::rHoursInDay; // convert to "per hour" from "per day"
     628              : 
     629              :             // Node equations determine the _Coeff terms--see Engineering Referenve for details on these equation types
     630              :             // Note that node numbers are shifted for c++ arrays that go from 0 to numNodes-1.
     631              :             // Node Type 1 (Top Node)
     632            2 :             Real64 commonTerm = thermDiff * deltat / (thickTop * thickTop);
     633            2 :             thisEarthTube.aCoeff[0] = 0.0; // no a0 value
     634            2 :             thisEarthTube.bCoeff[0] = 1.0 + 3.0 * commonTerm;
     635            2 :             thisEarthTube.cCoeff[0] = -1.0 * commonTerm;
     636            2 :             thisEarthTube.dMult0 = 2.0 * commonTerm; // does not include temperatures (upper boundary or previous time step)--added later
     637              :             // Node Type 2 (Generic Top Section Node)
     638           13 :             for (int nodeNum = 1; nodeNum <= thisEarthTubeParams.numNodesAbove - 2; ++nodeNum) {
     639           11 :                 thisEarthTube.aCoeff[nodeNum] = -1.0 * commonTerm;
     640           11 :                 thisEarthTube.bCoeff[nodeNum] = 1.0 + 2.0 * commonTerm;
     641           11 :                 thisEarthTube.cCoeff[nodeNum] = -1.0 * commonTerm;
     642              :             }
     643              :             // Node Type 3 (Last Top Section Node)
     644            2 :             int thisNode = thisEarthTubeParams.numNodesAbove - 1;
     645            2 :             Real64 commonTerm2 = 2.0 * thermDiff * deltat / (thickTop + thickEarthTube) / thickTop;
     646            2 :             thisEarthTube.aCoeff[thisNode] = -1.0 * commonTerm;
     647            2 :             thisEarthTube.bCoeff[thisNode] = 1.0 + commonTerm + commonTerm2;
     648            2 :             thisEarthTube.cCoeff[thisNode] = -1.0 * commonTerm2;
     649              :             // Node Type 4 (Earth Tube Node)
     650            2 :             thisNode = thisEarthTubeParams.numNodesAbove;
     651            2 :             commonTerm = 2.0 * thermDiff * deltat / (thickTop + thickEarthTube) / thickEarthTube;
     652            2 :             commonTerm2 = 2.0 * thermDiff * deltat / (thickBottom + thickEarthTube) / thickEarthTube;
     653            2 :             thisEarthTube.aCoeff[thisNode] = -1.0 * commonTerm;
     654            2 :             thisEarthTube.bCoeff[thisNode] = 1.0 + commonTerm + commonTerm2; // does not include earth tube air flow term--added later
     655            2 :             thisEarthTube.cCoeff[thisNode] = -1.0 * commonTerm2;
     656              :             // Node Type 5 (First Bottom Section Node)
     657            2 :             thisNode = thisEarthTubeParams.numNodesAbove + 1;
     658            2 :             commonTerm = thermDiff * deltat / (thickBottom * thickBottom);
     659            2 :             commonTerm2 = 2.0 * thermDiff * deltat / (thickBottom + thickEarthTube) / thickBottom;
     660            2 :             thisEarthTube.aCoeff[thisNode] = -1.0 * commonTerm2;
     661            2 :             thisEarthTube.bCoeff[thisNode] = 1.0 + commonTerm + commonTerm2;
     662            2 :             thisEarthTube.cCoeff[thisNode] = -1.0 * commonTerm;
     663              :             // Node Type 6 (Generic Bottom Section Node)
     664           11 :             for (int nodeNum = thisNode + 1; nodeNum <= thisEarthTube.totNodes - 2; ++nodeNum) {
     665            9 :                 thisEarthTube.aCoeff[nodeNum] = -1.0 * commonTerm;
     666            9 :                 thisEarthTube.bCoeff[nodeNum] = 1.0 + 2.0 * commonTerm;
     667            9 :                 thisEarthTube.cCoeff[nodeNum] = -1.0 * commonTerm;
     668              :             }
     669              :             // Node Type 7 (Last Bottom Section Node, i.e. Last Node)
     670            2 :             thisNode = thisEarthTube.totNodes - 1; // shifted due to c++ arrays that go from 0 to numNodes-1
     671            2 :             thisEarthTube.aCoeff[thisNode] = -1.0 * commonTerm;
     672            2 :             thisEarthTube.bCoeff[thisNode] = 1.0 + 3.0 * commonTerm;
     673            2 :             thisEarthTube.cCoeff[thisNode] = 0.0;    // no cN value
     674            2 :             thisEarthTube.dMultN = 2.0 * commonTerm; // does not include previous temperature and earth tube air flow terms--added later
     675              : 
     676              :             // Initialize node temperatures using undisturbed temperature equation and node depths
     677              :             // First, nodes above the earth tube
     678            2 :             thisEarthTube.depthNode[thisEarthTubeParams.numNodesAbove - 1] = thisEarthTube.z - 0.5 * (thickEarthTube + thickTop);
     679           15 :             for (int nodeNum = thisEarthTubeParams.numNodesAbove - 2; nodeNum >= 0; --nodeNum) {
     680           13 :                 thisEarthTube.depthNode[nodeNum] = thisEarthTube.depthNode[nodeNum + 1] - thickTop;
     681              :             }
     682              :             // Now, the earth tube node
     683            2 :             thisEarthTube.depthNode[thisEarthTubeParams.numNodesAbove] = thisEarthTube.z;
     684              :             // Finally the nodes below the earth tube
     685            2 :             thisEarthTube.depthNode[thisEarthTubeParams.numNodesAbove + 1] = thisEarthTube.z + 0.5 * (thickEarthTube + thickBottom);
     686           13 :             for (int nodeNumBelow = 2; nodeNumBelow <= thisEarthTubeParams.numNodesBelow; ++nodeNumBelow) {
     687           11 :                 int nodeNum = thisEarthTubeParams.numNodesAbove + nodeNumBelow;
     688           11 :                 thisEarthTube.depthNode[nodeNum] = thisEarthTube.depthNode[nodeNum - 1] + thickBottom;
     689              :             }
     690            2 :             thisEarthTube.depthUpperBound = thisEarthTube.depthNode[0] - 0.5 * thickTop;
     691            2 :             thisEarthTube.depthLowerBound = thisEarthTube.depthNode[thisEarthTube.totNodes - 1] + 0.5 * thickBottom;
     692              : 
     693              :             // Calculate constant part of air flow term at earth tube node.  Note that diffusiity/conductivity = 1/(density*specific_heat)
     694            2 :             thisEarthTube.airFlowCoeff = state.dataGlobal->TimeStepZone * thermDiff / thisEarthTube.SoilThermCond / thickEarthTube /
     695            2 :                                          thisEarthTubeParams.width / thisEarthTube.PipeLength;
     696              : 
     697              :             // Calculate some initial values in the Thomas algorithm.  This includes c' when effectiveness is zero (entire c').
     698              :             // For any other effectiveness, c' will be the same as c' when effectiveness for is zero for the nodes above the earth
     699              :             // tube.  So, the c' for effectiveness of zero (cPrime0) can be reused as needed.
     700           32 :             for (int nodeNum = 0; nodeNum <= thisEarthTube.totNodes - 1; ++nodeNum) {
     701           30 :                 thisEarthTube.cCoeff0[nodeNum] = thisEarthTube.cCoeff[nodeNum];
     702              :             }
     703            2 :             thisEarthTube.initCPrime0();
     704              : 
     705            2 :             auto &zone = state.dataHeatBal->Zone(thisEarthTube.ZonePtr);
     706           32 :             for (int nodeNum = 1; nodeNum <= thisEarthTube.totNodes; ++nodeNum) {
     707           90 :                 SetupOutputVariable(state,
     708           60 :                                     format("Earth Tube Node Temperature {}", nodeNum),
     709              :                                     Constant::Units::C,
     710           30 :                                     thisEarthTube.tCurrent[nodeNum - 1],
     711              :                                     OutputProcessor::TimeStepType::Zone,
     712              :                                     OutputProcessor::StoreType::Average,
     713           30 :                                     zone.Name);
     714           90 :                 SetupOutputVariable(state,
     715           60 :                                     format("Earth Tube Undisturbed Ground Temperature {}", nodeNum),
     716              :                                     Constant::Units::C,
     717           30 :                                     thisEarthTube.tUndist[nodeNum - 1],
     718              :                                     OutputProcessor::TimeStepType::Zone,
     719              :                                     OutputProcessor::StoreType::Average,
     720           30 :                                     zone.Name);
     721              :             }
     722            4 :             SetupOutputVariable(state,
     723              :                                 "Earth Tube Upper Boundary Ground Temperature",
     724              :                                 Constant::Units::C,
     725            2 :                                 thisEarthTube.tUpperBound,
     726              :                                 OutputProcessor::TimeStepType::Zone,
     727              :                                 OutputProcessor::StoreType::Average,
     728            2 :                                 zone.Name);
     729            4 :             SetupOutputVariable(state,
     730              :                                 "Earth Tube Lower Boundary Ground Temperature",
     731              :                                 Constant::Units::C,
     732            2 :                                 thisEarthTube.tLowerBound,
     733              :                                 OutputProcessor::TimeStepType::Zone,
     734              :                                 OutputProcessor::StoreType::Average,
     735            2 :                                 zone.Name);
     736              :         }
     737              :     } // ...end of firstTimeInits block
     738              : 
     739              :     Real64 timeElapsedLoc =
     740         4296 :         state.dataGlobal->HourOfDay + state.dataGlobal->TimeStep * state.dataGlobal->TimeStepZone + state.dataHVACGlobal->SysTimeElapsed;
     741         4296 :     if (state.dataEarthTube->timeElapsed !=
     742              :         timeElapsedLoc) { // time changed, update last with "current", avoids duplicate initializations and improper updates
     743         4171 :         if (state.dataGlobal->BeginDayFlag || state.dataGlobal->BeginEnvrnFlag) {
     744              :             // update all of the undisturbed temperatures (only need to do this once per day because the equation only changes as the day changes
     745          228 :             for (int etNum = 1; etNum <= totEarthTube; ++etNum) {
     746          171 :                 auto &thisEarthTube = state.dataEarthTube->EarthTubeSys(etNum);
     747          171 :                 if (thisEarthTube.ModelType != EarthTubeModelType::Vertical) {
     748          113 :                     continue; // Skip earth tubes that do not use vertical solution
     749              :                 }
     750           58 :                 thisEarthTube.tUpperBound = thisEarthTube.calcUndisturbedGroundTemperature(state, thisEarthTube.depthUpperBound);
     751           58 :                 thisEarthTube.tLowerBound = thisEarthTube.calcUndisturbedGroundTemperature(state, thisEarthTube.depthLowerBound);
     752          928 :                 for (int nodeNum = 0; nodeNum <= thisEarthTube.totNodes - 1; ++nodeNum) {
     753          870 :                     thisEarthTube.tUndist[nodeNum] = thisEarthTube.calcUndisturbedGroundTemperature(state, thisEarthTube.depthNode[nodeNum]);
     754              :                 }
     755              :             }
     756              :         } // ...end of BeginDayFlag block
     757              : 
     758         8321 :         if (state.dataGlobal->BeginEnvrnFlag ||
     759         4150 :             (!state.dataGlobal->WarmupFlag && state.dataGlobal->BeginDayFlag && state.dataGlobal->DayOfSim == 1)) {
     760          108 :             for (int etNum = 1; etNum <= totEarthTube; ++etNum) {
     761           81 :                 auto &thisEarthTube = state.dataEarthTube->EarthTubeSys(etNum);
     762           81 :                 if (thisEarthTube.ModelType != EarthTubeModelType::Vertical) {
     763           53 :                     continue; // Skip earth tubes that do not use vertical solution
     764              :                 }
     765          448 :                 for (int nodeNum = 0; nodeNum <= thisEarthTube.totNodes - 1; ++nodeNum) {
     766          420 :                     thisEarthTube.tLast[nodeNum] = thisEarthTube.tUndist[nodeNum];
     767          420 :                     thisEarthTube.tCurrent[nodeNum] = thisEarthTube.tLast[nodeNum];
     768              :                 }
     769              :             }
     770              :         }
     771              : 
     772        16684 :         for (int etNum = 1; etNum <= totEarthTube; ++etNum) {
     773        12513 :             auto &thisEarthTube = state.dataEarthTube->EarthTubeSys(etNum);
     774        12513 :             if (thisEarthTube.ModelType != EarthTubeModelType::Vertical) {
     775         8299 :                 continue; // Skip earth tubes that do not use vertical solution
     776              :             }
     777        67424 :             for (int nodeNum = 0; nodeNum <= thisEarthTube.totNodes - 1; ++nodeNum) {
     778        63210 :                 thisEarthTube.tLast[nodeNum] = thisEarthTube.tCurrent[nodeNum];
     779              :             }
     780              :         }
     781              :     }
     782         4296 :     state.dataEarthTube->timeElapsed = timeElapsedLoc;
     783         4296 : }
     784              : 
     785            2 : void EarthTubeData::initCPrime0()
     786              : {
     787              :     // Calculate c' for when effectiveness is zero.  Will use these values when there is no air flow through the earth tube
     788              :     // and also use the values in the top portion of the solution (before the earth tube node) since these will not change.
     789            2 :     this->cPrime0[0] = this->cCoeff0[0] / this->bCoeff[0];
     790           28 :     for (int i = 1; i <= this->totNodes - 2; ++i) {
     791           26 :         this->cPrime0[i] = this->cCoeff0[i] / (this->bCoeff[i] - this->aCoeff[i] * this->cPrime0[i - 1]);
     792              :     }
     793            2 :     cPrime0[this->totNodes - 1] = 0.0;
     794            2 : }
     795              : 
     796         4296 : void CalcEarthTube(EnergyPlusData &state)
     797              : {
     798              : 
     799              :     // SUBROUTINE INFORMATION:
     800              :     //       AUTHOR         Kwang Ho Lee
     801              :     //       DATE WRITTEN   November 2005
     802              : 
     803              :     // PURPOSE OF THIS SUBROUTINE:
     804              :     // This subroutine simulates the components making up the EarthTube unit.
     805              : 
     806              :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
     807              :     Real64 Process1;    // Variable Used in the Middle of the Calculation
     808              :     Real64 GroundTempt; // Ground Temperature between Depth z at time t
     809              : 
     810              :     Real64 AirThermCond;         // Thermal Conductivity of Air (W/mC)
     811              :     Real64 AirKinemVisco;        // Kinematic Viscosity of Air (m2/s)
     812              :     Real64 AirThermDiffus;       // Thermal Diffusivity of Air (m2/s)
     813              :     Real64 Re;                   // Reynolds Number for Flow Inside Pipe
     814              :     Real64 Pr;                   // Prandtl Number for Flow Inside Pipe
     815              :     Real64 Nu;                   // Nusselt Number for Flow Inside Pipe
     816              :     Real64 fa;                   // Friction Factor of Pipe
     817              :     Real64 PipeHeatTransCoef;    // Convective Heat Transfer Coefficient at Inner Pipe Surface
     818              :     Real64 Rc;                   // Thermal Resistance due to Convection between Air and Pipe Inner Surface
     819              :     Real64 Rp;                   // Thermal Resistance due to Conduction between Pipe Inner and Outer Surface
     820              :     Real64 Rs;                   // Thermal Resistance due to Conduction between Pipe Outer Surface and Soil
     821              :     Real64 Rt;                   // Total Thermal Resistance between Pipe Air and Soil
     822              :     Real64 OverallHeatTransCoef; // Overall Heat Transfer Coefficient of Earth Tube
     823              :     Real64 AverPipeAirVel;       // Average Pipe Air Velocity (m/s)
     824              :     Real64 AirMassFlowRate;      // Actual Mass Flow Rate of Air inside Pipe
     825              :     Real64 AirSpecHeat;          // Specific Heat of Air
     826              :     Real64 AirDensity;           // Density of Air
     827              :     Real64 EVF;
     828              : 
     829         4296 :     int numEarthTubes = (int)state.dataEarthTube->EarthTubeSys.size();
     830         4296 :     Real64 outTdb = state.dataEnvrn->OutDryBulbTemp;
     831        17184 :     for (int Loop = 1; Loop <= numEarthTubes; ++Loop) {
     832        12888 :         auto &thisEarthTube = state.dataEarthTube->EarthTubeSys(Loop);
     833        12888 :         int NZ = thisEarthTube.ZonePtr;
     834        12888 :         auto &thisZoneHB = state.dataZoneTempPredictorCorrector->zoneHeatBalance(NZ);
     835        12888 :         thisZoneHB.MCPTE = 0.0;
     836        12888 :         thisZoneHB.MCPE = 0.0;
     837        12888 :         thisZoneHB.EAMFL = 0.0;
     838        12888 :         thisZoneHB.EAMFLxHumRat = 0.0;
     839        12888 :         thisEarthTube.FanPower = 0.0;
     840              : 
     841              :         // Don't simulate for Basic Solution if the zone is below the minimum temperature limit, above the maximum temperature limit
     842              :         // or below the temperature difference limit
     843        25776 :         bool tempShutDown = thisZoneHB.MAT < thisEarthTube.MinTemperature || thisZoneHB.MAT > thisEarthTube.MaxTemperature ||
     844        12888 :                             std::abs(thisZoneHB.MAT - outTdb) < thisEarthTube.DelTemperature;
     845              :         // check for Basic model and some temperature limit preventing the earth tube from running
     846        12888 :         if ((thisEarthTube.ModelType == EarthTubeModelType::Basic) && (tempShutDown)) {
     847         2012 :             continue;
     848              :         }
     849              : 
     850        10876 :         AirDensity = Psychrometrics::PsyRhoAirFnPbTdbW(state, state.dataEnvrn->OutBaroPress, outTdb, state.dataEnvrn->OutHumRat);
     851        10876 :         AirSpecHeat = Psychrometrics::PsyCpAirFnW(state.dataEnvrn->OutHumRat);
     852        10876 :         if (tempShutDown) {
     853          281 :             EVF = 0.0;
     854              :         } else {
     855        10595 :             EVF = thisEarthTube.DesignLevel * thisEarthTube.availSched->getCurrentVal();
     856              :         }
     857        10876 :         thisZoneHB.MCPE =
     858        21752 :             EVF * AirDensity * AirSpecHeat *
     859        21752 :             (thisEarthTube.ConstantTermCoef +
     860        10876 :              std::abs(outTdb - state.dataZoneTempPredictorCorrector->zoneHeatBalance(NZ).MAT) * thisEarthTube.TemperatureTermCoef +
     861        10876 :              state.dataEnvrn->WindSpeed * (thisEarthTube.VelocityTermCoef + state.dataEnvrn->WindSpeed * thisEarthTube.VelocitySQTermCoef));
     862              : 
     863        10876 :         thisZoneHB.EAMFL = thisZoneHB.MCPE / AirSpecHeat;
     864        10876 :         if (thisEarthTube.FanEfficiency > 0.0) {
     865        10876 :             thisEarthTube.FanPower = thisZoneHB.EAMFL * thisEarthTube.FanPressure / (thisEarthTube.FanEfficiency * AirDensity);
     866              :         }
     867              : 
     868        10876 :         AverPipeAirVel = EVF / Constant::Pi / pow_2(thisEarthTube.r1);
     869        10876 :         AirMassFlowRate = EVF * AirDensity;
     870              : 
     871        10876 :         if (thisEarthTube.ModelType == EarthTubeModelType::Basic) {
     872              :             // Calculation of Ground Temperature at Depth z at time t for Basic model
     873         6496 :             GroundTempt = thisEarthTube.calcUndisturbedGroundTemperature(state, thisEarthTube.z);
     874         6496 :             thisEarthTube.GroundTempt = GroundTempt;
     875              :         }
     876              : 
     877              :         // Calculation of Convective Heat Transfer Coefficient at Inner Pipe Surface
     878        10876 :         AirThermCond = 0.02442 + 0.6992 * outTdb / 10000.0;
     879        10876 :         AirKinemVisco = (0.1335 + 0.000925 * outTdb) / 10000.0;
     880        10876 :         AirThermDiffus = (0.0014 * outTdb + 0.1872) / 10000.0;
     881        10876 :         Re = 2.0 * thisEarthTube.r1 * AverPipeAirVel / AirKinemVisco;
     882        10876 :         Pr = AirKinemVisco / AirThermDiffus;
     883        10876 :         if (Re <= 2300.0) {
     884          281 :             Nu = 3.66;
     885        10595 :         } else if (Re <= 4000.0) {
     886            0 :             fa = std::pow(1.58 * std::log(Re) - 3.28, -2);
     887            0 :             Process1 = (fa / 2.0) * (Re - 1000.0) * Pr / (1.0 + 12.7 * std::sqrt(fa / 2.0) * (std::pow(Pr, 2.0 / 3.0) - 1.0));
     888            0 :             Nu = (Process1 - 3.66) / (1700.0) * Re + (4000.0 * 3.66 - 2300.0 * Process1) / 1700.0;
     889              :         } else {
     890        10595 :             fa = std::pow(1.58 * std::log(Re) - 3.28, -2);
     891        10595 :             Nu = (fa / 2.0) * (Re - 1000.0) * Pr / (1.0 + 12.7 * std::sqrt(fa / 2.0) * (std::pow(Pr, 2.0 / 3.0) - 1.0));
     892              :         }
     893        10876 :         PipeHeatTransCoef = Nu * AirThermCond / 2.0 / thisEarthTube.r1;
     894              : 
     895              :         // Calculation of Thermal Resistance and Overall Heat Transfer Coefficient
     896        10876 :         Rc = 1.0 / 2.0 / Constant::Pi / thisEarthTube.r1 / PipeHeatTransCoef;
     897        10876 :         Rp = std::log((thisEarthTube.r1 + thisEarthTube.r2) / thisEarthTube.r1) / 2.0 / Constant::Pi / thisEarthTube.PipeThermCond;
     898        10876 :         if (thisEarthTube.r3 > 0.0) {
     899         6496 :             Rs = std::log((thisEarthTube.r1 + thisEarthTube.r2 + thisEarthTube.r3) / (thisEarthTube.r1 + thisEarthTube.r2)) / 2.0 / Constant::Pi /
     900         6496 :                  thisEarthTube.SoilThermCond;
     901              :         } else { // for the Vertical solution .r3 was reset to zero for this
     902         4380 :             Rs = 0.0;
     903              :         }
     904        10876 :         Rt = Rc + Rp + Rs;
     905        10876 :         OverallHeatTransCoef = 1.0 / Rt;
     906              : 
     907        10876 :         switch (thisEarthTube.ModelType) {
     908         4380 :         case EarthTubeModelType::Vertical: {
     909              :             // First calculate term that will need to be added at the diagonal for flow and then solve the matrix for new temperatures
     910              :             Real64 eff; // effectiveness
     911         4380 :             if (AirMassFlowRate > 0.0) {
     912              :                 // Calculate the NTU parameter: NTU = UA/[(Mdot*Cp)min] where Mdot*Cp is for the air side
     913              :                 // where: U = OverallHeatTransCoef
     914              :                 //        A = 2*Pi*r1*TubeLength
     915         4099 :                 Real64 NTU =
     916         4099 :                     OverallHeatTransCoef * 2.0 * Constant::Pi * thisEarthTube.r1 * thisEarthTube.PipeLength / (AirMassFlowRate * AirSpecHeat);
     917              : 
     918              :                 // Effectiveness is 1 - e(-NTU)
     919         4099 :                 Real64 constexpr maxExpPower(50.0); // Maximum power after which EXP argument would be zero for DP variables
     920         4099 :                 if (NTU > maxExpPower) {
     921            0 :                     eff = 1.0;
     922              :                 } else {
     923         4099 :                     eff = 1.0 - std::exp(-NTU);
     924              :                 }
     925              :             } else { // if no flow, then eff is zero
     926          281 :                 eff = 0.0;
     927              :             }
     928              : 
     929         4380 :             Real64 airFlowTerm = AirMassFlowRate * AirSpecHeat * eff * thisEarthTube.airFlowCoeff;
     930         4380 :             thisEarthTube.calcVerticalEarthTube(state, airFlowTerm);
     931              : 
     932         4380 :             int nodeET = state.dataEarthTube->EarthTubePars(thisEarthTube.vertParametersPtr).numNodesAbove;
     933         4380 :             if (eff <= 0.0) { // no flow--air temperature leaving earth tube is the same as what went in
     934          281 :                 thisEarthTube.InsideAirTemp = outTdb;
     935         4099 :             } else if (eff >= 1.0) { // effectiveness is one so leaving temperature is the same as the ground node temperatre
     936            0 :                 thisEarthTube.InsideAirTemp = thisEarthTube.tCurrent[nodeET];
     937              :             } else { // the temperature is between the inlet and ground temperatures
     938         4099 :                 thisEarthTube.InsideAirTemp = outTdb - eff * (outTdb - thisEarthTube.tCurrent[nodeET]);
     939              :             }
     940              : 
     941         4380 :         } break;
     942         6496 :         case EarthTubeModelType::Basic: { // Basic model
     943         6496 :             if (AirMassFlowRate * AirSpecHeat == 0.0) {
     944            0 :                 thisEarthTube.InsideAirTemp = GroundTempt;
     945              : 
     946              :             } else {
     947              : 
     948              :                 // Calculation of Pipe Outlet Air Temperature
     949         6496 :                 if (outTdb > GroundTempt) {
     950         6484 :                     Process1 =
     951         6484 :                         (std::log(std::abs(outTdb - GroundTempt)) * AirMassFlowRate * AirSpecHeat - OverallHeatTransCoef * thisEarthTube.PipeLength) /
     952         6484 :                         (AirMassFlowRate * AirSpecHeat);
     953         6484 :                     thisEarthTube.InsideAirTemp = std::exp(Process1) + GroundTempt;
     954           12 :                 } else if (outTdb == GroundTempt) {
     955            0 :                     thisEarthTube.InsideAirTemp = GroundTempt;
     956              :                 } else {
     957           12 :                     Process1 =
     958           12 :                         (std::log(std::abs(outTdb - GroundTempt)) * AirMassFlowRate * AirSpecHeat - OverallHeatTransCoef * thisEarthTube.PipeLength) /
     959           12 :                         (AirMassFlowRate * AirSpecHeat);
     960           12 :                     thisEarthTube.InsideAirTemp = GroundTempt - std::exp(Process1);
     961              :                 }
     962              :             }
     963         6496 :         } break;
     964            0 :         default: { // should never get here
     965            0 :             assert(false);
     966              :         } break;
     967              :         }
     968              : 
     969        10876 :         thisEarthTube.CalcEarthTubeHumRat(state, NZ);
     970              :     }
     971         4296 : }
     972              : 
     973         7482 : Real64 EarthTubeData::calcUndisturbedGroundTemperature(EnergyPlusData &state, Real64 depth)
     974              : {
     975         7482 :     return this->AverSoilSurTemp -
     976        14964 :            this->ApmlSoilSurTemp * std::exp(-depth * std::sqrt(Constant::Pi / 365.0 / this->SoilThermDiff)) *
     977         7482 :                std::cos(2.0 * Constant::Pi / 365.0 *
     978         7482 :                         (state.dataEnvrn->DayOfYear - this->SoilSurPhaseConst - depth / 2.0 * std::sqrt(365.0 / Constant::Pi / this->SoilThermDiff)));
     979              : }
     980              : 
     981         4380 : void EarthTubeData::calcVerticalEarthTube(EnergyPlusData &state, Real64 airFlowTerm)
     982              : {
     983              :     // Perform matrix calculations to model the earth tube using the vertical solution.
     984              :     // At this point, temperatures have already been shifted so tLast is correct and
     985              :     // undisturbed ground temperature have also been calculated.  We need to assign/update
     986              :     // vectors of coefficients and then perform the Thomas algorithm.
     987              :     // Note that airFlowTerm is mdot_a*cp_a*eff*deltat/rho_soil/cp_soil/nodethickness_et/width/length
     988              : 
     989         4380 :     int nodeET = state.dataEarthTube->EarthTubePars(this->vertParametersPtr).numNodesAbove;
     990         4380 :     int nodeLast = this->totNodes - 1; // minus one because c++ arrays start at 0
     991              : 
     992              :     // First, calculate cPrime in the forward sweep.
     993              :     // If airFlowTerm is zero, there is no flow so we can use can use cPrime0 for cPrime.
     994         4380 :     if (airFlowTerm <= 0.0) {
     995         4442 :         for (int nodeNum = 0; nodeNum <= nodeLast; ++nodeNum) {
     996         4161 :             this->cPrime[nodeNum] = this->cPrime0[nodeNum];
     997              :         }
     998              :     } else { // there is positive flow so calculate cPrime
     999         4099 :         this->cPrime[0] = this->cCoeff[0] / this->bCoeff[0];
    1000        61539 :         for (int nodeNum = 1; nodeNum <= nodeLast; ++nodeNum) {
    1001        57440 :             Real64 addTerm = 0.0;
    1002        57440 :             if (nodeNum == nodeET) {
    1003         4099 :                 addTerm = airFlowTerm;
    1004              :             }
    1005        57440 :             this->cPrime[nodeNum] = this->cCoeff[nodeNum] / (this->bCoeff[nodeNum] + addTerm - this->aCoeff[nodeNum] * this->cPrime[nodeNum - 1]);
    1006              :         }
    1007              :     }
    1008              : 
    1009              :     // Second, set-up dCoeff
    1010         4380 :     this->dCoeff[0] = this->tLast[0] + this->dMult0 * this->tUpperBound;
    1011        61320 :     for (int nodeNum = 1; nodeNum <= nodeLast - 1; ++nodeNum) {
    1012        56940 :         if (nodeNum != nodeET) {
    1013        52560 :             this->dCoeff[nodeNum] = this->tLast[nodeNum];
    1014              :         } else {
    1015         4380 :             this->dCoeff[nodeNum] = this->tLast[nodeNum] + airFlowTerm * state.dataEnvrn->OutDryBulbTemp;
    1016              :         }
    1017              :     }
    1018         4380 :     this->dCoeff[nodeLast] = this->tLast[nodeLast] + this->dMultN * this->tLowerBound;
    1019              : 
    1020              :     // Third, calculate dPrime in the forward sweep.
    1021         4380 :     this->dPrime[0] = this->dCoeff[0] / this->bCoeff[0];
    1022        65700 :     for (int nodeNum = 1; nodeNum <= nodeLast; ++nodeNum) {
    1023        61320 :         Real64 addTerm = 0.0;
    1024        61320 :         if (nodeNum == nodeET) {
    1025         4380 :             addTerm = airFlowTerm;
    1026              :         }
    1027       122640 :         this->dPrime[nodeNum] = (this->dCoeff[nodeNum] - this->aCoeff[nodeNum] * this->dPrime[nodeNum - 1]) /
    1028        61320 :                                 (this->bCoeff[nodeNum] + addTerm - this->aCoeff[nodeNum] * this->cPrime[nodeNum - 1]);
    1029              :     }
    1030              : 
    1031              :     // Finally, obtain the solution (tCurrent) by back substitution.
    1032         4380 :     this->tCurrent[nodeLast] = this->dPrime[nodeLast];
    1033        65700 :     for (int nodeNum = nodeLast - 1; nodeNum >= 0; --nodeNum) {
    1034        61320 :         this->tCurrent[nodeNum] = this->dPrime[nodeNum] - this->cPrime[nodeNum] * this->tCurrent[nodeNum + 1];
    1035              :     }
    1036         4380 : }
    1037              : 
    1038        10876 : void EarthTubeData::CalcEarthTubeHumRat(EnergyPlusData &state, int const NZ)
    1039              : { // Zone number (index)
    1040              : 
    1041              :     // SUBROUTINE INFORMATION:
    1042              :     //       AUTHOR         Kwang Ho Lee
    1043              :     //       DATE WRITTEN   November 2005
    1044              :     //       MODIFIED       Rick Strand, June 2017 (made this a separate subroutine)
    1045              : 
    1046              :     // PURPOSE OF THIS SUBROUTINE:
    1047              :     // This subroutine determines the leaving humidity ratio for the EarthTube
    1048              :     // and calculates parameters associated with humidity ratio.
    1049              : 
    1050        10876 :     Real64 InsideDewPointTemp = Psychrometrics::PsyTdpFnWPb(state, state.dataEnvrn->OutHumRat, state.dataEnvrn->OutBaroPress);
    1051        10876 :     Real64 InsideHumRat = 0.0;
    1052        10876 :     auto &thisZoneHB = state.dataZoneTempPredictorCorrector->zoneHeatBalance(NZ);
    1053              : 
    1054        10876 :     if (this->InsideAirTemp >= InsideDewPointTemp) {
    1055         1099 :         InsideHumRat = state.dataEnvrn->OutHumRat;
    1056         1099 :         Real64 const InsideEnthalpy = Psychrometrics::PsyHFnTdbW(this->InsideAirTemp, state.dataEnvrn->OutHumRat);
    1057              :         // Intake fans will add some heat to the air, raising the temperature for an intake fan...
    1058         1099 :         if (this->FanType == Ventilation::Intake) {
    1059              :             Real64 OutletAirEnthalpy;
    1060          337 :             if (thisZoneHB.EAMFL == 0.0) {
    1061          136 :                 OutletAirEnthalpy = InsideEnthalpy;
    1062              :             } else {
    1063          201 :                 OutletAirEnthalpy = InsideEnthalpy + this->FanPower / thisZoneHB.EAMFL;
    1064              :             }
    1065          337 :             this->AirTemp = Psychrometrics::PsyTdbFnHW(OutletAirEnthalpy, state.dataEnvrn->OutHumRat);
    1066              :         } else {
    1067          762 :             this->AirTemp = this->InsideAirTemp;
    1068              :         }
    1069         1099 :         thisZoneHB.MCPTE = thisZoneHB.MCPE * this->AirTemp;
    1070              : 
    1071              :     } else {
    1072         9777 :         InsideHumRat = Psychrometrics::PsyWFnTdpPb(state, this->InsideAirTemp, state.dataEnvrn->OutBaroPress);
    1073         9777 :         Real64 const InsideEnthalpy = Psychrometrics::PsyHFnTdbW(this->InsideAirTemp, InsideHumRat);
    1074              :         // Intake fans will add some heat to the air, raising the temperature for an intake fan...
    1075         9777 :         if (this->FanType == Ventilation::Intake) {
    1076              :             Real64 OutletAirEnthalpy;
    1077         3465 :             if (thisZoneHB.EAMFL == 0.0) {
    1078            0 :                 OutletAirEnthalpy = InsideEnthalpy;
    1079              :             } else {
    1080         3465 :                 OutletAirEnthalpy = InsideEnthalpy + this->FanPower / thisZoneHB.EAMFL;
    1081              :             }
    1082         3465 :             this->AirTemp = Psychrometrics::PsyTdbFnHW(OutletAirEnthalpy, InsideHumRat);
    1083              :         } else {
    1084         6312 :             this->AirTemp = this->InsideAirTemp;
    1085              :         }
    1086         9777 :         thisZoneHB.MCPTE = thisZoneHB.MCPE * this->AirTemp;
    1087              :     }
    1088              : 
    1089        10876 :     this->HumRat = InsideHumRat;
    1090        10876 :     this->WetBulbTemp = Psychrometrics::PsyTwbFnTdbWPb(state, this->InsideAirTemp, InsideHumRat, state.dataEnvrn->OutBaroPress);
    1091        10876 :     thisZoneHB.EAMFLxHumRat = thisZoneHB.EAMFL * InsideHumRat;
    1092        10876 : }
    1093              : 
    1094         4296 : void ReportEarthTube(EnergyPlusData &state)
    1095              : {
    1096              : 
    1097              :     // SUBROUTINE INFORMATION:
    1098              :     //       AUTHOR         Kwang Ho Lee
    1099              :     //       DATE WRITTEN   November 2005
    1100              :     //       MODIFIED       B. Griffith April 2010 added output reports
    1101              : 
    1102              :     // PURPOSE OF THIS SUBROUTINE: This subroutine fills remaining report variables.
    1103              : 
    1104         4296 :     Real64 const ReportingConstant = state.dataHVACGlobal->TimeStepSysSec;
    1105              : 
    1106        17184 :     for (int ZoneLoop = 1; ZoneLoop <= state.dataGlobal->NumOfZones; ++ZoneLoop) { // Start of zone loads report variable update loop ...
    1107        12888 :         auto &thisZone = state.dataEarthTube->ZnRptET(ZoneLoop);
    1108        12888 :         auto const &thisZoneHB = state.dataZoneTempPredictorCorrector->zoneHeatBalance(ZoneLoop);
    1109              : 
    1110              :         // Break the infiltration load into heat gain and loss components.
    1111              :         Real64 const AirDensity =
    1112        12888 :             Psychrometrics::PsyRhoAirFnPbTdbW(state, state.dataEnvrn->OutBaroPress, state.dataEnvrn->OutDryBulbTemp, state.dataEnvrn->OutHumRat);
    1113        12888 :         Real64 const CpAir = Psychrometrics::PsyCpAirFnW(state.dataEnvrn->OutHumRat);
    1114        12888 :         thisZone.EarthTubeVolume = (thisZoneHB.MCPE / CpAir / AirDensity) * ReportingConstant;
    1115        12888 :         thisZone.EarthTubeMass = (thisZoneHB.MCPE / CpAir) * ReportingConstant;
    1116        12888 :         thisZone.EarthTubeVolFlowRate = thisZoneHB.MCPE / CpAir / AirDensity;
    1117        12888 :         thisZone.EarthTubeVolFlowRateStd = thisZoneHB.MCPE / CpAir / state.dataEnvrn->StdRhoAir;
    1118        12888 :         thisZone.EarthTubeMassFlowRate = thisZoneHB.MCPE / CpAir;
    1119        12888 :         thisZone.EarthTubeWaterMassFlowRate = thisZoneHB.EAMFLxHumRat;
    1120              : 
    1121        12888 :         thisZone.EarthTubeFanElec = 0.0;
    1122        12888 :         thisZone.EarthTubeAirTemp = 0.0;
    1123        25776 :         for (auto const &thisEarthTube : state.dataEarthTube->EarthTubeSys) {
    1124        25776 :             if (thisEarthTube.ZonePtr == ZoneLoop) {
    1125        12888 :                 thisZone.EarthTubeFanElec = thisEarthTube.FanPower * ReportingConstant;
    1126        12888 :                 thisZone.EarthTubeFanElecPower = thisEarthTube.FanPower;
    1127              : 
    1128              :                 // Break the EarthTube load into heat gain and loss components.
    1129        12888 :                 if (thisZoneHB.ZT > thisEarthTube.AirTemp) {
    1130        12766 :                     thisZone.EarthTubeHeatLoss = thisZoneHB.MCPE * (thisZoneHB.ZT - thisEarthTube.AirTemp) * ReportingConstant;
    1131        12766 :                     thisZone.EarthTubeHeatLossRate = thisZoneHB.MCPE * (thisZoneHB.ZT - thisEarthTube.AirTemp);
    1132        12766 :                     thisZone.EarthTubeHeatGain = 0.0;
    1133        12766 :                     thisZone.EarthTubeHeatGainRate = 0.0;
    1134              :                 } else {
    1135          122 :                     thisZone.EarthTubeHeatGain = thisZoneHB.MCPE * (thisEarthTube.AirTemp - thisZoneHB.ZT) * ReportingConstant;
    1136          122 :                     thisZone.EarthTubeHeatGainRate = thisZoneHB.MCPE * (thisEarthTube.AirTemp - thisZoneHB.ZT);
    1137          122 :                     thisZone.EarthTubeHeatLoss = 0.0;
    1138          122 :                     thisZone.EarthTubeHeatLossRate = 0.0;
    1139              :                 }
    1140              : 
    1141        12888 :                 thisZone.EarthTubeAirTemp = thisEarthTube.AirTemp;
    1142        12888 :                 thisZone.EarthTubeWetBulbTemp = thisEarthTube.WetBulbTemp;
    1143        12888 :                 thisZone.EarthTubeHumRat = thisEarthTube.HumRat;
    1144        12888 :                 thisZone.EarthTubeOATreatmentPower = thisZoneHB.MCPE * (thisEarthTube.AirTemp - state.dataEnvrn->OutDryBulbTemp);
    1145        12888 :                 break; // DO loop
    1146              :             }
    1147        12888 :         }
    1148              : 
    1149              :     } // ... end of zone loads report variable update loop.
    1150         4296 : }
    1151              : 
    1152              : } // namespace EnergyPlus::EarthTube
        

Generated by: LCOV version 2.0-1