LCOV - code coverage report
Current view: top level - EnergyPlus - EarthTube.cc (source / functions) Coverage Total Hit
Test: lcov.output.filtered Lines: 10.8 % 586 63
Test Date: 2025-05-22 16:09:37 Functions: 60.0 % 10 6

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

Generated by: LCOV version 2.0-1