LCOV - code coverage report
Current view: top level - EnergyPlus - EarthTube.cc (source / functions) Hit Total Coverage
Test: lcov.output.filtered Lines: 467 605 77.2 %
Date: 2024-08-24 18:31:18 Functions: 10 10 100.0 %

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

Generated by: LCOV version 1.14