LCOV - code coverage report
Current view: top level - EnergyPlus - HeatBalFiniteDiffManager.cc (source / functions) Hit Total Coverage
Test: lcov.output.filtered Lines: 1108 1394 79.5 %
Date: 2024-08-23 23:50:59 Functions: 19 20 95.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 <cassert>
      50             : #include <cmath>
      51             : #include <string>
      52             : 
      53             : // ObjexxFCL Headers
      54             : #include <ObjexxFCL/Array.functions.hh>
      55             : #include <ObjexxFCL/Fmath.hh>
      56             : 
      57             : // EnergyPlus Headers
      58             : #include <AirflowNetwork/Solver.hpp>
      59             : #include <EnergyPlus/Construction.hh>
      60             : #include <EnergyPlus/Data/EnergyPlusData.hh>
      61             : #include <EnergyPlus/DataEnvironment.hh>
      62             : #include <EnergyPlus/DataHeatBalFanSys.hh>
      63             : #include <EnergyPlus/DataHeatBalSurface.hh>
      64             : #include <EnergyPlus/DataHeatBalance.hh>
      65             : #include <EnergyPlus/DataIPShortCuts.hh>
      66             : #include <EnergyPlus/DataMoistureBalance.hh>
      67             : #include <EnergyPlus/DataSurfaces.hh>
      68             : #include <EnergyPlus/EMSManager.hh>
      69             : #include <EnergyPlus/General.hh>
      70             : #include <EnergyPlus/HeatBalFiniteDiffManager.hh>
      71             : #include <EnergyPlus/InputProcessing/InputProcessor.hh>
      72             : #include <EnergyPlus/Material.hh>
      73             : #include <EnergyPlus/OutputProcessor.hh>
      74             : #include <EnergyPlus/PhaseChangeModeling/HysteresisModel.hh>
      75             : #include <EnergyPlus/PluginManager.hh>
      76             : #include <EnergyPlus/UtilityRoutines.hh>
      77             : #include <EnergyPlus/ZoneTempPredictorCorrector.hh>
      78             : 
      79             : namespace EnergyPlus {
      80             : 
      81             : namespace HeatBalFiniteDiffManager {
      82             : 
      83             :     // Module containing the heat balance simulation routines
      84             : 
      85             :     // MODULE INFORMATION:
      86             :     //       AUTHOR         Richard J. Liesen
      87             :     //       DATE WRITTEN   October 2003
      88             :     //       RE-ENGINEERED  Curtis Pedersen, 2006, Changed to Implicit FD calc for conduction.
      89             :     //                      and included enthalpy formulations for phase change materials
      90             :     // PURPOSE OF THIS MODULE:
      91             :     // To encapsulate the data and algorithms required to
      92             :     // manage the finite difference heat balance simulation on the building.
      93             : 
      94             :     // REFERENCES:
      95             :     // The MFD moisture balance method
      96             :     //  C. O. Pedersen, Enthalpy Formulation of conduction heat transfer problems
      97             :     //    involving latent heat, Simulation, Vol 18, No. 2, February 1972
      98             :     // Fan system Source/Sink heat value, and source/sink location temp from CondFD
      99             : 
     100    10239636 :     void ManageHeatBalFiniteDiff(EnergyPlusData &state,
     101             :                                  int const SurfNum,
     102             :                                  Real64 &SurfTempInTmp, // INSIDE SURFACE TEMPERATURE OF EACH HEAT TRANSFER SURF.
     103             :                                  Real64 &TempSurfOutTmp // Outside Surface Temperature of each Heat Transfer Surface
     104             :     )
     105             :     {
     106             :         // SUBROUTINE INFORMATION:
     107             :         //       AUTHOR         Richard Liesen
     108             :         //       DATE WRITTEN   May 2000
     109             : 
     110             :         // PURPOSE OF THIS SUBROUTINE:
     111             :         // This subroutine manages the moisture balance method.  It is called
     112             :         // from the HeatBalanceManager at the time step level.
     113             :         // This driver manages the calls to all of
     114             :         // the other drivers and simulation algorithms.
     115             : 
     116             :         // Get the moisture balance input at the beginning of the simulation only
     117    10239636 :         if (state.dataHeatBalFiniteDiffMgr->GetHBFiniteDiffInputFlag) {
     118             :             // Obtains conduction FD related parameters from input file
     119           0 :             GetCondFDInput(state);
     120           0 :             state.dataHeatBalFiniteDiffMgr->GetHBFiniteDiffInputFlag = false;
     121             :         }
     122             :         // Solve the zone heat & moisture balance using a finite difference solution
     123    10239636 :         CalcHeatBalFiniteDiff(state, SurfNum, SurfTempInTmp, TempSurfOutTmp);
     124    10239636 :     }
     125             : 
     126          12 :     void GetCondFDInput(EnergyPlusData &state)
     127             :     {
     128             :         // SUBROUTINE INFORMATION:
     129             :         //       AUTHOR         Curtis Pedersen
     130             :         //       DATE WRITTEN   July 2006
     131             :         //       MODIFIED       Brent Griffith Mar 2011, user settings
     132             : 
     133             :         // PURPOSE OF THIS SUBROUTINE:
     134             :         // This subroutine is the main driver for initializations for the variable property CondFD part of the
     135             :         // MFD algorithm
     136             : 
     137             :         // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
     138             :         int IOStat;                         // IO Status when calling get input subroutine
     139          12 :         Array1D_string MaterialNames(3);    // Number of Material Alpha names defined
     140          12 :         Array1D_string ConstructionName(3); // Name of Construction with CondFDsimplified
     141             :         int MaterNum;                       // Counter to keep track of the material number
     142             :         int MaterialNumAlpha;               // Number of material alpha names being passed
     143             :         int MaterialNumProp;                // Number of material properties being passed
     144          12 :         Array1D<Real64> MaterialProps;      // Temporary array to transfer material properties (allocated based on user input)
     145          12 :         bool ErrorsFound(false);            // If errors detected in input
     146             :         int Loop;
     147             :         int propNum;
     148             :         int pcMat;
     149             :         int vcMat;
     150             :         int inegptr;
     151             :         bool nonInc;
     152          12 :         auto &cCurrentModuleObject = state.dataIPShortCut->cCurrentModuleObject;
     153             :         // user settings for numerical parameters
     154          12 :         cCurrentModuleObject = "HeatBalanceSettings:ConductionFiniteDifference";
     155             : 
     156          12 :         if (state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, cCurrentModuleObject) > 0) {
     157             :             int NumAlphas;
     158             :             int NumNumbers;
     159           2 :             state.dataInputProcessing->inputProcessor->getObjectItem(state,
     160             :                                                                      cCurrentModuleObject,
     161             :                                                                      1,
     162           1 :                                                                      state.dataIPShortCut->cAlphaArgs,
     163             :                                                                      NumAlphas,
     164           1 :                                                                      state.dataIPShortCut->rNumericArgs,
     165             :                                                                      NumNumbers,
     166             :                                                                      IOStat,
     167           1 :                                                                      state.dataIPShortCut->lNumericFieldBlanks,
     168           1 :                                                                      state.dataIPShortCut->lAlphaFieldBlanks,
     169           1 :                                                                      state.dataIPShortCut->cAlphaFieldNames,
     170           1 :                                                                      state.dataIPShortCut->cNumericFieldNames);
     171             : 
     172           1 :             if (!state.dataIPShortCut->lAlphaFieldBlanks(1)) {
     173             :                 {
     174           2 :                     state.dataHeatBalFiniteDiffMgr->CondFDSchemeType =
     175           1 :                         static_cast<CondFDScheme>(getEnumValue(CondFDSchemeTypeNamesUC, Util::makeUPPER(state.dataIPShortCut->cAlphaArgs(1))));
     176           1 :                     if (state.dataHeatBalFiniteDiffMgr->CondFDSchemeType == CondFDScheme::Invalid) {
     177           0 :                         ShowSevereError(state,
     178           0 :                                         format("{}: invalid {} entered={}, must match CrankNicholsonSecondOrder or FullyImplicitFirstOrder.",
     179             :                                                cCurrentModuleObject,
     180           0 :                                                state.dataIPShortCut->cAlphaFieldNames(1),
     181           0 :                                                state.dataIPShortCut->cAlphaArgs(1)));
     182           0 :                         ErrorsFound = true;
     183             :                     }
     184             :                 }
     185             :             }
     186             : 
     187           1 :             if (!state.dataIPShortCut->lNumericFieldBlanks(1)) {
     188           1 :                 state.dataHeatBalFiniteDiffMgr->SpaceDescritConstant = state.dataIPShortCut->rNumericArgs(1);
     189             :             }
     190           1 :             if (!state.dataIPShortCut->lNumericFieldBlanks(2)) {
     191           1 :                 state.dataHeatBal->CondFDRelaxFactorInput = state.dataIPShortCut->rNumericArgs(2);
     192           1 :                 state.dataHeatBal->CondFDRelaxFactor = state.dataHeatBal->CondFDRelaxFactorInput;
     193             :             }
     194           1 :             if (!state.dataIPShortCut->lNumericFieldBlanks(3)) {
     195           1 :                 state.dataHeatBal->MaxAllowedDelTempCondFD = state.dataIPShortCut->rNumericArgs(3);
     196             :             }
     197             : 
     198             :         } // settings object
     199             : 
     200          12 :         pcMat = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, "MaterialProperty:PhaseChange");
     201          12 :         vcMat = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, "MaterialProperty:VariableThermalConductivity");
     202             : 
     203          12 :         int numProps = setSizeMaxProperties(state);
     204          12 :         MaterialProps.allocate(numProps);
     205             : 
     206          12 :         auto &MaterialFD = state.dataHeatBalFiniteDiffMgr->MaterialFD;
     207             : 
     208          12 :         MaterialFD.allocate(state.dataMaterial->TotMaterials);
     209             : 
     210             :         // Load the additional CondFD Material properties
     211          12 :         cCurrentModuleObject = "MaterialProperty:PhaseChange"; // Phase Change Information First
     212             : 
     213          12 :         if (pcMat != 0) { //  Get Phase Change info
     214             :             //    CondFDVariableProperties = .TRUE.
     215           2 :             for (Loop = 1; Loop <= pcMat; ++Loop) {
     216             : 
     217             :                 // Call Input Get routine to retrieve material data
     218           2 :                 state.dataInputProcessing->inputProcessor->getObjectItem(state,
     219             :                                                                          cCurrentModuleObject,
     220             :                                                                          Loop,
     221             :                                                                          MaterialNames,
     222             :                                                                          MaterialNumAlpha,
     223             :                                                                          MaterialProps,
     224             :                                                                          MaterialNumProp,
     225             :                                                                          IOStat,
     226           1 :                                                                          state.dataIPShortCut->lNumericFieldBlanks,
     227           1 :                                                                          state.dataIPShortCut->lAlphaFieldBlanks,
     228           1 :                                                                          state.dataIPShortCut->cAlphaFieldNames,
     229           1 :                                                                          state.dataIPShortCut->cNumericFieldNames);
     230             : 
     231             :                 // Load the material derived type from the input data.
     232           1 :                 MaterNum = Util::FindItemInPtrList(MaterialNames(1), state.dataMaterial->Material);
     233           1 :                 if (MaterNum == 0) {
     234           0 :                     ShowSevereError(state,
     235           0 :                                     format("{}: invalid {} entered={}, must match to a valid Material name.",
     236             :                                            cCurrentModuleObject,
     237           0 :                                            state.dataIPShortCut->cAlphaFieldNames(1),
     238             :                                            MaterialNames(1)));
     239           0 :                     ErrorsFound = true;
     240           0 :                     continue;
     241             :                 }
     242           1 :                 auto const *thisMaterial = state.dataMaterial->Material(MaterNum);
     243             : 
     244           1 :                 if (thisMaterial->group != Material::Group::Regular) {
     245           0 :                     ShowSevereError(state,
     246           0 :                                     format("{}: Reference Material is not appropriate type for CondFD properties, material={}, must have regular "
     247             :                                            "properties (L,Cp,K,D)",
     248             :                                            cCurrentModuleObject,
     249           0 :                                            thisMaterial->Name));
     250           0 :                     ErrorsFound = true;
     251             :                 }
     252             : 
     253             :                 // Once the material derived type number is found then load the additional CondFD variable material properties
     254             :                 //   Some or all may be zero (default).  They will be checked when calculating node temperatures
     255           1 :                 MaterialFD(MaterNum).tk1 = MaterialProps(1);
     256           1 :                 MaterialFD(MaterNum).numTempEnth = (MaterialNumProp - 1) / 2;
     257           1 :                 if (MaterialFD(MaterNum).numTempEnth * 2 != (MaterialNumProp - 1)) {
     258           0 :                     ShowSevereError(state, format("GetCondFDInput: {}=\"{}\", mismatched pairs", cCurrentModuleObject, MaterialNames(1)));
     259           0 :                     ShowContinueError(
     260           0 :                         state, format("...expected {} pairs, but only entered {} numbers.", MaterialFD(MaterNum).numTempEnth, MaterialNumProp - 1));
     261           0 :                     ErrorsFound = true;
     262             :                 }
     263           1 :                 MaterialFD(MaterNum).TempEnth.dimension(2, MaterialFD(MaterNum).numTempEnth, 0.0);
     264           1 :                 propNum = 2;
     265             :                 // Temperature first
     266           5 :                 for (int pcount = 1, pcount_end = MaterialFD(MaterNum).numTempEnth; pcount <= pcount_end; ++pcount) {
     267           4 :                     MaterialFD(MaterNum).TempEnth(1, pcount) = MaterialProps(propNum);
     268           4 :                     propNum += 2;
     269             :                 }
     270           1 :                 propNum = 3;
     271             :                 // Then Enthalpy
     272           5 :                 for (int pcount = 1, pcount_end = MaterialFD(MaterNum).numTempEnth; pcount <= pcount_end; ++pcount) {
     273           4 :                     MaterialFD(MaterNum).TempEnth(2, pcount) = MaterialProps(propNum);
     274           4 :                     propNum += 2;
     275             :                 }
     276           1 :                 nonInc = false;
     277           1 :                 inegptr = 0;
     278           4 :                 for (int pcount = 1, pcount_end = MaterialFD(MaterNum).numTempEnth - 1; pcount <= pcount_end; ++pcount) {
     279           3 :                     if (MaterialFD(MaterNum).TempEnth(1, pcount) < MaterialFD(MaterNum).TempEnth(1, pcount + 1)) continue;
     280           0 :                     nonInc = true;
     281           0 :                     inegptr = pcount + 1;
     282           0 :                     break;
     283             :                 }
     284           1 :                 if (nonInc) {
     285           0 :                     ShowSevereError(state,
     286           0 :                                     format("GetCondFDInput: {}=\"{}\", non increasing Temperatures. Temperatures must be strictly increasing.",
     287             :                                            cCurrentModuleObject,
     288             :                                            MaterialNames(1)));
     289           0 :                     ShowContinueError(
     290             :                         state,
     291           0 :                         format("...occurs first at item=[{}], value=[{:.2R}].", fmt::to_string(inegptr), MaterialFD(MaterNum).TempEnth(1, inegptr)));
     292           0 :                     ErrorsFound = true;
     293             :                 }
     294           1 :                 nonInc = false;
     295           1 :                 inegptr = 0;
     296           4 :                 for (int pcount = 1, pcount_end = MaterialFD(MaterNum).numTempEnth - 1; pcount <= pcount_end; ++pcount) {
     297           3 :                     if (MaterialFD(MaterNum).TempEnth(2, pcount) <= MaterialFD(MaterNum).TempEnth(2, pcount + 1)) continue;
     298           0 :                     nonInc = true;
     299           0 :                     inegptr = pcount + 1;
     300           0 :                     break;
     301             :                 }
     302           1 :                 if (nonInc) {
     303           0 :                     ShowSevereError(state, format("GetCondFDInput: {}=\"{}\", non increasing Enthalpy.", cCurrentModuleObject, MaterialNames(1)));
     304           0 :                     ShowContinueError(state,
     305           0 :                                       format("...occurs first at item=[{}], value=[{:.2R}].", inegptr, MaterialFD(MaterNum).TempEnth(2, inegptr)));
     306           0 :                     ShowContinueError(state, "...These values may be Cp (Specific Heat) rather than Enthalpy.  Please correct.");
     307           0 :                     ErrorsFound = true;
     308             :                 }
     309             :             }
     310             :         }
     311             :         //   Get CondFD Variable Thermal Conductivity Input
     312             : 
     313          12 :         cCurrentModuleObject = "MaterialProperty:VariableThermalConductivity"; // Variable Thermal Conductivity Info next
     314          12 :         if (vcMat != 0) {                                                      //  variable k info
     315             :             //    CondFDVariableProperties = .TRUE.
     316           2 :             for (Loop = 1; Loop <= vcMat; ++Loop) {
     317             : 
     318             :                 // Call Input Get routine to retrieve material data
     319           2 :                 state.dataInputProcessing->inputProcessor->getObjectItem(state,
     320             :                                                                          cCurrentModuleObject,
     321             :                                                                          Loop,
     322             :                                                                          MaterialNames,
     323             :                                                                          MaterialNumAlpha,
     324             :                                                                          MaterialProps,
     325             :                                                                          MaterialNumProp,
     326             :                                                                          IOStat,
     327           1 :                                                                          state.dataIPShortCut->lNumericFieldBlanks,
     328           1 :                                                                          state.dataIPShortCut->lAlphaFieldBlanks,
     329           1 :                                                                          state.dataIPShortCut->cAlphaFieldNames,
     330           1 :                                                                          state.dataIPShortCut->cNumericFieldNames);
     331             : 
     332             :                 // Load the material derived type from the input data.
     333           1 :                 MaterNum = Util::FindItemInPtrList(MaterialNames(1), state.dataMaterial->Material);
     334           1 :                 if (MaterNum == 0) {
     335           0 :                     ShowSevereError(state,
     336           0 :                                     format("{}: invalid {} entered={}, must match to a valid Material name.",
     337             :                                            cCurrentModuleObject,
     338           0 :                                            state.dataIPShortCut->cAlphaFieldNames(1),
     339             :                                            MaterialNames(1)));
     340           0 :                     ErrorsFound = true;
     341           0 :                     continue;
     342             :                 }
     343           1 :                 auto const *thisMaterial = state.dataMaterial->Material(MaterNum);
     344             : 
     345           1 :                 if (thisMaterial->group != Material::Group::Regular) {
     346           0 :                     ShowSevereError(state,
     347           0 :                                     format("{}: Reference Material is not appropriate type for CondFD properties, material={}, must have regular "
     348             :                                            "properties (L,Cp,K,D)",
     349             :                                            cCurrentModuleObject,
     350           0 :                                            thisMaterial->Name));
     351           0 :                     ErrorsFound = true;
     352             :                 }
     353             : 
     354             :                 // Once the material derived type number is found then load the additional CondFD variable material properties
     355             :                 //   Some or all may be zero (default).  They will be checked when calculating node temperatures
     356           1 :                 MaterialFD(MaterNum).numTempCond = MaterialNumProp / 2;
     357           1 :                 if (MaterialFD(MaterNum).numTempCond * 2 != MaterialNumProp) {
     358           0 :                     ShowSevereError(state, format("GetCondFDInput: {}=\"{}\", mismatched pairs", cCurrentModuleObject, MaterialNames(1)));
     359           0 :                     ShowContinueError(
     360           0 :                         state, format("...expected {} pairs, but only entered {} numbers.", MaterialFD(MaterNum).numTempCond, MaterialNumProp));
     361           0 :                     ErrorsFound = true;
     362             :                 }
     363           1 :                 MaterialFD(MaterNum).TempCond.dimension(2, MaterialFD(MaterNum).numTempCond, 0.0);
     364           1 :                 propNum = 1;
     365             :                 // Temperature first
     366           5 :                 for (int pcount = 1, pcount_end = MaterialFD(MaterNum).numTempCond; pcount <= pcount_end; ++pcount) {
     367           4 :                     MaterialFD(MaterNum).TempCond(1, pcount) = MaterialProps(propNum);
     368           4 :                     propNum += 2;
     369             :                 }
     370           1 :                 propNum = 2;
     371             :                 // Then Conductivity
     372           5 :                 for (int pcount = 1, pcount_end = MaterialFD(MaterNum).numTempCond; pcount <= pcount_end; ++pcount) {
     373           4 :                     MaterialFD(MaterNum).TempCond(2, pcount) = MaterialProps(propNum);
     374           4 :                     propNum += 2;
     375             :                 }
     376           1 :                 nonInc = false;
     377           1 :                 inegptr = 0;
     378           4 :                 for (int pcount = 1, pcount_end = MaterialFD(MaterNum).numTempCond - 1; pcount <= pcount_end; ++pcount) {
     379           3 :                     if (MaterialFD(MaterNum).TempCond(1, pcount) < MaterialFD(MaterNum).TempCond(1, pcount + 1)) continue;
     380           0 :                     nonInc = true;
     381           0 :                     inegptr = pcount + 1;
     382           0 :                     break;
     383             :                 }
     384           1 :                 if (nonInc) {
     385           0 :                     ShowSevereError(state,
     386           0 :                                     format("GetCondFDInput: {}=\"{}\", non increasing Temperatures. Temperatures must be strictly increasing.",
     387             :                                            cCurrentModuleObject,
     388             :                                            MaterialNames(1)));
     389           0 :                     ShowContinueError(state,
     390           0 :                                       format("...occurs first at item=[{}], value=[{:.2R}].", inegptr, MaterialFD(MaterNum).TempCond(1, inegptr)));
     391           0 :                     ErrorsFound = true;
     392             :                 }
     393             :             }
     394             :         }
     395             : 
     396         403 :         for (MaterNum = 1; MaterNum <= state.dataMaterial->TotMaterials; ++MaterNum) {
     397         391 :             if (MaterialFD(MaterNum).numTempEnth == 0) {
     398         390 :                 MaterialFD(MaterNum).numTempEnth = 3;
     399         390 :                 MaterialFD(MaterNum).TempEnth.dimension(2, 3, -100.0);
     400             :             }
     401         391 :             if (MaterialFD(MaterNum).numTempCond == 0) {
     402         390 :                 MaterialFD(MaterNum).numTempCond = 3;
     403         390 :                 MaterialFD(MaterNum).TempCond.dimension(2, 3, -100.0);
     404             :             }
     405             :         }
     406             : 
     407          12 :         if (ErrorsFound) {
     408           0 :             ShowFatalError(state, "GetCondFDInput: Errors found getting ConductionFiniteDifference properties. Program terminates.");
     409             :         }
     410             : 
     411          12 :         InitialInitHeatBalFiniteDiff(state);
     412          12 :     }
     413             : 
     414          12 :     int setSizeMaxProperties(EnergyPlusData &state)
     415             :     {
     416             :         int numArgs;
     417             :         int numAlphas;
     418             :         int numNumerics;
     419          12 :         int maxTotalProps = 0;
     420             : 
     421          12 :         state.dataInputProcessing->inputProcessor->getObjectDefMaxArgs(state, "MaterialProperty:PhaseChange", numArgs, numAlphas, numNumerics);
     422          12 :         maxTotalProps = max(maxTotalProps, numNumerics);
     423             : 
     424          12 :         state.dataInputProcessing->inputProcessor->getObjectDefMaxArgs(
     425             :             state, "MaterialProperty:VariableThermalConductivity", numArgs, numAlphas, numNumerics);
     426          12 :         maxTotalProps = max(maxTotalProps, numNumerics);
     427             : 
     428          12 :         return maxTotalProps;
     429             :     }
     430             : 
     431      158532 :     void InitHeatBalFiniteDiff(EnergyPlusData &state)
     432             :     {
     433             : 
     434             :         // SUBROUTINE INFORMATION:
     435             :         //       AUTHOR         Richard J. Liesen
     436             :         //       DATE WRITTEN   Oct 2003
     437             :         //       RE-ENGINEERED  C O Pedersen 2006
     438             :         //                      B. Griffith May 2011 move begin-environment and every-timestep inits, cleanup formatting
     439             : 
     440             :         // PURPOSE OF THIS SUBROUTINE:
     441             :         // This subroutine sets the initial values for the FD moisture calculation
     442             : 
     443             :         // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
     444             :         bool ErrorsFound;
     445             : 
     446      158532 :         if (state.dataHeatBalFiniteDiffMgr->GetHBFiniteDiffInputFlag) {
     447             :             // Obtains conduction FD related parameters from input file
     448          12 :             GetCondFDInput(state);
     449          12 :             state.dataHeatBalFiniteDiffMgr->GetHBFiniteDiffInputFlag = false;
     450             :         }
     451             : 
     452      158532 :         auto &SurfaceFD = state.dataHeatBalFiniteDiffMgr->SurfaceFD;
     453      158532 :         ErrorsFound = false;
     454             : 
     455             :         // now do begin environment inits.
     456      158532 :         if (state.dataGlobal->BeginEnvrnFlag && state.dataHeatBalFiniteDiffMgr->MyEnvrnFlag) {
     457        1112 :             for (int SurfNum = 1; SurfNum <= state.dataSurface->TotSurfaces; ++SurfNum) {
     458        1036 :                 if (state.dataSurface->Surface(SurfNum).HeatTransferAlgorithm != DataSurfaces::HeatTransferModel::CondFD) continue;
     459         968 :                 if (state.dataSurface->Surface(SurfNum).Construction <= 0) continue; // Shading surface, not really a heat transfer surface
     460         968 :                 int ConstrNum = state.dataSurface->Surface(SurfNum).Construction;
     461         968 :                 if (state.dataConstruction->Construct(ConstrNum).TypeIsWindow) continue; //  Windows simulated in Window module
     462         968 :                 auto &thisSurface = SurfaceFD(SurfNum);
     463         968 :                 thisSurface.T = TempInitValue;
     464         968 :                 thisSurface.TOld = TempInitValue;
     465         968 :                 thisSurface.TT = TempInitValue;
     466         968 :                 thisSurface.Rhov = RhovInitValue;
     467         968 :                 thisSurface.RhovOld = RhovInitValue;
     468         968 :                 thisSurface.RhoT = RhovInitValue;
     469         968 :                 thisSurface.TD = TempInitValue;
     470         968 :                 thisSurface.TDT = TempInitValue;
     471         968 :                 thisSurface.TDTLast = TempInitValue;
     472         968 :                 thisSurface.TDOld = TempInitValue;
     473         968 :                 thisSurface.TDreport = TempInitValue;
     474         968 :                 thisSurface.RH = 0.0;
     475         968 :                 thisSurface.RHreport = 0.0;
     476         968 :                 thisSurface.EnthOld = EnthInitValue;
     477         968 :                 thisSurface.EnthNew = EnthInitValue;
     478         968 :                 thisSurface.EnthLast = EnthInitValue;
     479         968 :                 thisSurface.QDreport = 0.0;
     480         968 :                 thisSurface.CpDelXRhoS1 = 0.0;
     481         968 :                 thisSurface.CpDelXRhoS2 = 0.0;
     482         968 :                 thisSurface.TDpriortimestep = 0.0;
     483         968 :                 thisSurface.PhaseChangeState = 0;
     484         968 :                 thisSurface.PhaseChangeStateOld = 0;
     485         968 :                 thisSurface.PhaseChangeStateOldOld = 0;
     486         968 :                 thisSurface.PhaseChangeTemperatureReverse = 50;
     487             : 
     488         968 :                 state.dataMstBal->TempOutsideAirFD(SurfNum) = 0.0;
     489         968 :                 state.dataMstBal->RhoVaporAirOut(SurfNum) = 0.0;
     490         968 :                 state.dataMstBal->RhoVaporSurfIn(SurfNum) = 0.0;
     491         968 :                 state.dataMstBal->RhoVaporAirIn(SurfNum) = 0.0;
     492         968 :                 state.dataMstBal->HConvExtFD(SurfNum) = 0.0;
     493         968 :                 state.dataMstBal->HMassConvExtFD(SurfNum) = 0.0;
     494         968 :                 state.dataMstBal->HConvInFD(SurfNum) = 0.0;
     495         968 :                 state.dataMstBal->HMassConvInFD(SurfNum) = 0.0;
     496         968 :                 state.dataMstBal->HSkyFD(SurfNum) = 0.0;
     497         968 :                 state.dataMstBal->HGrndFD(SurfNum) = 0.0;
     498         968 :                 state.dataMstBal->HAirFD(SurfNum) = 0.0;
     499             :             }
     500          76 :             state.dataHeatBalFiniteDiffMgr->WarmupSurfTemp = 0;
     501          76 :             state.dataHeatBalFiniteDiffMgr->MyEnvrnFlag = false;
     502             :         }
     503      158532 :         if (!state.dataGlobal->BeginEnvrnFlag) {
     504      158456 :             state.dataHeatBalFiniteDiffMgr->MyEnvrnFlag = true;
     505             :         }
     506             : 
     507             :         // now do every timestep inits
     508             : 
     509     1930572 :         for (int SurfNum = 1; SurfNum <= state.dataSurface->TotSurfaces; ++SurfNum) {
     510     1772040 :             if (state.dataSurface->Surface(SurfNum).HeatTransferAlgorithm != DataSurfaces::HeatTransferModel::CondFD) continue;
     511     1654326 :             if (state.dataSurface->Surface(SurfNum).Construction <= 0) continue; // Shading surface, not really a heat transfer surface
     512     1654326 :             int ConstrNum = state.dataSurface->Surface(SurfNum).Construction;
     513     1654326 :             if (state.dataConstruction->Construct(ConstrNum).TypeIsWindow) continue; //  Windows simulated in Window module
     514     1654326 :             auto &thisSurface = SurfaceFD(SurfNum);
     515     1654326 :             thisSurface.T = thisSurface.TOld;
     516     1654326 :             thisSurface.Rhov = thisSurface.RhovOld;
     517     1654326 :             thisSurface.TD = thisSurface.TDOld;
     518     1654326 :             thisSurface.TDT = thisSurface.TDreport; // PT changes from TDold to TDreport
     519     1654326 :             thisSurface.TDTLast = thisSurface.TDOld;
     520     1654326 :             thisSurface.EnthOld = thisSurface.EnthOld;
     521     1654326 :             thisSurface.EnthNew = thisSurface.EnthOld;
     522     1654326 :             thisSurface.EnthLast = thisSurface.EnthOld;
     523     1654326 :             thisSurface.TDpriortimestep = thisSurface.TDreport; // Save TD for heat flux calc
     524             :         }
     525      158532 :     }
     526             : 
     527          12 :     void InitialInitHeatBalFiniteDiff(EnergyPlusData &state)
     528             :     {
     529             : 
     530             :         // SUBROUTINE INFORMATION:
     531             :         //       AUTHOR         Linda Lawrie
     532             :         //       DATE WRITTEN   March 2012
     533             : 
     534             :         // PURPOSE OF THIS SUBROUTINE:
     535             :         // This routine performs the original allocate, inits and setup output variables for the
     536             :         // module.
     537             : 
     538             :         // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
     539             :         Real64 dxn; // Intermediate calculation of nodal spacing. This is the full dx. There is
     540             :         // a half dxn thick node at each surface. dxn is the "capacitor" spacing.
     541             :         int Ipts1; // Intermediate calculation for number of full thickness nodes per layer. There
     542             :         // are always two half nodes at the layer faces.
     543             :         int Layer; // Loop counter
     544             :         int Delt;
     545             : 
     546             :         Real64 Alpha;
     547             :         Real64 mAlpha;
     548             :         Real64 StabilityTemp;
     549             :         Real64 StabilityMoist;
     550             :         Real64 a;
     551             :         Real64 b;
     552             :         Real64 c;
     553             :         Real64 d;
     554             :         Real64 kt;
     555             :         Real64 RhoS;
     556             :         Real64 Por;
     557             :         Real64 Cp;
     558             :         Real64 Dv;
     559             :         Real64 DeltaTimestep;      // zone timestep in seconds, for local check of properties
     560             :         Real64 ThicknessThreshold; // min thickness consistent with other thermal properties, for local check
     561             : 
     562          12 :         auto &ConstructFD = state.dataHeatBalFiniteDiffMgr->ConstructFD;
     563          12 :         auto &SigmaR = state.dataHeatBalFiniteDiffMgr->SigmaR;
     564          12 :         auto &SigmaC = state.dataHeatBalFiniteDiffMgr->SigmaC;
     565          12 :         auto &SurfaceFD = state.dataHeatBalFiniteDiffMgr->SurfaceFD;
     566          12 :         auto &QHeatInFlux = state.dataHeatBalFiniteDiffMgr->QHeatInFlux;
     567          12 :         auto &QHeatOutFlux = state.dataHeatBalFiniteDiffMgr->QHeatOutFlux;
     568             : 
     569          12 :         ConstructFD.allocate(state.dataHeatBal->TotConstructs);
     570          12 :         SigmaR.allocate(state.dataHeatBal->TotConstructs);
     571          12 :         SigmaC.allocate(state.dataHeatBal->TotConstructs);
     572             : 
     573          12 :         SurfaceFD.allocate(state.dataSurface->TotSurfaces);
     574          12 :         QHeatInFlux.allocate(state.dataSurface->TotSurfaces);
     575          12 :         QHeatOutFlux.allocate(state.dataSurface->TotSurfaces);
     576             : 
     577             :         // And then initialize
     578         160 :         for (int SurfNum = 1; SurfNum <= state.dataSurface->TotSurfaces; ++SurfNum) {
     579         148 :             QHeatInFlux(SurfNum) = 0.0;
     580         148 :             QHeatOutFlux(SurfNum) = 0.0;
     581         148 :             state.dataHeatBalSurf->SurfOpaqInsFaceCondFlux(SurfNum) = 0.0;
     582         148 :             state.dataHeatBalSurf->SurfOpaqOutFaceCondFlux(SurfNum) = 0.0;
     583             :         }
     584             : 
     585             :         // Setup Output Variables
     586             : 
     587             :         //  set a Delt that fits the zone time step and keeps it below 200s.
     588             : 
     589          12 :         state.dataHeatBalFiniteDiffMgr->fracTimeStepZone_Hour = 1.0 / double(state.dataGlobal->NumOfTimeStepInHour);
     590             : 
     591          12 :         for (int index = 1; index <= 20; ++index) {
     592          12 :             Delt = (state.dataHeatBalFiniteDiffMgr->fracTimeStepZone_Hour * Constant::SecInHour) /
     593             :                    index; // TimeStepZone = Zone time step in fractional hours
     594          12 :             if (Delt <= 200) break;
     595             :         }
     596             : 
     597          63 :         for (int ConstrNum = 1; ConstrNum <= state.dataHeatBal->TotConstructs; ++ConstrNum) {
     598          51 :             auto const &thisConstruct = state.dataConstruction->Construct(ConstrNum);
     599          51 :             auto &thisConstructFD = ConstructFD(ConstrNum);
     600             :             // Need to skip window constructions, IRT, air wall and construction not in use.
     601             :             // Need to also skip constructions for surfaces that do not use CondFD.
     602          51 :             if (thisConstruct.TypeIsWindow) continue;
     603          45 :             if (thisConstruct.TypeIsIRT) continue;
     604          45 :             if (thisConstruct.TypeIsAirBoundary) continue;
     605          45 :             if (!thisConstruct.IsUsed) continue;
     606          45 :             if (!findAnySurfacesUsingConstructionAndCondFD(state, ConstrNum)) continue;
     607             : 
     608          42 :             thisConstructFD.Name.allocate(thisConstruct.TotLayers);
     609          42 :             thisConstructFD.Thickness.allocate(thisConstruct.TotLayers);
     610          42 :             thisConstructFD.NodeNumPoint.allocate(thisConstruct.TotLayers);
     611          42 :             thisConstructFD.DelX.allocate(thisConstruct.TotLayers);
     612          42 :             thisConstructFD.TempStability.allocate(thisConstruct.TotLayers);
     613          42 :             thisConstructFD.MoistStability.allocate(thisConstruct.TotLayers);
     614             : 
     615          42 :             int TotNodes = 0;
     616          42 :             SigmaR(ConstrNum) = 0.0;
     617          42 :             SigmaC(ConstrNum) = 0.0;
     618             : 
     619         165 :             for (Layer = 1; Layer <= thisConstruct.TotLayers; ++Layer) { // Begin layer loop ...
     620             : 
     621             :                 // Loop through all of the layers in the current construct. The purpose
     622             :                 // of this loop is to define the thermal properties and to.
     623             :                 // determine the total number of full size nodes in each layer.
     624             :                 // The number of temperature points is one more than this
     625             :                 // because of the two half nodes at the layer faces.
     626             :                 // The calculation of dxn used here is based on a standard stability
     627             :                 // criteria for explicit finite difference solutions.  This criteria
     628             :                 // was chosen not because it is viewed to be correct, but rather for
     629             :                 // lack of any better criteria at this time.  The use of a Fourier
     630             :                 // number based criteria such as this is probably physically correct.
     631             :                 //  Change to implicit formulation still uses explicit stability, but
     632             :                 // now there are special equations for R-only layers.
     633             : 
     634         123 :                 int CurrentLayer = thisConstruct.LayerPoint(Layer);
     635         123 :                 auto *thisMaterial = dynamic_cast<Material::MaterialChild *>(state.dataMaterial->Material(CurrentLayer));
     636         123 :                 assert(thisMaterial != nullptr);
     637             : 
     638         123 :                 thisConstructFD.Name(Layer) = thisMaterial->Name;
     639         123 :                 thisConstructFD.Thickness(Layer) = thisMaterial->Thickness;
     640             : 
     641             :                 // Do some quick error checks for this section.
     642             : 
     643         123 :                 if (thisMaterial->ROnly) { // Rlayer
     644             : 
     645             :                     //  These values are only needed temporarily and to calculate flux,
     646             :                     //   Layer will be handled
     647             :                     //  as a pure R in the temperature calc.
     648             :                     // assign other properties based on resistance
     649             : 
     650           8 :                     thisMaterial->SpecHeat = 0.0001;
     651           8 :                     thisMaterial->Density = 1.0;
     652           8 :                     thisMaterial->Thickness = 0.1; //  arbitrary thickness for R layer
     653           8 :                     thisMaterial->Conductivity = thisMaterial->Thickness / thisMaterial->Resistance;
     654           8 :                     kt = thisMaterial->Conductivity;
     655           8 :                     thisConstructFD.Thickness(Layer) = thisMaterial->Thickness;
     656             : 
     657           8 :                     SigmaR(ConstrNum) += thisMaterial->Resistance; // add resistance of R layer
     658           8 :                     SigmaC(ConstrNum) += 0.0;                      //  no capacitance for R layer
     659             : 
     660           8 :                     Alpha = kt / (thisMaterial->Density * thisMaterial->SpecHeat);
     661             : 
     662           8 :                     mAlpha = 0.0;
     663             : 
     664         115 :                 } else if (thisMaterial->group == Material::Group::Air) { //  Group 1 = Air
     665             : 
     666             :                     //  Again, these values are only needed temporarily and to calculate flux,
     667             :                     //   Air layer will be handled
     668             :                     //  as a pure R in the temperature calc.
     669             :                     // assign
     670             :                     // other properties based on resistance
     671             : 
     672           0 :                     thisMaterial->SpecHeat = 0.0001;
     673           0 :                     thisMaterial->Density = 1.0;
     674           0 :                     thisMaterial->Thickness = 0.1; //  arbitrary thickness for R layer
     675           0 :                     thisMaterial->Conductivity = thisMaterial->Thickness / thisMaterial->Resistance;
     676           0 :                     kt = thisMaterial->Conductivity;
     677           0 :                     thisConstructFD.Thickness(Layer) = thisMaterial->Thickness;
     678             : 
     679           0 :                     SigmaR(ConstrNum) += thisMaterial->Resistance; // add resistance of R layer
     680           0 :                     SigmaC(ConstrNum) += 0.0;                      //  no capacitance for R layer
     681             : 
     682           0 :                     Alpha = kt / (thisMaterial->Density * thisMaterial->SpecHeat);
     683           0 :                     mAlpha = 0.0;
     684         115 :                 } else if (thisConstruct.TypeIsIRT) { // make similar to air? (that didn't seem to work well)
     685           0 :                     ShowSevereError(state,
     686           0 :                                     format("InitHeatBalFiniteDiff: Construction =\"{}\" uses Material:InfraredTransparent. Cannot be used currently "
     687             :                                            "with finite difference calculations.",
     688           0 :                                            thisConstruct.Name));
     689           0 :                     if (thisConstruct.IsUsed) {
     690           0 :                         ShowContinueError(state, "...since this construction is used in a surface, the simulation is not allowed.");
     691             :                     } else {
     692           0 :                         ShowContinueError(state, "...if this construction were used in a surface, the simulation would be terminated.");
     693             :                     }
     694           0 :                     continue;
     695             :                 } else {
     696             :                     //    Regular material Properties
     697         115 :                     a = thisMaterial->MoistACoeff;
     698         115 :                     b = thisMaterial->MoistBCoeff;
     699         115 :                     c = thisMaterial->MoistCCoeff;
     700         115 :                     d = thisMaterial->MoistDCoeff;
     701         115 :                     kt = thisMaterial->Conductivity;
     702         115 :                     RhoS = thisMaterial->Density;
     703         115 :                     Por = thisMaterial->Porosity;
     704         115 :                     Cp = thisMaterial->SpecHeat;
     705             :                     // Need Resistance for reg layer
     706         115 :                     thisMaterial->Resistance = thisMaterial->Thickness / thisMaterial->Conductivity;
     707         115 :                     Dv = thisMaterial->VaporDiffus;
     708         115 :                     SigmaR(ConstrNum) += thisMaterial->Resistance; // add resistance
     709         115 :                     SigmaC(ConstrNum) += thisMaterial->Density * thisMaterial->SpecHeat * thisMaterial->Thickness;
     710         115 :                     Alpha = kt / (RhoS * Cp);
     711         115 :                     mAlpha = 0.0;
     712             : 
     713             :                     // check for Material layers that are too thin and highly conductivity (not appropriate for surface models)
     714         115 :                     if (Alpha > DataHeatBalance::HighDiffusivityThreshold) {
     715           4 :                         DeltaTimestep = state.dataGlobal->TimeStepZoneSec;
     716           4 :                         ThicknessThreshold = std::sqrt(Alpha * DeltaTimestep * 3.0);
     717           4 :                         if (thisMaterial->Thickness < ThicknessThreshold) {
     718           0 :                             ShowSevereError(
     719             :                                 state,
     720           0 :                                 format(
     721             :                                     "InitialInitHeatBalFiniteDiff: Found Material that is too thin and/or too highly conductive, material name = {}",
     722           0 :                                     thisMaterial->Name));
     723           0 :                             ShowContinueError(state,
     724           0 :                                               format("High conductivity Material layers are not well supported by Conduction Finite Difference, "
     725             :                                                      "material conductivity = {:.3R} [W/m-K]",
     726           0 :                                                      thisMaterial->Conductivity));
     727           0 :                             ShowContinueError(state, format("Material thermal diffusivity = {:.3R} [m2/s]", Alpha));
     728           0 :                             ShowContinueError(
     729           0 :                                 state, format("Material with this thermal diffusivity should have thickness > {:.5R} [m]", ThicknessThreshold));
     730           0 :                             if (thisMaterial->Thickness < DataHeatBalance::ThinMaterialLayerThreshold) {
     731           0 :                                 ShowContinueError(
     732           0 :                                     state, format("Material may be too thin to be modeled well, thickness = {:.5R} [m]", thisMaterial->Thickness));
     733           0 :                                 ShowContinueError(state,
     734           0 :                                                   format("Material with this thermal diffusivity should have thickness > {:.5R} [m]",
     735             :                                                          DataHeatBalance::ThinMaterialLayerThreshold));
     736             :                             }
     737           0 :                             ShowFatalError(state, "Preceding conditions cause termination.");
     738             :                         }
     739             :                     }
     740             : 
     741             :                 } //  R, Air  or regular material properties and parameters
     742             : 
     743             :                 // Proceed with setting node sizes in layers
     744             : 
     745         123 :                 dxn = std::sqrt(Alpha * Delt * state.dataHeatBalFiniteDiffMgr->SpaceDescritConstant); // The Fourier number is set using user constant
     746             : 
     747             :                 // number of nodes=thickness/spacing.  This is number of full size node spaces across layer.
     748         123 :                 Ipts1 = int(thisMaterial->Thickness / dxn);
     749             :                 //  set high conductivity layers to a single full size node thickness. (two half nodes)
     750         123 :                 if (Ipts1 <= 1) Ipts1 = 1;
     751         123 :                 if (thisMaterial->ROnly || thisMaterial->group == Material::Group::Air) {
     752             : 
     753           8 :                     Ipts1 = 1; //  single full node in R layers- surfaces of adjacent material or inside/outside layer
     754             :                 }
     755             : 
     756         123 :                 dxn = thisMaterial->Thickness / double(Ipts1); // full node thickness
     757             : 
     758         123 :                 StabilityTemp = Alpha * Delt / pow_2(dxn);
     759         123 :                 StabilityMoist = mAlpha * Delt / pow_2(dxn);
     760         123 :                 thisConstructFD.TempStability(Layer) = StabilityTemp;
     761         123 :                 thisConstructFD.MoistStability(Layer) = StabilityMoist;
     762         123 :                 thisConstructFD.DelX(Layer) = dxn;
     763             : 
     764         123 :                 TotNodes += Ipts1;                           //  number of full size nodes
     765         123 :                 thisConstructFD.NodeNumPoint(Layer) = Ipts1; //  number of full size nodes
     766             :             }                                                //  end of layer loop.
     767             : 
     768          42 :             thisConstructFD.TotNodes = TotNodes;
     769          42 :             thisConstructFD.DeltaTime = Delt;
     770             : 
     771             :         } // End of Construction Loop.  TotNodes in each construction now set
     772             : 
     773             :         // now determine x location, or distance that nodes are from the outside face in meters
     774          63 :         for (int ConstrNum = 1; ConstrNum <= state.dataHeatBal->TotConstructs; ++ConstrNum) {
     775          51 :             auto &thisConstructFD = ConstructFD(ConstrNum);
     776          51 :             auto const &thisConstruct = state.dataConstruction->Construct(ConstrNum);
     777          51 :             if (thisConstructFD.TotNodes > 0) {
     778          42 :                 thisConstructFD.NodeXlocation.allocate(thisConstructFD.TotNodes + 1);
     779          42 :                 thisConstructFD.NodeXlocation = 0.0; // init them all
     780          42 :                 Ipts1 = 0;                           // init counter
     781         165 :                 for (Layer = 1; Layer <= thisConstruct.TotLayers; ++Layer) {
     782         123 :                     int OutwardMatLayerNum = Layer - 1;
     783         411 :                     for (int LayerNode = 1; LayerNode <= thisConstructFD.NodeNumPoint(Layer); ++LayerNode) {
     784         288 :                         ++Ipts1;
     785         288 :                         if (Ipts1 == 1) {
     786          42 :                             thisConstructFD.NodeXlocation(Ipts1) = 0.0; // first node is on outside face
     787             : 
     788         246 :                         } else if (LayerNode == 1) {
     789          81 :                             if (OutwardMatLayerNum > 0 && OutwardMatLayerNum <= thisConstruct.TotLayers) {
     790             :                                 // later nodes are Delx away from previous, but use Delx from previous layer
     791          81 :                                 thisConstructFD.NodeXlocation(Ipts1) =
     792          81 :                                     thisConstructFD.NodeXlocation(Ipts1 - 1) + thisConstructFD.DelX(OutwardMatLayerNum);
     793             :                             }
     794             :                         } else {
     795             :                             // later nodes are Delx away from previous
     796         165 :                             thisConstructFD.NodeXlocation(Ipts1) = thisConstructFD.NodeXlocation(Ipts1 - 1) + thisConstructFD.DelX(Layer);
     797             :                         }
     798             :                     }
     799             :                 }
     800          42 :                 Layer = thisConstruct.TotLayers;
     801          42 :                 ++Ipts1;
     802          42 :                 thisConstructFD.NodeXlocation(Ipts1) = thisConstructFD.NodeXlocation(Ipts1 - 1) + thisConstructFD.DelX(Layer);
     803             :             }
     804             :         }
     805             : 
     806         160 :         for (int Surf = 1; Surf <= state.dataSurface->TotSurfaces; ++Surf) {
     807         148 :             if (!state.dataSurface->Surface(Surf).HeatTransSurf) continue;
     808         148 :             if (state.dataSurface->Surface(Surf).Class == DataSurfaces::SurfaceClass::Window) continue;
     809         142 :             if (state.dataSurface->Surface(Surf).HeatTransferAlgorithm != DataSurfaces::HeatTransferModel::CondFD) continue;
     810         137 :             int ConstrNum = state.dataSurface->Surface(Surf).Construction;
     811         137 :             int TotNodes = ConstructFD(ConstrNum).TotNodes;
     812         137 :             int TotLayers = state.dataConstruction->Construct(ConstrNum).TotLayers;
     813             : 
     814             :             // Allocate the Surface Arrays
     815         137 :             SurfaceFD(Surf).T.allocate(TotNodes + 1);
     816         137 :             SurfaceFD(Surf).TOld.allocate(TotNodes + 1);
     817         137 :             SurfaceFD(Surf).TT.allocate(TotNodes + 1);
     818         137 :             SurfaceFD(Surf).Rhov.allocate(TotNodes + 1);
     819         137 :             SurfaceFD(Surf).RhovOld.allocate(TotNodes + 1);
     820         137 :             SurfaceFD(Surf).RhoT.allocate(TotNodes + 1);
     821         137 :             SurfaceFD(Surf).TD.allocate(TotNodes + 1);
     822         137 :             SurfaceFD(Surf).TDT.allocate(TotNodes + 1);
     823         137 :             SurfaceFD(Surf).TDTLast.allocate(TotNodes + 1);
     824         137 :             SurfaceFD(Surf).TDOld.allocate(TotNodes + 1);
     825         137 :             SurfaceFD(Surf).TDreport.allocate(TotNodes + 1);
     826         137 :             SurfaceFD(Surf).RH.allocate(TotNodes + 1);
     827         137 :             SurfaceFD(Surf).RHreport.allocate(TotNodes + 1);
     828         137 :             SurfaceFD(Surf).EnthOld.allocate(TotNodes + 1);
     829         137 :             SurfaceFD(Surf).EnthNew.allocate(TotNodes + 1);
     830         137 :             SurfaceFD(Surf).EnthLast.allocate(TotNodes + 1);
     831         137 :             SurfaceFD(Surf).QDreport.allocate(TotNodes + 1);
     832         137 :             SurfaceFD(Surf).CpDelXRhoS1.allocate(TotNodes + 1);
     833         137 :             SurfaceFD(Surf).CpDelXRhoS2.allocate(TotNodes + 1);
     834         137 :             SurfaceFD(Surf).TDpriortimestep.allocate(TotNodes + 1);
     835         137 :             SurfaceFD(Surf).PhaseChangeState.allocate(TotNodes + 1);
     836         137 :             SurfaceFD(Surf).PhaseChangeStateOld.allocate(TotNodes + 1);
     837         137 :             SurfaceFD(Surf).PhaseChangeStateOldOld.allocate(TotNodes + 1);
     838         137 :             SurfaceFD(Surf).PhaseChangeTemperatureReverse.allocate(TotNodes + 1);
     839         137 :             SurfaceFD(Surf).condMaterialActuators.allocate(TotLayers);
     840         137 :             SurfaceFD(Surf).specHeatMaterialActuators.allocate(TotLayers);
     841         137 :             SurfaceFD(Surf).condNodeReport.allocate(TotNodes + 1);
     842         137 :             SurfaceFD(Surf).specHeatNodeReport.allocate(TotNodes + 1);
     843         137 :             SurfaceFD(Surf).heatSourceFluxMaterialActuators.allocate(TotLayers - 1);
     844         137 :             SurfaceFD(Surf).heatSourceInternalFluxLayerReport.allocate(TotLayers - 1);
     845         137 :             SurfaceFD(Surf).heatSourceInternalFluxEnergyLayerReport.allocate(TotLayers - 1);
     846         137 :             SurfaceFD(Surf).heatSourceEMSFluxLayerReport.allocate(TotLayers - 1);
     847         137 :             SurfaceFD(Surf).heatSourceEMSFluxEnergyLayerReport.allocate(TotLayers - 1);
     848             : 
     849             :             // Initialize the allocated arrays.
     850         137 :             SurfaceFD(Surf).T = TempInitValue;
     851         137 :             SurfaceFD(Surf).TOld = TempInitValue;
     852         137 :             SurfaceFD(Surf).TT = TempInitValue;
     853         137 :             SurfaceFD(Surf).Rhov = RhovInitValue;
     854         137 :             SurfaceFD(Surf).RhovOld = RhovInitValue;
     855         137 :             SurfaceFD(Surf).RhoT = RhovInitValue;
     856         137 :             SurfaceFD(Surf).TD = TempInitValue;
     857         137 :             SurfaceFD(Surf).TDT = TempInitValue;
     858         137 :             SurfaceFD(Surf).TDTLast = TempInitValue;
     859         137 :             SurfaceFD(Surf).TDOld = TempInitValue;
     860         137 :             SurfaceFD(Surf).TDreport = TempInitValue;
     861         137 :             SurfaceFD(Surf).RH = 0.0;
     862         137 :             SurfaceFD(Surf).RHreport = 0.0;
     863         137 :             SurfaceFD(Surf).EnthOld = EnthInitValue;
     864         137 :             SurfaceFD(Surf).EnthNew = EnthInitValue;
     865         137 :             SurfaceFD(Surf).EnthLast = EnthInitValue;
     866         137 :             SurfaceFD(Surf).QDreport = 0.0;
     867         137 :             SurfaceFD(Surf).CpDelXRhoS1 = 0.0;
     868         137 :             SurfaceFD(Surf).CpDelXRhoS2 = 0.0;
     869         137 :             SurfaceFD(Surf).TDpriortimestep = 0.0;
     870         137 :             SurfaceFD(Surf).PhaseChangeState = 0;
     871         137 :             SurfaceFD(Surf).PhaseChangeStateOld = 0;
     872         137 :             SurfaceFD(Surf).PhaseChangeStateOldOld = 0;
     873         137 :             SurfaceFD(Surf).PhaseChangeTemperatureReverse = 50;
     874         137 :             SurfaceFD(Surf).condNodeReport = 0.0;
     875         137 :             SurfaceFD(Surf).specHeatNodeReport = 0.0;
     876         137 :             SurfaceFD(Surf).heatSourceInternalFluxLayerReport = 0.0;
     877         137 :             SurfaceFD(Surf).heatSourceInternalFluxEnergyLayerReport = 0.0;
     878         137 :             SurfaceFD(Surf).heatSourceEMSFluxLayerReport = 0.0;
     879         137 :             SurfaceFD(Surf).heatSourceEMSFluxEnergyLayerReport = 0.0;
     880             : 
     881             :             // Setup EMS data
     882         567 :             for (int lay = 1; lay <= TotLayers; ++lay) {
     883             :                 // Setup material layer names actuators
     884         430 :                 int matLay = state.dataConstruction->Construct(ConstrNum).LayerPoint(lay);
     885             :                 // Actuator name format: "{SurfName}:{MaterialLayerName}"
     886         430 :                 std::string actName = fmt::format("{}:{}", state.dataSurface->Surface(Surf).Name, state.dataMaterial->Material(matLay)->Name);
     887         430 :                 SurfaceFD(Surf).condMaterialActuators(lay).actuatorName = actName;
     888         430 :                 SurfaceFD(Surf).specHeatMaterialActuators(lay).actuatorName = actName;
     889             : 
     890             :                 // only setup for heat source actuator for layers 1 to N-1
     891         430 :                 if (lay != TotLayers) {
     892         293 :                     SurfaceFD(Surf).heatSourceFluxMaterialActuators(lay).actuatorName = actName;
     893             :                 }
     894         430 :             }
     895             :         }
     896             : 
     897         160 :         for (int SurfNum = 1; SurfNum <= state.dataSurface->TotSurfaces; ++SurfNum) {
     898         148 :             if (!state.dataSurface->Surface(SurfNum).HeatTransSurf) continue;
     899         148 :             if (state.dataSurface->Surface(SurfNum).Class == DataSurfaces::SurfaceClass::Window) continue;
     900         142 :             if (state.dataSurface->Surface(SurfNum).HeatTransferAlgorithm != DataSurfaces::HeatTransferModel::CondFD) continue;
     901             : 
     902         137 :             SetupOutputVariable(state,
     903             :                                 "CondFD Inner Solver Loop Iteration Count",
     904             :                                 Constant::Units::None,
     905         137 :                                 SurfaceFD(SurfNum).GSloopCounter,
     906             :                                 OutputProcessor::TimeStepType::Zone,
     907             :                                 OutputProcessor::StoreType::Sum,
     908         137 :                                 state.dataSurface->Surface(SurfNum).Name);
     909             : 
     910             :             // Setup EMS Material Actuators for Conductivity and Specific Heat
     911         137 :             int ConstrNum = state.dataSurface->Surface(SurfNum).Construction;
     912             : 
     913         137 :             auto const &thisConstruct = state.dataConstruction->Construct(ConstrNum);
     914             :             // Setup internal heat source output variables
     915             :             // Only setup for layers 1 to N-1
     916         430 :             for (int lay = 1; lay < thisConstruct.TotLayers; ++lay) {
     917         879 :                 SetupOutputVariable(state,
     918         586 :                                     format("CondFD Internal Heat Source Power After Layer {}", lay),
     919             :                                     Constant::Units::W,
     920         293 :                                     SurfaceFD(SurfNum).heatSourceInternalFluxLayerReport(lay),
     921             :                                     OutputProcessor::TimeStepType::Zone,
     922             :                                     OutputProcessor::StoreType::Average,
     923         293 :                                     state.dataSurface->Surface(SurfNum).Name);
     924         879 :                 SetupOutputVariable(state,
     925         586 :                                     format("CondFD Internal Heat Source Energy After Layer {}", lay),
     926             :                                     Constant::Units::J,
     927         293 :                                     SurfaceFD(SurfNum).heatSourceInternalFluxEnergyLayerReport(lay),
     928             :                                     OutputProcessor::TimeStepType::Zone,
     929             :                                     OutputProcessor::StoreType::Sum,
     930         293 :                                     state.dataSurface->Surface(SurfNum).Name);
     931             :             }
     932             : 
     933         137 :             if (state.dataGlobal->AnyEnergyManagementSystemInModel) {
     934          50 :                 for (int lay = 1; lay <= thisConstruct.TotLayers; ++lay) {
     935          76 :                     EnergyPlus::SetupEMSActuator(state,
     936             :                                                  "CondFD Surface Material Layer",
     937          38 :                                                  SurfaceFD(SurfNum).condMaterialActuators(lay).actuatorName,
     938             :                                                  "Thermal Conductivity",
     939             :                                                  "[W/m-K]",
     940          38 :                                                  SurfaceFD(SurfNum).condMaterialActuators(lay).isActuated,
     941          38 :                                                  SurfaceFD(SurfNum).condMaterialActuators(lay).actuatedValue);
     942          76 :                     EnergyPlus::SetupEMSActuator(state,
     943             :                                                  "CondFD Surface Material Layer",
     944          38 :                                                  SurfaceFD(SurfNum).specHeatMaterialActuators(lay).actuatorName,
     945             :                                                  "Specific Heat",
     946             :                                                  "[J/kg-C]",
     947          38 :                                                  SurfaceFD(SurfNum).specHeatMaterialActuators(lay).isActuated,
     948          38 :                                                  SurfaceFD(SurfNum).specHeatMaterialActuators(lay).actuatedValue);
     949             :                 }
     950             : 
     951             :                 // Setup EMS Actuator and Output Variables for Heat Flux
     952             :                 // Only setup for layers 1 to N-1
     953          38 :                 for (int lay = 1; lay < thisConstruct.TotLayers; ++lay) {
     954          52 :                     EnergyPlus::SetupEMSActuator(state,
     955             :                                                  "CondFD Surface Material Layer",
     956          26 :                                                  SurfaceFD(SurfNum).heatSourceFluxMaterialActuators(lay).actuatorName,
     957             :                                                  "Heat Flux",
     958             :                                                  "[W/m2]",
     959          26 :                                                  SurfaceFD(SurfNum).heatSourceFluxMaterialActuators(lay).isActuated,
     960          26 :                                                  SurfaceFD(SurfNum).heatSourceFluxMaterialActuators(lay).actuatedValue);
     961          78 :                     SetupOutputVariable(state,
     962          52 :                                         format("CondFD EMS Heat Source Power After Layer {}", lay),
     963             :                                         Constant::Units::W,
     964          26 :                                         SurfaceFD(SurfNum).heatSourceEMSFluxLayerReport(lay),
     965             :                                         OutputProcessor::TimeStepType::Zone,
     966             :                                         OutputProcessor::StoreType::Average,
     967          26 :                                         state.dataSurface->Surface(SurfNum).Name);
     968          78 :                     SetupOutputVariable(state,
     969          52 :                                         format("CondFD EMS Heat Source Energy After Layer {}", lay),
     970             :                                         Constant::Units::J,
     971          26 :                                         SurfaceFD(SurfNum).heatSourceEMSFluxEnergyLayerReport(lay),
     972             :                                         OutputProcessor::TimeStepType::Zone,
     973             :                                         OutputProcessor::StoreType::Sum,
     974          26 :                                         state.dataSurface->Surface(SurfNum).Name,
     975             :                                         Constant::eResource::Electricity,
     976             :                                         OutputProcessor::Group::Building,
     977             :                                         OutputProcessor::EndUseCat::Heating);
     978             :                 }
     979             :             }
     980             : 
     981         137 :             int TotNodes = ConstructFD(state.dataSurface->Surface(SurfNum).Construction).TotNodes; // Full size nodes, start with outside face.
     982        1378 :             for (int node = 1; node <= TotNodes + 1; ++node) {                                     // include inside face node
     983        3723 :                 SetupOutputVariable(state,
     984        2482 :                                     format("CondFD Surface Temperature Node {}", node),
     985             :                                     Constant::Units::C,
     986        1241 :                                     SurfaceFD(SurfNum).TDreport(node),
     987             :                                     OutputProcessor::TimeStepType::Zone,
     988             :                                     OutputProcessor::StoreType::Average,
     989        1241 :                                     state.dataSurface->Surface(SurfNum).Name);
     990        3723 :                 SetupOutputVariable(state,
     991        2482 :                                     format("CondFD Surface Heat Flux Node {}", node),
     992             :                                     Constant::Units::W_m2,
     993        1241 :                                     SurfaceFD(SurfNum).QDreport(node),
     994             :                                     OutputProcessor::TimeStepType::Zone,
     995             :                                     OutputProcessor::StoreType::Average,
     996        1241 :                                     state.dataSurface->Surface(SurfNum).Name);
     997        1241 :                 SetupOutputVariable(state,
     998        2482 :                                     format("CondFD Phase Change State {}", node),
     999             :                                     Constant::Units::None,
    1000        1241 :                                     SurfaceFD(SurfNum).PhaseChangeState(node),
    1001             :                                     OutputProcessor::TimeStepType::Zone,
    1002             :                                     OutputProcessor::StoreType::Average,
    1003        1241 :                                     state.dataSurface->Surface(SurfNum).Name);
    1004        1241 :                 SetupOutputVariable(state,
    1005        2482 :                                     format("CondFD Phase Change Previous State {}", node),
    1006             :                                     Constant::Units::None,
    1007        1241 :                                     SurfaceFD(SurfNum).PhaseChangeStateOld(node),
    1008             :                                     OutputProcessor::TimeStepType::Zone,
    1009             :                                     OutputProcessor::StoreType::Average,
    1010        1241 :                                     state.dataSurface->Surface(SurfNum).Name);
    1011        3723 :                 SetupOutputVariable(state,
    1012        2482 :                                     format("CondFD Phase Change Node Temperature {}", node),
    1013             :                                     Constant::Units::C,
    1014        1241 :                                     SurfaceFD(SurfNum).TDT(node),
    1015             :                                     OutputProcessor::TimeStepType::Zone,
    1016             :                                     OutputProcessor::StoreType::Average,
    1017        1241 :                                     state.dataSurface->Surface(SurfNum).Name);
    1018        3723 :                 SetupOutputVariable(state,
    1019        2482 :                                     format("CondFD Phase Change Node Conductivity {}", node),
    1020             :                                     Constant::Units::W_mK,
    1021        1241 :                                     SurfaceFD(SurfNum).condNodeReport(node),
    1022             :                                     OutputProcessor::TimeStepType::Zone,
    1023             :                                     OutputProcessor::StoreType::Average,
    1024        1241 :                                     state.dataSurface->Surface(SurfNum).Name);
    1025        3723 :                 SetupOutputVariable(state,
    1026        2482 :                                     format("CondFD Phase Change Node Specific Heat {}", node),
    1027             :                                     Constant::Units::J_kgK,
    1028        1241 :                                     SurfaceFD(SurfNum).specHeatNodeReport(node),
    1029             :                                     OutputProcessor::TimeStepType::Zone,
    1030             :                                     OutputProcessor::StoreType::Average,
    1031        1241 :                                     state.dataSurface->Surface(SurfNum).Name);
    1032        1241 :                 if (state.dataGlobal->DisplayAdvancedReportVariables) {
    1033         303 :                     SetupOutputVariable(state,
    1034         202 :                                         format("CondFD Surface Heat Capacitance Outer Half Node {}", node),
    1035             :                                         Constant::Units::W_m2K,
    1036         101 :                                         SurfaceFD(SurfNum).CpDelXRhoS1(node),
    1037             :                                         OutputProcessor::TimeStepType::Zone,
    1038             :                                         OutputProcessor::StoreType::Average,
    1039         101 :                                         state.dataSurface->Surface(SurfNum).Name);
    1040         303 :                     SetupOutputVariable(state,
    1041         202 :                                         format("CondFD Surface Heat Capacitance Inner Half Node {}", node),
    1042             :                                         Constant::Units::W_m2K,
    1043         101 :                                         SurfaceFD(SurfNum).CpDelXRhoS2(node),
    1044             :                                         OutputProcessor::TimeStepType::Zone,
    1045             :                                         OutputProcessor::StoreType::Average,
    1046         101 :                                         state.dataSurface->Surface(SurfNum).Name);
    1047             :                 }
    1048             :             }
    1049             : 
    1050             :         } // End of the Surface Loop for Report Variable Setup
    1051             : 
    1052          12 :         ReportFiniteDiffInits(state); // Report the results from the Finite Diff Inits
    1053          12 :     }
    1054             : 
    1055          13 :     int numNodesInMaterialLayer(EnergyPlusData &state, std::string const &surfName, std::string const &matName)
    1056             :     {
    1057          28 :         for (auto const &surface : state.dataSurface->Surface) {
    1058          28 :             if (surface.Name == surfName) {
    1059          13 :                 int constrNum = surface.Construction;
    1060          34 :                 for (int lay = 1; lay <= state.dataConstruction->Construct(constrNum).TotLayers; ++lay) {
    1061          34 :                     int matLay = state.dataConstruction->Construct(constrNum).LayerPoint(lay);
    1062          34 :                     if (state.dataMaterial->Material(matLay)->Name == matName) {
    1063          13 :                         return state.dataHeatBalFiniteDiffMgr->ConstructFD(constrNum).NodeNumPoint(lay);
    1064             :                     }
    1065             :                 }
    1066             :             }
    1067          26 :         }
    1068             : 
    1069           0 :         return 0;
    1070             :     }
    1071             : 
    1072     1374592 :     void relax_array(Array1D<Real64> &a,       // Array to relax
    1073             :                      Array1D<Real64> const &b, // Array to relax towards
    1074             :                      Real64 const r            // Relaxation factor [0-1]
    1075             :     )
    1076             :     {
    1077     1374592 :         assert(equal_dimensions(a, b));
    1078     1374592 :         assert((0.0 <= r) && (r <= 1.0));
    1079     1374592 :         Real64 const q(1.0 - r);
    1080    17265002 :         for (int i = a.l(), e = a.u(); i <= e; ++i) {
    1081    15890410 :             a(i) = r * b(i) + q * a(i);
    1082             :         }
    1083     1374592 :     }
    1084             : 
    1085    14564104 :     Real64 sum_array_diff(Array1D<Real64> const &a, Array1D<Real64> const &b)
    1086             :     {
    1087    14564104 :         assert(equal_dimensions(a, b));
    1088    14564104 :         Real64 s(0.0);
    1089   165106691 :         for (int i = a.l(), e = a.u(); i <= e; ++i) {
    1090   150542587 :             s += a(i) - b(i); //? Should this be in abs?
    1091             :         }
    1092    14564104 :         return s;
    1093             :     }
    1094             : 
    1095    10239636 :     void CalcHeatBalFiniteDiff(EnergyPlusData &state,
    1096             :                                int const Surf,        // Surface number
    1097             :                                Real64 &SurfTempInTmp, // INSIDE SURFACE TEMPERATURE OF EACH HEAT TRANSFER SURF.
    1098             :                                Real64 &TempSurfOutTmp // Outside Surface Temperature of each Heat Transfer Surface
    1099             :     )
    1100             :     {
    1101             : 
    1102             :         // SUBROUTINE INFORMATION:
    1103             :         //       AUTHOR         Richard J. Liesen
    1104             :         //       DATE WRITTEN   Oct 2003
    1105             :         //       MODIFIED       Aug 2006 by C O Pedersen to include implicit solution and variable properties with
    1106             :         //                                material enthalpy added for Phase Change Materials.
    1107             :         //                      Sept 2010 B. Griffith, remove allocate/deallocate, use structure variables
    1108             :         //                      March 2011 P. Tabares, add relaxation factor and add surfIteration to
    1109             :         //                                 update TD and TDT, correct interzone partition
    1110             :         //                      May 2011  B. Griffith add logging and errors when inner GS loop does not converge
    1111             :         //                      November 2011 P. Tabares fixed problems with adiabatic walls/massless walls and PCM stability problems
    1112             : 
    1113             :         // PURPOSE OF THIS SUBROUTINE:
    1114             :         // this routine controls the calculation of the fluxes and temperatures using
    1115             :         //      finite difference procedures for
    1116             :         //      all building surface constructs.
    1117             : 
    1118    10239636 :         int const ConstrNum = state.dataSurface->Surface(Surf).Construction;
    1119    10239636 :         auto &constructFD = state.dataHeatBalFiniteDiffMgr->ConstructFD(ConstrNum);
    1120             : 
    1121    10239636 :         int const TotNodes = constructFD.TotNodes;
    1122    10239636 :         int const TotLayers = state.dataConstruction->Construct(ConstrNum).TotLayers;
    1123             : 
    1124    10239636 :         SurfTempInTmp = 0.0;
    1125    10239636 :         TempSurfOutTmp = 0.0;
    1126             : 
    1127    10239636 :         int const Delt = constructFD.DeltaTime; //   (seconds)
    1128             : 
    1129             :         // Aliases
    1130    10239636 :         auto &surfaceFD = state.dataHeatBalFiniteDiffMgr->SurfaceFD(Surf);
    1131             : 
    1132    10239636 :         Real64 HMovInsul = 0;
    1133    10239636 :         if (state.dataSurface->AnyMovableInsulation) HMovInsul = state.dataHeatBalSurf->SurfMovInsulHExt(Surf);
    1134             :         // Start stepping through the slab with time.
    1135    20479272 :         for (int J = 1, J_end = nint(state.dataGlobal->TimeStepZoneSec / Delt); J <= J_end; ++J) { // PT testing higher time steps
    1136             : 
    1137             :             int GSiter;                                                                       // iteration counter for implicit repeat calculation
    1138    35043380 :             for (GSiter = 1; GSiter <= state.dataHeatBalFiniteDiffMgr->MaxGSiter; ++GSiter) { //  Iterate implicit equations
    1139    35043376 :                 surfaceFD.TDTLast = surfaceFD.TDT;                                            // Save last iteration's TDT (New temperature) values
    1140    35043376 :                 surfaceFD.EnthLast = surfaceFD.EnthNew;                                       // Last iterations new enthalpy value
    1141             : 
    1142             :                 // Original loop version
    1143    35043376 :                 int i(1);                                    //  Node counter
    1144   153641035 :                 for (int Lay = 1; Lay <= TotLayers; ++Lay) { // Begin layer loop ...
    1145             : 
    1146             :                     // For the exterior surface node with a convective boundary condition
    1147   118597659 :                     if ((i == 1) && (Lay == 1)) {
    1148    35043376 :                         ExteriorBCEqns(state,
    1149             :                                        Delt,
    1150             :                                        i,
    1151             :                                        Lay,
    1152             :                                        Surf,
    1153    35043376 :                                        surfaceFD.T,
    1154    35043376 :                                        surfaceFD.TT,
    1155    35043376 :                                        surfaceFD.Rhov,
    1156    35043376 :                                        surfaceFD.RhoT,
    1157    35043376 :                                        surfaceFD.RH,
    1158    35043376 :                                        surfaceFD.TD,
    1159    35043376 :                                        surfaceFD.TDT,
    1160    35043376 :                                        surfaceFD.EnthOld,
    1161    35043376 :                                        surfaceFD.EnthNew,
    1162             :                                        TotNodes,
    1163             :                                        HMovInsul);
    1164             :                     }
    1165             : 
    1166             :                     // For the Layer Interior nodes.  Arrive here after exterior surface node or interface node
    1167   118597659 :                     if (TotNodes != 1) {
    1168   324601076 :                         for (int ctr = 2, ctr_end = constructFD.NodeNumPoint(Lay); ctr <= ctr_end; ++ctr) {
    1169   207119728 :                             ++i;
    1170   207119728 :                             InteriorNodeEqns(state,
    1171             :                                              Delt,
    1172             :                                              i,
    1173             :                                              Lay,
    1174             :                                              Surf,
    1175   207119728 :                                              surfaceFD.T,
    1176   207119728 :                                              surfaceFD.TT,
    1177   207119728 :                                              surfaceFD.Rhov,
    1178   207119728 :                                              surfaceFD.RhoT,
    1179   207119728 :                                              surfaceFD.RH,
    1180   207119728 :                                              surfaceFD.TD,
    1181   207119728 :                                              surfaceFD.TDT,
    1182   207119728 :                                              surfaceFD.EnthOld,
    1183   207119728 :                                              surfaceFD.EnthNew);
    1184             :                         }
    1185             :                     }
    1186             : 
    1187   118597659 :                     if ((Lay < TotLayers) && (TotNodes != 1)) { // Interface equations for 2 capacitive materials
    1188    83554283 :                         ++i;
    1189    83554283 :                         IntInterfaceNodeEqns(state,
    1190             :                                              Delt,
    1191             :                                              i,
    1192             :                                              Lay,
    1193             :                                              Surf,
    1194    83554283 :                                              surfaceFD.T,
    1195    83554283 :                                              surfaceFD.TT,
    1196    83554283 :                                              surfaceFD.Rhov,
    1197    83554283 :                                              surfaceFD.RhoT,
    1198    83554283 :                                              surfaceFD.RH,
    1199    83554283 :                                              surfaceFD.TD,
    1200    83554283 :                                              surfaceFD.TDT,
    1201    83554283 :                                              surfaceFD.EnthOld,
    1202    83554283 :                                              surfaceFD.EnthNew,
    1203             :                                              GSiter);
    1204    35043376 :                     } else if (Lay == TotLayers) { // For the Interior surface node with a convective boundary condition
    1205    35043376 :                         ++i;
    1206    35043376 :                         InteriorBCEqns(state,
    1207             :                                        Delt,
    1208             :                                        i,
    1209             :                                        Lay,
    1210             :                                        Surf,
    1211    35043376 :                                        surfaceFD.T,
    1212    35043376 :                                        surfaceFD.TT,
    1213    35043376 :                                        surfaceFD.Rhov,
    1214    35043376 :                                        surfaceFD.RhoT,
    1215    35043376 :                                        surfaceFD.RH,
    1216    35043376 :                                        surfaceFD.TD,
    1217    35043376 :                                        surfaceFD.TDT,
    1218    35043376 :                                        surfaceFD.EnthOld,
    1219    35043376 :                                        surfaceFD.EnthNew,
    1220    35043376 :                                        surfaceFD.TDreport);
    1221             :                     }
    1222             : 
    1223             :                 } // layer loop
    1224             : 
    1225             :                 // Apply Relaxation factor for stability, use current (TDT) and previous (TDTLast) iteration temperature values
    1226             :                 // to obtain the actual temperature that is going to be used for next iteration. This would mostly happen with PCM
    1227             :                 // Tuned Function call to eliminate array temporaries and multiple relaxation passes
    1228    35043376 :                 if (GSiter > 15) {
    1229       24681 :                     relax_array(surfaceFD.TDT, surfaceFD.TDTLast, 0.9875);
    1230    35018695 :                 } else if (GSiter > 10) {
    1231      190741 :                     relax_array(surfaceFD.TDT, surfaceFD.TDTLast, 0.875);
    1232    34827954 :                 } else if (GSiter > 5) {
    1233     1149228 :                     relax_array(surfaceFD.TDT, surfaceFD.TDTLast, 0.5);
    1234             :                 }
    1235             : 
    1236             :                 // the following could blow up when all the node temps sum to less than 1.0.  seems poorly formulated for temperature in C.
    1237             :                 // PT delete one zero and decrease number of minimum iterations, from 3 (which actually requires 4 iterations) to 2.
    1238             : 
    1239    35043376 :                 if ((GSiter > 2) && (std::abs(sum_array_diff(surfaceFD.TDT, surfaceFD.TDTLast) / sum(surfaceFD.TDT)) < 0.00001)) break;
    1240             : 
    1241             :             } // End of Gauss Seidell iteration loop
    1242             : 
    1243    10239636 :             surfaceFD.GSloopCounter = GSiter; // outputs GSloop iterations, useful for pinpointing stability issues with condFD
    1244    10239636 :             if (state.dataHeatBal->CondFDRelaxFactor != 1.0) {
    1245             :                 // Apply Relaxation factor for stability, use current (TDT) and previous (TDreport) temperature values
    1246             :                 //   to obtain the actual temperature that is going to be exported/use
    1247        9942 :                 relax_array(surfaceFD.TDT, surfaceFD.TDreport, 1.0 - state.dataHeatBal->CondFDRelaxFactor);
    1248        9942 :                 surfaceFD.EnthOld = surfaceFD.EnthNew;
    1249             :             }
    1250             : 
    1251   115348724 :             for (int I = 1; I <= (TotNodes + 1); I++) {
    1252             :                 // When the phase change process reverses its direction while melting or freezing (without completing its phase
    1253             :                 // to either liquid or solid), the temperature at which it changes its direction is saved
    1254             :                 // in the variable PhaseChangeTemperatureReverse, and this variable will hold the value of the temperature until
    1255             :                 // the next reverse in the process takes place.
    1256   105109088 :                 if (((surfaceFD.PhaseChangeStateOld(I) == HysteresisPhaseChange::PhaseChangeStates::FREEZING &&
    1257        4214 :                       surfaceFD.PhaseChangeState(I) == HysteresisPhaseChange::PhaseChangeStates::TRANSITION) ||
    1258   105109088 :                      (surfaceFD.PhaseChangeStateOld(I) == HysteresisPhaseChange::PhaseChangeStates::TRANSITION &&
    1259   315182923 :                       surfaceFD.PhaseChangeState(I) == HysteresisPhaseChange::PhaseChangeStates::FREEZING)) ||
    1260   105109088 :                     ((surfaceFD.PhaseChangeStateOld(I) == HysteresisPhaseChange::PhaseChangeStates::MELTING &&
    1261       27806 :                       surfaceFD.PhaseChangeState(I) == HysteresisPhaseChange::PhaseChangeStates::TRANSITION) ||
    1262   105109088 :                      (surfaceFD.PhaseChangeStateOld(I) == HysteresisPhaseChange::PhaseChangeStates::TRANSITION &&
    1263   104964747 :                       surfaceFD.PhaseChangeState(I) == HysteresisPhaseChange::PhaseChangeStates::MELTING))) {
    1264           0 :                     surfaceFD.PhaseChangeTemperatureReverse(I) = surfaceFD.TDT(I);
    1265             :                 }
    1266             :             }
    1267             : 
    1268    10239636 :             surfaceFD.PhaseChangeStateOldOld = surfaceFD.PhaseChangeStateOld;
    1269    10239636 :             surfaceFD.PhaseChangeStateOld = surfaceFD.PhaseChangeState;
    1270             : 
    1271             :         } // Time Loop  //PT solving time steps
    1272             : 
    1273    10239636 :         TempSurfOutTmp = surfaceFD.TDT(1);
    1274    10239636 :         SurfTempInTmp = surfaceFD.TDT(TotNodes + 1);
    1275    10239636 :         state.dataMstBal->RhoVaporSurfIn(Surf) = 0.0;
    1276             : 
    1277             :         // For ground surfaces or when raining, outside face inner half-node heat capacity was unknown and set to -1 in ExteriorBCEqns
    1278             :         // Now check for the flag and set equal to the second node's outer half-node heat capacity if needed
    1279    10239636 :         if (surfaceFD.CpDelXRhoS2(1) == -1.0) {
    1280     1206319 :             surfaceFD.CpDelXRhoS2(1) = surfaceFD.CpDelXRhoS1(2); // Set to node 2's outer half node heat capacity
    1281             :         }
    1282    10239636 :         CalcNodeHeatFlux(state, Surf, TotNodes);
    1283             : 
    1284             :         // Determine largest change in node temps
    1285    10239636 :         Real64 MaxDelTemp = 0.0;
    1286   115348724 :         for (int NodeNum = 1; NodeNum <= TotNodes + 1; ++NodeNum) { // need to consider all nodes
    1287   105109088 :             MaxDelTemp = max(std::abs(surfaceFD.TDT(NodeNum) - surfaceFD.TDreport(NodeNum)), MaxDelTemp);
    1288             :         }
    1289    10239636 :         surfaceFD.MaxNodeDelTemp = MaxDelTemp;
    1290    10239636 :         surfaceFD.TDreport = surfaceFD.TDT;
    1291    10239636 :         surfaceFD.EnthOld = surfaceFD.EnthNew;
    1292    10239636 :     }
    1293             : 
    1294          12 :     void ReportFiniteDiffInits(EnergyPlusData &state)
    1295             :     {
    1296             : 
    1297             :         // SUBROUTINE INFORMATION:
    1298             :         //       AUTHOR         Richard Liesen
    1299             :         //       DATE WRITTEN   November 2003
    1300             :         //       MODIFIED       B. Griffith, May 2011 add reporting of node x locations
    1301             : 
    1302             :         // PURPOSE OF THIS SUBROUTINE:
    1303             :         // This routine gives a detailed report to the user about
    1304             :         // the initializations for the Finite Difference calculations
    1305             :         // of each construction.
    1306             : 
    1307             :         // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    1308             :         bool DoReport;
    1309             : 
    1310             :         // Formats
    1311             :         static constexpr std::string_view Format_702(" ConductionFiniteDifference Node,{},{:.8R},{},{},{}\n");
    1312             : 
    1313          12 :         print(state.files.eio,
    1314             :               "! <ConductionFiniteDifference HeatBalanceSettings>,Scheme Type,Space Discretization Constant,Relaxation Factor,Inside Face Surface "
    1315             :               "Temperature Convergence Criteria\n");
    1316          12 :         print(state.files.eio,
    1317             :               " ConductionFiniteDifference HeatBalanceSettings,{},{:.2R},{:.2R},{:.4R}\n",
    1318          12 :               CondFDSchemeTypeNamesCC[static_cast<int>(state.dataHeatBalFiniteDiffMgr->CondFDSchemeType)],
    1319          12 :               state.dataHeatBalFiniteDiffMgr->SpaceDescritConstant,
    1320          12 :               state.dataHeatBal->CondFDRelaxFactorInput,
    1321          12 :               state.dataHeatBal->MaxAllowedDelTempCondFD);
    1322             : 
    1323          12 :         General::ScanForReports(state, "Constructions", DoReport, "Constructions");
    1324             : 
    1325          12 :         if (DoReport) {
    1326             : 
    1327             :             //                                      Write Descriptions
    1328           5 :             print(state.files.eio, "{}\n", "! <Construction CondFD>,Construction Name,Index,#Layers,#Nodes,Time Step {hours}");
    1329           5 :             print(state.files.eio,
    1330             :                   "{}\n",
    1331             :                   "! <Material CondFD Summary>,Material Name,Thickness {m},#Layer Elements,Layer Delta X,Layer Alpha*Delt/Delx**2,Layer Moisture "
    1332             :                   "Stability");
    1333             : 
    1334             :             // HT Algo issue
    1335           5 :             if (state.dataHeatBal->AnyCondFD) {
    1336           5 :                 print(state.files.eio,
    1337             :                       "{}\n",
    1338             :                       "! <ConductionFiniteDifference Node>,Node Identifier, Node Distance From Outside Face {m}, Construction Name, Outward Material "
    1339             :                       "Name (or Face), Inward Material Name (or Face)");
    1340             :             }
    1341             : 
    1342          25 :             for (int ThisNum = 1; ThisNum <= state.dataHeatBal->TotConstructs; ++ThisNum) {
    1343          20 :                 auto &construct = state.dataConstruction->Construct(ThisNum);
    1344             : 
    1345          23 :                 if (construct.TypeIsWindow) continue;
    1346          19 :                 if (construct.TypeIsIRT) continue;
    1347          19 :                 if (construct.TypeIsAirBoundary) continue;
    1348          19 :                 if (!construct.IsUsed) continue;
    1349          19 :                 if (!findAnySurfacesUsingConstructionAndCondFD(state, ThisNum)) continue;
    1350             : 
    1351          16 :                 auto &constructFD = state.dataHeatBalFiniteDiffMgr->ConstructFD(ThisNum);
    1352             :                 static constexpr std::string_view Format_700(" Construction CondFD,{},{},{},{},{:.6R}\n");
    1353          16 :                 print(state.files.eio,
    1354             :                       Format_700,
    1355          16 :                       construct.Name,
    1356             :                       ThisNum,
    1357          16 :                       construct.TotLayers,
    1358           0 :                       int(constructFD.TotNodes + 1),
    1359          16 :                       constructFD.DeltaTime / Constant::SecInHour);
    1360             : 
    1361          52 :                 for (int Layer = 1; Layer <= construct.TotLayers; ++Layer) {
    1362             :                     static constexpr std::string_view Format_701(" Material CondFD Summary,{},{:.4R},{},{:.8R},{:.8R},{:.8R}\n");
    1363          36 :                     print(state.files.eio,
    1364             :                           Format_701,
    1365             :                           constructFD.Name(Layer),
    1366             :                           constructFD.Thickness(Layer),
    1367             :                           constructFD.NodeNumPoint(Layer),
    1368             :                           constructFD.DelX(Layer),
    1369             :                           constructFD.TempStability(Layer),
    1370             :                           constructFD.MoistStability(Layer));
    1371             :                 }
    1372             : 
    1373             :                 // now list each CondFD Node with its X distance from outside face in m along with other identifiers
    1374          16 :                 int Inodes = 0;
    1375             : 
    1376          52 :                 for (int Layer = 1; Layer <= construct.TotLayers; ++Layer) {
    1377          36 :                     int OutwardMatLayerNum = Layer - 1;
    1378         110 :                     for (int LayerNode = 1; LayerNode <= constructFD.NodeNumPoint(Layer); ++LayerNode) {
    1379          74 :                         ++Inodes;
    1380          74 :                         if (Inodes == 1) {
    1381          32 :                             print(state.files.eio,
    1382             :                                   Format_702,
    1383          32 :                                   format("Node #{}", Inodes),
    1384             :                                   constructFD.NodeXlocation(Inodes),
    1385          16 :                                   construct.Name,
    1386             :                                   "Surface Outside Face",
    1387             :                                   constructFD.Name(Layer));
    1388             : 
    1389          58 :                         } else if (LayerNode == 1) {
    1390             : 
    1391          20 :                             if (OutwardMatLayerNum > 0 && OutwardMatLayerNum <= construct.TotLayers) {
    1392          40 :                                 print(state.files.eio,
    1393             :                                       Format_702,
    1394          40 :                                       format("Node #{}", Inodes),
    1395             :                                       constructFD.NodeXlocation(Inodes),
    1396          20 :                                       construct.Name,
    1397             :                                       constructFD.Name(OutwardMatLayerNum),
    1398             :                                       constructFD.Name(Layer));
    1399             :                             }
    1400          38 :                         } else if (LayerNode > 1) {
    1401          38 :                             OutwardMatLayerNum = Layer;
    1402          76 :                             print(state.files.eio,
    1403             :                                   Format_702,
    1404          76 :                                   format("Node #{}", Inodes),
    1405             :                                   constructFD.NodeXlocation(Inodes),
    1406          38 :                                   construct.Name,
    1407             :                                   constructFD.Name(OutwardMatLayerNum),
    1408             :                                   constructFD.Name(Layer));
    1409             :                         }
    1410             :                     }
    1411             :                 }
    1412             : 
    1413          16 :                 int Layer = construct.TotLayers;
    1414          16 :                 ++Inodes;
    1415          32 :                 print(state.files.eio,
    1416             :                       Format_702,
    1417          32 :                       format("Node #{}", Inodes),
    1418             :                       constructFD.NodeXlocation(Inodes),
    1419          16 :                       construct.Name,
    1420             :                       constructFD.Name(Layer),
    1421             :                       "Surface Inside Face");
    1422             :             }
    1423             :         }
    1424          12 :     }
    1425             : 
    1426     2350788 :     Real64 terpld(Array2<Real64> const &a, Real64 const x1, int const nind, int const ndep)
    1427             :     {
    1428             :         // author:c. o. pedersen
    1429             :         // purpose:
    1430             :         //   this function performs a linear interpolation
    1431             :         //     on a two dimensional array containing both
    1432             :         //     dependent and independent variables.
    1433             : 
    1434             :         // inputs:
    1435             :         //  a = two dimensional array
    1436             :         //  nind=row containing independent variable
    1437             :         //  ndep=row containing the dependent variable
    1438             :         //   x1 = specific independent variable value for which
    1439             :         //      interpolated output is wanted
    1440             :         // outputs:
    1441             :         //    the value of dependent variable corresponding
    1442             :         //       to x1
    1443             :         //    routine returns first or last dependent variable
    1444             :         //      for out of range x1.
    1445             : 
    1446     2350788 :         int const first(a.l2());
    1447             : 
    1448     2350788 :         assert(a.size() > 0u);
    1449     2350788 :         Array2<Real64>::size_type l(1);
    1450     2350788 :         Real64 r(a[0]);
    1451     2350788 :         int last(first);
    1452     9403152 :         for (int i1 = first + 1, e1 = a.u2(); i1 <= e1; ++i1, ++l) {
    1453     7052364 :             if (a[l] > r) {
    1454     7052364 :                 r = a[l];
    1455     7052364 :                 last = i1;
    1456             :             }
    1457             :         }
    1458             : 
    1459     2350788 :         Array2<Real64>::size_type lind(a.index(nind, 0));
    1460     2350788 :         Array2<Real64>::size_type ldep(a.index(ndep, 0));
    1461     2350788 :         if ((a.size2() == 1u) || (x1 <= a[lind + first])) { // [ lind + first ] == ( nind, first )
    1462           0 :             return a[ldep + first];                         // [ ldep + first ] == ( ndep, first )
    1463     2350788 :         } else if (x1 >= a[lind + last]) {                  // [ lind + last ] == ( nind, last )
    1464           0 :             return a[ldep + last];                          // [ ldep + last ] == ( ndep, last )
    1465             :         } else {
    1466             :             int i;
    1467     2350788 :             int i1(first);
    1468     2350788 :             int i2(last);
    1469     6179781 :             while ((i2 - i1) > 1) {
    1470     3828993 :                 i = i1 + ((i2 - i1) >> 1); // Tuned bit shift replaces / 2
    1471     3828993 :                 if (x1 < a[lind + i]) {    // [ lind + i ] == ( nind, i )
    1472     1010936 :                     i2 = i;
    1473             :                 } else {
    1474     2818057 :                     i1 = i;
    1475             :                 }
    1476             :             }
    1477     2350788 :             i = i2;
    1478     2350788 :             lind += i;
    1479     2350788 :             ldep += i;
    1480     2350788 :             Real64 const fract((x1 - a[lind - 1]) / (a[lind] - a[lind - 1])); // [ lind ] == ( nind, i ), [ lind - 1 ] == ( nind, i - 1 )
    1481     2350788 :             return a[ldep - 1] + fract * (a[ldep] - a[ldep - 1]);             // [ ldep ] == ( ndep, i ), [ ldep - 1 ] == ( ndep, i - 1 )
    1482             :         }
    1483             :     }
    1484             : 
    1485    35043376 :     void ExteriorBCEqns(EnergyPlusData &state,
    1486             :                         int const Delt,                               // Time Increment
    1487             :                         int const i,                                  // Node Index
    1488             :                         int const Lay,                                // Layer Number for Construction
    1489             :                         int const Surf,                               // Surface number
    1490             :                         [[maybe_unused]] Array1D<Real64> const &T,    // Old node Temperature in MFD finite difference solution
    1491             :                         Array1D<Real64> &TT,                          // New node Temperature in MFD finite difference solution.
    1492             :                         [[maybe_unused]] Array1D<Real64> const &Rhov, // MFD Nodal Vapor Density[kg/m3] and is the old or last time step result.
    1493             :                         Array1D<Real64> &RhoT,                        // MFD vapor density for the new time step.
    1494             :                         [[maybe_unused]] Array1D<Real64> &RH,         // Nodal relative humidity
    1495             :                         Array1D<Real64> const &TD,                    // The old dry Temperature at each node for the CondFD algorithm..
    1496             :                         Array1D<Real64> &TDT,     // The current or new Temperature at each node location for the CondFD solution..
    1497             :                         Array1D<Real64> &EnthOld, // Old Nodal enthalpy
    1498             :                         Array1D<Real64> &EnthNew, // New Nodal enthalpy
    1499             :                         int const TotNodes,       // Total nodes in layer
    1500             :                         Real64 const HMovInsul    // Conductance of movable(transparent) insulation.
    1501             :     )
    1502             :     {
    1503             : 
    1504             :         // SUBROUTINE INFORMATION:
    1505             :         //       AUTHOR         Richard Liesen
    1506             :         //       DATE WRITTEN   November, 2003
    1507             :         //       MODIFIED       B. Griffith 2010, fix adiabatic and other side surfaces
    1508             :         //                      May 2011, B. Griffith, P. Tabares
    1509             :         //                      November 2011 P. Tabares fixed problems with adiabatic walls/massless walls
    1510             :         //                      November 2011 P. Tabares fixed problems PCM stability problems
    1511             :         //       RE-ENGINEERED  Curtis Pedersen 2006
    1512             : 
    1513    35043376 :         auto &SurfaceFD = state.dataHeatBalFiniteDiffMgr->SurfaceFD;
    1514    35043376 :         auto &ConstructFD = state.dataHeatBalFiniteDiffMgr->ConstructFD;
    1515             : 
    1516    35043376 :         auto const &surface(state.dataSurface->Surface(Surf));
    1517    35043376 :         int const surface_ExtBoundCond(surface.ExtBoundCond);
    1518             : 
    1519             :         Real64 Tsky;
    1520             :         Real64 QRadSWOutFD;             // Short wave radiation absorbed on outside of opaque surface
    1521    35043376 :         Real64 QRadSWOutMvInsulFD(0.0); // SW radiation at outside of Movable Insulation
    1522    35043376 :         if (surface_ExtBoundCond == DataSurfaces::OtherSideCondModeledExt) {
    1523             :             // CR8046 switch modeled rad temp for sky temp.
    1524           0 :             Tsky = state.dataSurface->OSCM(surface.OSCMPtr).TRad;
    1525           0 :             QRadSWOutFD = 0.0; // eliminate incident shortwave on underlying surface
    1526             :         } else {               // Set the external conditions to local variables
    1527    35043376 :             QRadSWOutFD = state.dataHeatBalSurf->SurfOpaqQRadSWOutAbs(Surf);
    1528    35043376 :             QRadSWOutMvInsulFD = state.dataHeatBalSurf->SurfQRadSWOutMvIns(Surf);
    1529    35043376 :             Tsky = state.dataEnvrn->SkyTemp;
    1530             :         }
    1531             : 
    1532    35043376 :         if (surface_ExtBoundCond == DataSurfaces::Ground || state.dataEnvrn->IsRain) {
    1533     5858076 :             TDT(i) = TT(i) = state.dataMstBal->TempOutsideAirFD(Surf);
    1534     5858076 :             RhoT(i) = state.dataMstBal->RhoVaporAirOut(Surf);
    1535     5858076 :             SurfaceFD(Surf).CpDelXRhoS1(i) = 0.0;  // Outside face  does not have an outer half node
    1536     5858076 :             SurfaceFD(Surf).CpDelXRhoS2(i) = -1.0; // Set this to -1 as a flag, then set to node 2's outer half node heat capacity
    1537    29185300 :         } else if (surface_ExtBoundCond > 0) {
    1538             :             // this is actually the inside face of another surface, or maybe this same surface if adiabatic
    1539             :             // switch around arguments for the other surf and call routines as for interior side BC from opposite face
    1540             : 
    1541    11812181 :             int const ext_bound_construction(state.dataSurface->Surface(surface_ExtBoundCond).Construction);
    1542    11812181 :             int const LayIn(state.dataConstruction->Construct(ext_bound_construction).TotLayers); // layer number for call to interior eqs
    1543    11812181 :             int const NodeIn(ConstructFD(ext_bound_construction).TotNodes + 1);                   // node number "I" for call to interior eqs
    1544    11812181 :             int const TotNodesPlusOne(TotNodes + 1);
    1545    11812181 :             if (surface_ExtBoundCond == Surf) { // adiabatic surface, PT added since it is not the same as interzone wall
    1546             :                 // as Outside Boundary Condition Object can be left blank.
    1547             : 
    1548      916581 :                 auto &surfaceFD(SurfaceFD(Surf));
    1549      916581 :                 InteriorBCEqns(state,
    1550             :                                Delt,
    1551             :                                NodeIn,
    1552             :                                LayIn,
    1553             :                                Surf,
    1554      916581 :                                surfaceFD.T,
    1555      916581 :                                surfaceFD.TT,
    1556      916581 :                                surfaceFD.Rhov,
    1557      916581 :                                surfaceFD.RhoT,
    1558      916581 :                                surfaceFD.RH,
    1559      916581 :                                surfaceFD.TD,
    1560      916581 :                                surfaceFD.TDT,
    1561      916581 :                                surfaceFD.EnthOld,
    1562      916581 :                                surfaceFD.EnthNew,
    1563      916581 :                                surfaceFD.TDreport);
    1564      916581 :                 TDT(i) = surfaceFD.TDT(TotNodesPlusOne);
    1565      916581 :                 TT(i) = surfaceFD.TT(TotNodesPlusOne);
    1566      916581 :                 RhoT(i) = surfaceFD.RhoT(TotNodesPlusOne);
    1567             : 
    1568      916581 :                 surfaceFD.CpDelXRhoS1(i) = 0.0;                                    // Outside face  does not have an outer half node
    1569      916581 :                 surfaceFD.CpDelXRhoS2(i) = surfaceFD.CpDelXRhoS1(TotNodesPlusOne); // Save this for computing node flux values
    1570             : 
    1571             :             } else {
    1572             : 
    1573             :                 // potential-lkl-from old      CALL InteriorBCEqns(Delt,nodeIn,LayIn,Surf,SurfaceFD(Surface(Surf)%ExtBoundCond)%T, &
    1574    10895600 :                 auto &surfaceFDEBC(SurfaceFD(surface_ExtBoundCond));
    1575    10895600 :                 InteriorBCEqns(state,
    1576             :                                Delt,
    1577             :                                NodeIn,
    1578             :                                LayIn,
    1579             :                                surface_ExtBoundCond,
    1580    10895600 :                                surfaceFDEBC.T,
    1581    10895600 :                                surfaceFDEBC.TT,
    1582    10895600 :                                surfaceFDEBC.Rhov,
    1583    10895600 :                                surfaceFDEBC.RhoT,
    1584    10895600 :                                surfaceFDEBC.RH,
    1585    10895600 :                                surfaceFDEBC.TD,
    1586    10895600 :                                surfaceFDEBC.TDT,
    1587    10895600 :                                surfaceFDEBC.EnthOld,
    1588    10895600 :                                surfaceFDEBC.EnthNew,
    1589    10895600 :                                surfaceFDEBC.TDreport);
    1590             : 
    1591    10895600 :                 TDT(i) = surfaceFDEBC.TDT(TotNodesPlusOne);
    1592    10895600 :                 TT(i) = surfaceFDEBC.TT(TotNodesPlusOne);
    1593    10895600 :                 RhoT(i) = surfaceFDEBC.RhoT(TotNodesPlusOne);
    1594             : 
    1595    10895600 :                 SurfaceFD(Surf).CpDelXRhoS1(i) = 0.0;                                       // Outside face  does not have an outer half node
    1596    10895600 :                 SurfaceFD(Surf).CpDelXRhoS2(i) = surfaceFDEBC.CpDelXRhoS1(TotNodesPlusOne); // Save this for computing node flux values
    1597             :             }
    1598             : 
    1599    11812181 :             Real64 const QNetSurfFromOutside(state.dataHeatBalSurf->SurfOpaqInsFaceCondFlux(surface_ExtBoundCond)); // filled in InteriorBCEqns
    1600             :             //    QFluxOutsideToOutSurf(Surf)       = QnetSurfFromOutside
    1601    11812181 :             state.dataHeatBalSurf->SurfOpaqOutFaceCondFlux(Surf) = -QNetSurfFromOutside;
    1602    11812181 :             state.dataHeatBalFiniteDiffMgr->QHeatOutFlux(Surf) = QNetSurfFromOutside;
    1603             : 
    1604             :         } else { // regular outside conditions
    1605    17373119 :             Real64 TDT_i(TDT(i));
    1606    17373119 :             Real64 const TDT_p(TDT(i + 1));
    1607             : 
    1608    17373119 :             Real64 Tgndsurface = 0.0;
    1609    17373119 :             if (state.dataSurface->Surface(Surf).UseSurfPropertyGndSurfTemp) {
    1610           0 :                 Tgndsurface = state.dataSurface->GroundSurfsProperty(Surf).SurfsTempAvg;
    1611             :             } else {
    1612    17373119 :                 Tgndsurface = state.dataMstBal->TempOutsideAirFD(Surf);
    1613             :             }
    1614             : 
    1615             :             // Boundary Conditions from Simulation for Exterior
    1616    17373119 :             Real64 const hconvo(state.dataMstBal->HConvExtFD(Surf));
    1617             : 
    1618    17373119 :             Real64 const hrad(state.dataMstBal->HAirFD(Surf));
    1619    17373119 :             Real64 const hsky(state.dataMstBal->HSkyFD(Surf));
    1620    17373119 :             Real64 const hgnd(state.dataMstBal->HGrndFD(Surf));
    1621    17373119 :             Real64 const Toa(state.dataMstBal->TempOutsideAirFD(Surf));
    1622    17373119 :             Real64 const Tgnd(Tgndsurface);
    1623             : 
    1624    17373119 :             if (surface.HeatTransferAlgorithm == DataSurfaces::HeatTransferModel::CondFD) {
    1625             : 
    1626    17373119 :                 int const ConstrNum(surface.Construction);
    1627    17373119 :                 int const MatLay(state.dataConstruction->Construct(ConstrNum).LayerPoint(Lay));
    1628    17373119 :                 auto const *mat(dynamic_cast<const Material::MaterialChild *>(state.dataMaterial->Material(MatLay)));
    1629    17373119 :                 auto const &matFD(state.dataHeatBalFiniteDiffMgr->MaterialFD(MatLay));
    1630    17373119 :                 auto const &condActuator(SurfaceFD(Surf).condMaterialActuators(Lay));
    1631    17373119 :                 auto const &specHeatActuator(SurfaceFD(Surf).specHeatMaterialActuators(Lay));
    1632             : 
    1633             :                 // regular outside conditions
    1634             : 
    1635             :                 // Calculate the Dry Heat Conduction Equation
    1636             : 
    1637    17373119 :                 if (mat->ROnly || mat->group == Material::Group::Air) { // R Layer or Air Layer  **********
    1638             :                     // Use algebraic equation for TDT based on R
    1639     1116311 :                     Real64 const Rlayer(mat->Resistance);
    1640     1116311 :                     TDT_i = (TDT_p + (QRadSWOutFD + hgnd * Tgnd + (hconvo + hrad) * Toa + hsky * Tsky) * Rlayer) /
    1641     1116311 :                             (1.0 + (hconvo + hgnd + hrad + hsky) * Rlayer);
    1642             : 
    1643     1116311 :                 } else { // Regular or phase change material layer
    1644             : 
    1645             :                     // Set Thermal Conductivity. Can be constant, simple linear temp dep or multiple linear segment temp function dep.
    1646    16256808 :                     auto const &matFD_TempCond(matFD.TempCond);
    1647    16256808 :                     assert(matFD_TempCond.u2() >= 3);
    1648    16256808 :                     Real64 const lTC(matFD_TempCond.index(2, 1));
    1649             :                     Real64 kt;
    1650    16256808 :                     if (matFD_TempCond[lTC] + matFD_TempCond[lTC + 1] + matFD_TempCond[lTC + 2] >= 0.0) { // Multiple Linear Segment Function
    1651             :                         // Use average temp of surface and first node for k
    1652       75304 :                         kt = terpld(matFD_TempCond, (TDT_i + TDT_p) / 2.0, 1, 2); // 1: Temperature, 2: Thermal conductivity
    1653             :                     } else {
    1654    16181504 :                         kt = mat->Conductivity;      // 20C base conductivity
    1655    16181504 :                         Real64 const kt1(matFD.tk1); // linear coefficient (normally zero)
    1656    16181504 :                         if (kt1 != 0.0) kt = +kt1 * ((TDT_i + TDT_p) / 2.0 - 20.0);
    1657             :                     }
    1658             : 
    1659             :                     // Check for phase change material
    1660    16256808 :                     Real64 const TD_i(TD(i));
    1661    16256808 :                     Real64 const Cpo(mat->SpecHeat); // Specific heat from idf
    1662    16256808 :                     Real64 Cp(Cpo);                  // Specific heat modified if PCM, otherwise equal to Cpo // Will be changed if PCM
    1663    16256808 :                     auto const &matFD_TempEnth(matFD.TempEnth);
    1664    16256808 :                     assert(matFD_TempEnth.u2() >= 3);
    1665    16256808 :                     Real64 const lTE(matFD_TempEnth.index(2, 1));
    1666    16256808 :                     Real64 RhoS(mat->Density);
    1667    16256808 :                     if (mat->phaseChange) {
    1668           0 :                         adjustPropertiesForPhaseChange(state, i, Surf, mat, TD_i, TDT_i, Cp, RhoS, kt);
    1669           0 :                         SurfaceFD(Surf).EnthalpyF = mat->phaseChange->enthalpyF;
    1670           0 :                         SurfaceFD(Surf).EnthalpyM = mat->phaseChange->enthalpyM;
    1671    16256808 :                     } else if (matFD_TempEnth[lTE] + matFD_TempEnth[lTE + 1] + matFD_TempEnth[lTE + 2] >=
    1672             :                                0.0) { // Phase change material: Use TempEnth data to generate Cp
    1673             :                         // Enthalpy function used to get average specific heat. Updated by GS so enthalpy function is followed.
    1674           0 :                         EnthOld(i) = terpld(matFD_TempEnth, TD_i, 1, 2);  // 1: Temperature, 2: Enthalpy
    1675           0 :                         EnthNew(i) = terpld(matFD_TempEnth, TDT_i, 1, 2); // 1: Temperature, 2: Enthalpy
    1676           0 :                         if (EnthNew(i) != EnthOld(i)) {
    1677           0 :                             Cp = max(Cpo, (EnthNew(i) - EnthOld(i)) / (TDT_i - TD_i));
    1678             :                         }
    1679             :                     } // Phase Change Material option
    1680             : 
    1681             :                     // EMS Conductivity Override
    1682    16256808 :                     if (condActuator.isActuated) {
    1683           0 :                         kt = condActuator.actuatedValue;
    1684             :                     }
    1685             : 
    1686             :                     // EMS Specific Heat Override
    1687    16256808 :                     if (specHeatActuator.isActuated) {
    1688           0 :                         Cp = specHeatActuator.actuatedValue;
    1689             :                     }
    1690             : 
    1691             :                     // Update EMS internal variables
    1692    16256808 :                     SurfaceFD(Surf).condNodeReport(i) = kt;
    1693    16256808 :                     SurfaceFD(Surf).specHeatNodeReport(i) = Cp;
    1694             : 
    1695             :                     // Choose Regular or Transparent Insulation Case
    1696    16256808 :                     Real64 const DelX(ConstructFD(ConstrNum).DelX(Lay));
    1697    16256808 :                     Real64 const Delt_DelX(Delt * DelX);
    1698    16256808 :                     SurfaceFD(Surf).CpDelXRhoS1(i) = 0.0;                      // Outside face  does not have an outer half node
    1699    16256808 :                     SurfaceFD(Surf).CpDelXRhoS2(i) = (Cp * DelX * RhoS) / 2.0; // Save this for computing node flux values
    1700             : 
    1701    16256808 :                     if (HMovInsul <= 0.0) { // Regular  case
    1702             : 
    1703    16256808 :                         if (state.dataHeatBalFiniteDiffMgr->CondFDSchemeType == CondFDScheme::CrankNicholsonSecondOrder) { // Second Order equation
    1704           0 :                             Real64 const Cp_DelX_RhoS_2Delt(Cp * DelX * RhoS / (2.0 * Delt));
    1705           0 :                             Real64 const kt_2DelX(kt / (2.0 * DelX));
    1706           0 :                             Real64 const hsum(0.5 * (hconvo + hgnd + hrad + hsky));
    1707           0 :                             TDT_i = (QRadSWOutFD + Cp_DelX_RhoS_2Delt * TD_i + kt_2DelX * (TDT_p - TD_i + TD(i + 1)) + hgnd * Tgnd +
    1708           0 :                                      (hconvo + hrad) * Toa + hsky * Tsky - hsum * TD_i) /
    1709           0 :                                     (hsum + kt_2DelX + Cp_DelX_RhoS_2Delt);
    1710    16256808 :                         } else if (state.dataHeatBalFiniteDiffMgr->CondFDSchemeType == CondFDScheme::FullyImplicitFirstOrder) { // First Order
    1711    16256808 :                             Real64 const Two_Delt_DelX(2.0 * Delt_DelX);
    1712    16256808 :                             Real64 const Cp_DelX2_RhoS(Cp * pow_2(DelX) * RhoS);
    1713    16256808 :                             Real64 const Two_Delt_kt(2.0 * Delt * kt);
    1714    16256808 :                             TDT_i = (Two_Delt_DelX * (QRadSWOutFD + hgnd * Tgnd + (hconvo + hrad) * Toa + hsky * Tsky) + Cp_DelX2_RhoS * TD_i +
    1715    16256808 :                                      Two_Delt_kt * TDT_p) /
    1716    16256808 :                                     (Two_Delt_DelX * (hconvo + hgnd + hrad + hsky) + Two_Delt_kt + Cp_DelX2_RhoS);
    1717             :                         }
    1718             : 
    1719             :                     } else { // HMovInsul > 0.0: Transparent insulation on outside
    1720             :                         // Transparent insulation additions
    1721             : 
    1722             :                         // Movable Insulation Layer Outside surface temp
    1723             : 
    1724           0 :                         Real64 const TInsulOut((QRadSWOutMvInsulFD + hgnd * Tgnd + HMovInsul * TDT_i + (hconvo + hrad) * Toa + hsky * Tsky) /
    1725           0 :                                                (hconvo + hgnd + HMovInsul + hrad + hsky)); // Temperature of outside face of Outside Insulation
    1726           0 :                         Real64 const Two_Delt_DelX(2.0 * Delt_DelX);
    1727           0 :                         Real64 const Cp_DelX2_RhoS(Cp * pow_2(DelX) * RhoS);
    1728           0 :                         Real64 const Two_Delt_kt(2.0 * Delt * kt);
    1729             : 
    1730             :                         // Wall first node temperature behind Movable insulation
    1731           0 :                         if (state.dataHeatBalFiniteDiffMgr->CondFDSchemeType == CondFDScheme::CrankNicholsonSecondOrder) {
    1732           0 :                             TDT_i = (Two_Delt_DelX * (QRadSWOutFD + HMovInsul * TInsulOut) + Cp_DelX2_RhoS * TD_i + Two_Delt_kt * TDT_p) /
    1733           0 :                                     (Two_Delt_DelX * HMovInsul + Two_Delt_kt + Cp_DelX2_RhoS);
    1734           0 :                         } else if (state.dataHeatBalFiniteDiffMgr->CondFDSchemeType == CondFDScheme::FullyImplicitFirstOrder) {
    1735             :                             // Currently same as Crank Nicholson, need fully implicit formulation
    1736           0 :                             TDT_i = (Two_Delt_DelX * (QRadSWOutFD + HMovInsul * TInsulOut) + Cp_DelX2_RhoS * TD_i + Two_Delt_kt * TDT_p) /
    1737           0 :                                     (Two_Delt_DelX * HMovInsul + Two_Delt_kt + Cp_DelX2_RhoS);
    1738             :                         } else {
    1739           0 :                             assert(false); // Illegal CondFDSchemeType
    1740             :                         }
    1741             : 
    1742             :                     } // Regular layer or Movable insulation cases
    1743             : 
    1744             :                 } // R layer or Regular layer
    1745             : 
    1746    17373119 :                 CheckFDNodeTempLimits(state, Surf, i, TDT_i);
    1747             : 
    1748    17373119 :                 TDT(i) = TDT_i;
    1749             : 
    1750             :             } // regular detailed FD part or SigmaR SigmaC part
    1751             : 
    1752             :             // Determine net heat flux to outside face
    1753             :             // One formulation that works for Fully Implicit and CrankNicholson and massless wall
    1754             : 
    1755    17373119 :             Real64 const Toa_TDT_i(Toa - TDT_i);
    1756    17373119 :             Real64 const QNetSurfFromOutside(QRadSWOutFD + (hgnd * (-TDT_i + Tgnd) + (hconvo + hrad) * Toa_TDT_i + hsky * (-TDT_i + Tsky)));
    1757             : 
    1758             :             // Same sign convention as CTFs
    1759    17373119 :             state.dataHeatBalSurf->SurfOpaqOutFaceCondFlux(Surf) = -QNetSurfFromOutside;
    1760             : 
    1761             :             // Report all outside BC heat fluxes
    1762    17373119 :             state.dataHeatBalSurf->SurfQdotRadOutRepPerArea(Surf) = -(hgnd * (TDT_i - Tgnd) + hrad * (-Toa_TDT_i) + hsky * (TDT_i - Tsky));
    1763    17373119 :             state.dataHeatBalSurf->SurfQdotRadOutRep(Surf) = surface.Area * state.dataHeatBalSurf->SurfQdotRadOutRepPerArea(Surf);
    1764    17373119 :             state.dataHeatBalSurf->SurfQRadOutReport(Surf) = state.dataHeatBalSurf->SurfQdotRadOutRep(Surf) * state.dataGlobal->TimeStepZoneSec;
    1765             : 
    1766             :         } // regular BC part of the ground and Rain check
    1767    35043376 :     }
    1768             : 
    1769   207119728 :     void InteriorNodeEqns(EnergyPlusData &state,
    1770             :                           int const Delt,                               // Time Increment
    1771             :                           int const i,                                  // Node Index
    1772             :                           int const Lay,                                // Layer Number for Construction
    1773             :                           int const Surf,                               // Surface number
    1774             :                           [[maybe_unused]] Array1D<Real64> const &T,    // INSIDE SURFACE TEMPERATURE OF EACH HEAT TRANSFER SURF.
    1775             :                           [[maybe_unused]] Array1D<Real64> &TT,         // INSIDE SURFACE TEMPERATURE OF EACH HEAT TRANSFER SURF.
    1776             :                           [[maybe_unused]] Array1D<Real64> const &Rhov, // INSIDE SURFACE TEMPERATURE OF EACH HEAT TRANSFER SURF.
    1777             :                           [[maybe_unused]] Array1D<Real64> &RhoT,       // INSIDE SURFACE TEMPERATURE OF EACH HEAT TRANSFER SURF.
    1778             :                           [[maybe_unused]] Array1D<Real64> &RH,         // INSIDE SURFACE TEMPERATURE OF EACH HEAT TRANSFER SURF.
    1779             :                           Array1D<Real64> const &TD,                    // INSIDE SURFACE TEMPERATURE OF EACH HEAT TRANSFER SURF.
    1780             :                           Array1D<Real64> &TDT,                         // INSIDE SURFACE TEMPERATURE OF EACH HEAT TRANSFER SURF.
    1781             :                           Array1D<Real64> &EnthOld,                     // Old Nodal enthalpy
    1782             :                           Array1D<Real64> &EnthNew                      // New Nodal enthalpy
    1783             :     )
    1784             :     {
    1785             : 
    1786             :         // SUBROUTINE INFORMATION:
    1787             :         //       AUTHOR         Richard Liesen
    1788             :         //       DATE WRITTEN   November, 2003
    1789             :         //       MODIFIED       May 2011, B. Griffith and P. Tabares
    1790             :         //       RE-ENGINEERED  C. O. Pedersen, 2006
    1791             : 
    1792   207119728 :         int const ConstrNum(state.dataSurface->Surface(Surf).Construction);
    1793             : 
    1794   207119728 :         int const MatLay(state.dataConstruction->Construct(ConstrNum).LayerPoint(Lay));
    1795   207119728 :         auto const *mat(dynamic_cast<const Material::MaterialChild *>(state.dataMaterial->Material(MatLay)));
    1796   207119728 :         auto const &matFD(state.dataHeatBalFiniteDiffMgr->MaterialFD(MatLay));
    1797   207119728 :         auto const &condActuator(state.dataHeatBalFiniteDiffMgr->SurfaceFD(Surf).condMaterialActuators(Lay));
    1798   207119728 :         auto const &specHeatActuator(state.dataHeatBalFiniteDiffMgr->SurfaceFD(Surf).specHeatMaterialActuators(Lay));
    1799             : 
    1800   207119728 :         Real64 const TD_i(TD(i));
    1801             : 
    1802   207119728 :         Real64 const TDT_m(TDT(i - 1));
    1803   207119728 :         Real64 TDT_i(TDT(i));
    1804   207119728 :         Real64 const TDT_p(TDT(i + 1));
    1805   207119728 :         Real64 const TDT_mi((TDT_m + TDT_i) / 2.0);
    1806   207119728 :         Real64 const TDT_ip((TDT_i + TDT_p) / 2.0);
    1807             : 
    1808             :         //  Set Thermal Conductivity.  Can be constant, simple linear temp dep or multiple linear segment temp function dep.
    1809   207119728 :         auto const &matFD_TempCond(matFD.TempCond);
    1810   207119728 :         assert(matFD_TempCond.u2() >= 3);
    1811   207119728 :         Real64 const lTC(matFD_TempCond.index(2, 1));
    1812             :         Real64 ktA1; // Variable Outer Thermal conductivity in temperature equation
    1813             :         Real64 ktA2; // Thermal Inner conductivity in temperature equation
    1814   207119728 :         if (matFD_TempCond[lTC] + matFD_TempCond[lTC + 1] + matFD_TempCond[lTC + 2] >= 0.0) { // Multiple Linear Segment Function
    1815      225912 :             ktA1 = terpld(matFD.TempCond, TDT_ip, 1, 2);                                      // 1: Temperature, 2: Thermal conductivity
    1816      225912 :             ktA2 = terpld(matFD.TempCond, TDT_mi, 1, 2);                                      // 1: Temperature, 2: Thermal conductivity
    1817             :         } else {
    1818   206893816 :             ktA1 = ktA2 = mat->Conductivity; // 20C base conductivity
    1819   206893816 :             Real64 const kt1(matFD.tk1);     // temperature coefficient for simple temp dep k. // linear coefficient (normally zero)
    1820   206893816 :             if (kt1 != 0.0) {
    1821           0 :                 ktA1 += kt1 * (TDT_ip - 20.0);
    1822           0 :                 ktA2 += kt1 * (TDT_mi - 20.0);
    1823             :             }
    1824             :         }
    1825             : 
    1826   207119728 :         Real64 const Cpo(mat->SpecHeat); // Const Cp from input
    1827   207119728 :         Real64 Cp(Cpo);                  // Cp used // Will be changed if PCM
    1828   207119728 :         Real64 kt(0.0);
    1829   207119728 :         auto const &matFD_TempEnth(matFD.TempEnth);
    1830   207119728 :         assert(matFD_TempEnth.u2() >= 3);
    1831   207119728 :         Real64 const lTE(matFD_TempEnth.index(2, 1));
    1832   207119728 :         Real64 RhoS(mat->Density);
    1833   207119728 :         if (mat->phaseChange) {
    1834      329910 :             adjustPropertiesForPhaseChange(state, i, Surf, mat, TD_i, TDT_i, Cp, RhoS, kt);
    1835      329910 :             ktA1 = mat->phaseChange->getConductivity(TDT_ip);
    1836      329910 :             ktA2 = mat->phaseChange->getConductivity(TDT_mi);
    1837   206789818 :         } else if (matFD_TempEnth[lTE] + matFD_TempEnth[lTE + 1] + matFD_TempEnth[lTE + 2] >= 0.0) { // Phase change material: Use TempEnth data
    1838           0 :             EnthOld(i) = terpld(matFD_TempEnth, TD_i, 1, 2);                                         // 1: Temperature, 2: Enthalpy
    1839           0 :             EnthNew(i) = terpld(matFD_TempEnth, TDT_i, 1, 2);                                        // 1: Temperature, 2: Enthalpy
    1840           0 :             if (EnthNew(i) != EnthOld(i)) {
    1841           0 :                 Cp = max(Cpo, (EnthNew(i) - EnthOld(i)) / (TDT_i - TD_i));
    1842             :             }
    1843             :         } // Phase Change case
    1844             : 
    1845             :         // EMS Conductivity Override
    1846   207119728 :         if (condActuator.isActuated) {
    1847           0 :             kt = condActuator.actuatedValue;
    1848           0 :             ktA1 = kt;
    1849           0 :             ktA2 = kt;
    1850             :         }
    1851             : 
    1852             :         // EMS Specific Heat Override
    1853   207119728 :         if (specHeatActuator.isActuated) {
    1854           0 :             Cp = specHeatActuator.actuatedValue;
    1855             :         }
    1856             : 
    1857             :         // Update EMS internal variables
    1858   207119728 :         state.dataHeatBalFiniteDiffMgr->SurfaceFD(Surf).condNodeReport(i) = kt;
    1859   207119728 :         state.dataHeatBalFiniteDiffMgr->SurfaceFD(Surf).specHeatNodeReport(i) = Cp;
    1860             : 
    1861   207119728 :         Real64 const DelX(state.dataHeatBalFiniteDiffMgr->ConstructFD(ConstrNum).DelX(Lay));
    1862   207119728 :         Real64 const Cp_DelX_RhoS_Delt(Cp * DelX * RhoS / Delt);
    1863             : 
    1864   207119728 :         switch (state.dataHeatBalFiniteDiffMgr->CondFDSchemeType) {
    1865           0 :         case CondFDScheme::CrankNicholsonSecondOrder: { // Adams-Moulton second order
    1866           0 :             Real64 const inv2DelX(1.0 / (2.0 * DelX));
    1867           0 :             TDT_i = ((Cp_DelX_RhoS_Delt * TD_i) + ((ktA1 * (TD(i + 1) - TD_i + TDT_p) + ktA2 * (TD(i - 1) - TD_i + TDT_m)) * inv2DelX)) /
    1868           0 :                     (((ktA1 + ktA2) * inv2DelX) + Cp_DelX_RhoS_Delt);
    1869           0 :         } break;
    1870   207119728 :         case CondFDScheme::FullyImplicitFirstOrder: { // Adams-Moulton First order
    1871   207119728 :             Real64 const invDelX(1.0 / DelX);
    1872   207119728 :             TDT_i = ((Cp_DelX_RhoS_Delt * TD_i) + ((ktA2 * TDT_m) + (ktA1 * TDT_p)) * invDelX) / (((ktA1 + ktA2) * invDelX) + Cp_DelX_RhoS_Delt);
    1873   207119728 :         } break;
    1874           0 :         default:
    1875           0 :             assert(false); // Illegal CondFDSchemeType
    1876             :         }
    1877             : 
    1878   207119728 :         CheckFDNodeTempLimits(state, Surf, i, TDT_i);
    1879             : 
    1880   207119728 :         TDT(i) = TDT_i;
    1881   207119728 :         state.dataHeatBalFiniteDiffMgr->SurfaceFD(Surf).CpDelXRhoS1(i) = state.dataHeatBalFiniteDiffMgr->SurfaceFD(Surf).CpDelXRhoS2(i) =
    1882   207119728 :             (Cp * DelX * RhoS) / 2.0; // Save this for computing node flux values, half nodes are the same here
    1883   207119728 :     }
    1884             : 
    1885    83554283 :     void IntInterfaceNodeEqns(EnergyPlusData &state,
    1886             :                               int const Delt,                                  // Time Increment
    1887             :                               int const i,                                     // Node Index
    1888             :                               int const Lay,                                   // Layer Number for Construction
    1889             :                               int const Surf,                                  // Surface number
    1890             :                               [[maybe_unused]] Array1D<Real64> const &T,       // INSIDE SURFACE TEMPERATURE OF EACH HEAT TRANSFER SURF.
    1891             :                               [[maybe_unused]] Array1D<Real64> &TT,            // INSIDE SURFACE TEMPERATURE OF EACH HEAT TRANSFER SURF.
    1892             :                               [[maybe_unused]] Array1D<Real64> const &Rhov,    // INSIDE SURFACE TEMPERATURE OF EACH HEAT TRANSFER SURF.
    1893             :                               [[maybe_unused]] Array1D<Real64> &RhoT,          // INSIDE SURFACE TEMPERATURE OF EACH HEAT TRANSFER SURF.
    1894             :                               [[maybe_unused]] Array1D<Real64> &RH,            // RELATIVE HUMIDITY.
    1895             :                               Array1D<Real64> const &TD,                       // OLD NODE TEMPERATURES OF EACH HEAT TRANSFER SURF IN CONDFD.
    1896             :                               Array1D<Real64> &TDT,                            // NEW NODE TEMPERATURES OF EACH HEAT TRANSFER SURF IN CONDFD.
    1897             :                               [[maybe_unused]] Array1D<Real64> const &EnthOld, // Old Nodal enthalpy
    1898             :                               Array1D<Real64> &EnthNew,                        // New Nodal enthalpy
    1899             :                               [[maybe_unused]] int const GSiter                // Iteration number of Gauss Seidel iteration
    1900             :     )
    1901             :     {
    1902             : 
    1903             :         // SUBROUTINE INFORMATION:
    1904             :         //       AUTHOR         Richard Liesen
    1905             :         //       DATE WRITTEN   November, 2003
    1906             :         //       MODIFIED       May 2011, B. Griffith, P. Tabares,  add first order fully implicit, bug fixes, cleanup
    1907             :         //       RE-ENGINEERED  Curtis Pedersen, Changed to Implicit mode and included enthalpy.  FY2006
    1908             : 
    1909             :         // PURPOSE OF THIS SUBROUTINE:
    1910             :         // calculate finite difference heat transfer for nodes that interface two different material layers inside construction
    1911             : 
    1912    83554283 :         auto const &surface(state.dataSurface->Surface(Surf));
    1913             : 
    1914    83554283 :         if (surface.HeatTransferAlgorithm == DataSurfaces::HeatTransferModel::CondFD) { // HT Algo issue
    1915             : 
    1916    83554283 :             int const ConstrNum(surface.Construction);
    1917    83554283 :             auto const &construct(state.dataConstruction->Construct(ConstrNum));
    1918             : 
    1919    83554283 :             int const MatLay(construct.LayerPoint(Lay));
    1920    83554283 :             auto const *mat(dynamic_cast<const Material::MaterialChild *>(state.dataMaterial->Material(MatLay)));
    1921             : 
    1922    83554283 :             int const MatLay2(construct.LayerPoint(Lay + 1));
    1923    83554283 :             auto const *mat2(dynamic_cast<const Material::MaterialChild *>(state.dataMaterial->Material(MatLay2)));
    1924             : 
    1925    83554283 :             auto const &condActuator1(state.dataHeatBalFiniteDiffMgr->SurfaceFD(Surf).condMaterialActuators(Lay));
    1926    83554283 :             auto const &condActuator2(state.dataHeatBalFiniteDiffMgr->SurfaceFD(Surf).condMaterialActuators(Lay + 1));
    1927             : 
    1928    83554283 :             auto const &specHeatActuator1(state.dataHeatBalFiniteDiffMgr->SurfaceFD(Surf).specHeatMaterialActuators(Lay));
    1929    83554283 :             auto const &specHeatActuator2(state.dataHeatBalFiniteDiffMgr->SurfaceFD(Surf).specHeatMaterialActuators(Lay + 1));
    1930             : 
    1931    83554283 :             auto const &heatFluxActuator(state.dataHeatBalFiniteDiffMgr->SurfaceFD(Surf).heatSourceFluxMaterialActuators(Lay));
    1932             : 
    1933    83554283 :             Real64 const TDT_m(TDT(i - 1));
    1934    83554283 :             Real64 const TDT_p(TDT(i + 1));
    1935             : 
    1936    83554283 :             bool const RLayerPresent(mat->ROnly || mat->group == Material::Group::Air);
    1937    83554283 :             bool const RLayer2Present(mat2->ROnly || mat2->group == Material::Group::Air);
    1938             : 
    1939    83554283 :             Real64 const Rlayer(mat->Resistance);   // Resistance value of R Layer
    1940    83554283 :             Real64 const Rlayer2(mat2->Resistance); // Resistance value of next layer to inside
    1941             : 
    1942    83554283 :             if (RLayerPresent && RLayer2Present) {
    1943             : 
    1944           0 :                 TDT(i) = (Rlayer2 * TDT_m + Rlayer * TDT_p) / (Rlayer + Rlayer2); // Two adjacent R layers
    1945             : 
    1946             :             } else {
    1947             : 
    1948    83554283 :                 auto const &matFD(state.dataHeatBalFiniteDiffMgr->MaterialFD(MatLay));
    1949    83554283 :                 auto const &matFD2(state.dataHeatBalFiniteDiffMgr->MaterialFD(MatLay2));
    1950    83554283 :                 Real64 TDT_i(TDT(i));
    1951             : 
    1952             :                 // Set Thermal Conductivity. Can be constant, simple linear temp dep or multiple linear segment temp function dep.
    1953             : 
    1954    83554283 :                 Real64 kt1(0.0);
    1955    83554283 :                 if (!RLayerPresent) {
    1956    83360535 :                     auto const &matFD_TempCond(matFD.TempCond);
    1957    83360535 :                     assert(matFD_TempCond.u2() >= 3);
    1958    83360535 :                     Real64 const lTC(matFD_TempCond.index(2, 1));
    1959    83360535 :                     if (matFD_TempCond[lTC] + matFD_TempCond[lTC + 1] + matFD_TempCond[lTC + 2] >= 0.0) { // Multiple Linear Segment Function
    1960           0 :                         kt1 = terpld(matFD.TempCond, (TDT_i + TDT_m) / 2.0, 1, 2);                        // 1: Temperature, 2: Thermal conductivity
    1961             :                     } else {
    1962    83360535 :                         kt1 = mat->Conductivity;      // 20C base conductivity
    1963    83360535 :                         Real64 const kt11(matFD.tk1); // temperature coefficient for simple temp dep k. // linear coefficient (normally zero)
    1964    83360535 :                         if (kt11 != 0.0) kt1 += kt11 * ((TDT_i + TDT_m) / 2.0 - 20.0);
    1965             :                     }
    1966             :                 }
    1967             : 
    1968    83554283 :                 Real64 kt2(0.0);
    1969    83554283 :                 if (!RLayer2Present) {
    1970    83360535 :                     auto const &matFD2_TempCond(matFD2.TempCond);
    1971    83360535 :                     assert(matFD2_TempCond.u2() >= 3);
    1972    83360535 :                     Real64 const lTC2(matFD2_TempCond.index(2, 1));
    1973    83360535 :                     if (matFD2_TempCond[lTC2] + matFD2_TempCond[lTC2 + 1] + matFD2_TempCond[lTC2 + 2] >= 0.0) { // Multiple Linear Segment Function
    1974           0 :                         kt2 = terpld(matFD2_TempCond, (TDT_i + TDT_p) / 2.0, 1, 2); // 1: Temperature, 2: Thermal conductivity
    1975             :                     } else {
    1976    83360535 :                         kt2 = mat2->Conductivity;      // 20C base conductivity
    1977    83360535 :                         Real64 const kt21(matFD2.tk1); // temperature coefficient for simple temp dep k. // linear coefficient (normally zero)
    1978    83360535 :                         if (kt21 != 0.0) kt2 += kt21 * ((TDT_i + TDT_p) / 2.0 - 20.0);
    1979             :                     }
    1980             :                 }
    1981             : 
    1982    83554283 :                 Real64 RhoS1(mat->Density);
    1983    83554283 :                 Real64 const Cpo1(mat->SpecHeat); // constant Cp from input file
    1984    83554283 :                 Real64 Cp1(Cpo1);                 // Will be reset if PCM
    1985    83554283 :                 Real64 const Delx1(state.dataHeatBalFiniteDiffMgr->ConstructFD(ConstrNum).DelX(Lay));
    1986             : 
    1987    83554283 :                 Real64 RhoS2(mat2->Density);
    1988    83554283 :                 Real64 const Cpo2(mat2->SpecHeat);
    1989    83554283 :                 Real64 Cp2(Cpo2); // will be reset if PCM
    1990    83554283 :                 Real64 const Delx2(state.dataHeatBalFiniteDiffMgr->ConstructFD(ConstrNum).DelX(Lay + 1));
    1991             : 
    1992             :                 // Calculate the Dry Heat Conduction Equation
    1993             : 
    1994             :                 // Source/Sink Flux Capability ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    1995             : 
    1996    83554283 :                 Real64 QSSFlux = 0.0;
    1997    83554283 :                 if ((surface.Area > 0.0) && (construct.SourceSinkPresent && Lay == construct.SourceAfterLayer)) {
    1998             :                     // Source/Sink flux value at a layer interface // Includes QPV Source
    1999     5787108 :                     QSSFlux = (state.dataHeatBalFanSys->QRadSysSource(Surf) + state.dataHeatBalFanSys->QPVSysSource(Surf)) / surface.Area;
    2000             :                 }
    2001             : 
    2002             :                 // update report variables
    2003    83554283 :                 auto &surfFD = state.dataHeatBalFiniteDiffMgr->SurfaceFD(Surf);
    2004             : 
    2005             :                 // only includes internal heat source
    2006    83554283 :                 surfFD.heatSourceInternalFluxLayerReport(Lay) = QSSFlux * surface.Area;
    2007    83554283 :                 surfFD.heatSourceInternalFluxEnergyLayerReport(Lay) = QSSFlux * surface.Area * state.dataGlobal->TimeStepZoneSec;
    2008             : 
    2009             :                 // Add EMS actuated value
    2010    83554283 :                 if (heatFluxActuator.isActuated) {
    2011      160612 :                     Real64 actuatedVal = heatFluxActuator.actuatedValue;
    2012      160612 :                     if (actuatedVal >= 0) {
    2013      160612 :                         QSSFlux += heatFluxActuator.actuatedValue;
    2014             :                     } else {
    2015           0 :                         ShowSevereError(state, fmt::format("Surface: {}, Material: {}", surface.Name, mat->Name));
    2016           0 :                         ShowContinueError(state, "EMS Actuator does not support negative values");
    2017           0 :                         ShowFatalError(state, "Program terminates due to preceding conditions.");
    2018             :                     }
    2019             : 
    2020             :                     // Update report variables
    2021             :                     // Only includes the EMS values
    2022      160612 :                     surfFD.heatSourceEMSFluxLayerReport(Lay) = heatFluxActuator.actuatedValue * surface.Area;
    2023      160612 :                     surfFD.heatSourceEMSFluxEnergyLayerReport(Lay) =
    2024      160612 :                         heatFluxActuator.actuatedValue * surface.Area * state.dataGlobal->TimeStepZoneSec;
    2025             :                 }
    2026             : 
    2027             :                 //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    2028             : 
    2029    83554283 :                 Real64 const TD_i(TD(i));
    2030             : 
    2031    83554283 :                 auto const &matFD_TempEnth(matFD.TempEnth);
    2032    83554283 :                 assert(matFD_TempEnth.u2() >= 3);
    2033    83554283 :                 Real64 const lTE(matFD_TempEnth.index(2, 1));
    2034    83554283 :                 Real64 const matFD_sum(matFD_TempEnth[lTE] + matFD_TempEnth[lTE + 1] + matFD_TempEnth[lTE + 2]);
    2035             : 
    2036    83554283 :                 auto const &matFD2_TempEnth(matFD2.TempEnth);
    2037    83554283 :                 assert(matFD2_TempEnth.u2() >= 3);
    2038    83554283 :                 Real64 const lTE2(matFD2_TempEnth.index(2, 1));
    2039    83554283 :                 Real64 const matFD2_sum(matFD2_TempEnth[lTE2] + matFD2_TempEnth[lTE2 + 1] + matFD2_TempEnth[lTE2 + 2]);
    2040             : 
    2041    83554283 :                 if (RLayerPresent && !RLayer2Present) { // R-layer first
    2042             : 
    2043             :                     // Check for PCM second layer
    2044      193748 :                     if (mat2->phaseChange) {
    2045           0 :                         adjustPropertiesForPhaseChange(state, i, Surf, mat2, TD_i, TDT_i, Cp2, RhoS2, kt2);
    2046      193748 :                     } else if ((matFD_sum < 0.0) && (matFD2_sum > 0.0)) {            // Phase change material Layer2, Use TempEnth Data
    2047           0 :                         Real64 const Enth2Old(terpld(matFD2_TempEnth, TD_i, 1, 2));  // 1: Temperature, 2: Thermal conductivity
    2048           0 :                         Real64 const Enth2New(terpld(matFD2_TempEnth, TDT_i, 1, 2)); // 1: Temperature, 2: Thermal conductivity
    2049           0 :                         EnthNew(i) = Enth2New; // This node really doesn't have an enthalpy, this gives it a value
    2050           0 :                         if ((std::abs(Enth2New - Enth2Old) > smalldiff) && (std::abs(TDT_i - TD_i) > smalldiff)) {
    2051           0 :                             Cp2 = max(Cpo2, (Enth2New - Enth2Old) / (TDT_i - TD_i));
    2052             :                         }
    2053             :                     }
    2054             : 
    2055             :                     // EMS Conductivity 2 Override
    2056      193748 :                     if (condActuator2.isActuated) {
    2057           0 :                         kt2 = condActuator1.actuatedValue;
    2058             :                     }
    2059             : 
    2060             :                     // EMS Specific Heat 2 Override
    2061      193748 :                     if (specHeatActuator2.isActuated) {
    2062           0 :                         Cp2 = specHeatActuator1.actuatedValue;
    2063             :                     }
    2064             : 
    2065             :                     // Update EMS internal variables
    2066      193748 :                     surfFD.condNodeReport(i) = kt1;
    2067      193748 :                     surfFD.specHeatNodeReport(i) = Cp1;
    2068      193748 :                     surfFD.condNodeReport(i + 1) = kt2;
    2069      193748 :                     surfFD.specHeatNodeReport(i + 1) = Cp2;
    2070             : 
    2071             :                     // R layer first, then PCM or regular layer
    2072      193748 :                     Real64 const Delt_Delx2(Delt * Delx2);
    2073      193748 :                     Real64 const Cp2_fac(Cp2 * pow_2(Delx2) * RhoS2 * Rlayer);
    2074      193748 :                     Real64 const Delt_kt2_Rlayer(Delt * kt2 * Rlayer);
    2075      193748 :                     if (state.dataHeatBalFiniteDiffMgr->CondFDSchemeType == CondFDScheme::CrankNicholsonSecondOrder) {
    2076           0 :                         TDT_i = (2.0 * Delt_Delx2 * QSSFlux * Rlayer + (Cp2_fac - Delt_Delx2 - Delt_kt2_Rlayer) * TD_i +
    2077           0 :                                  Delt_Delx2 * (TD(i - 1) + TDT_m) + Delt_kt2_Rlayer * (TD(i + 1) + TDT_p)) /
    2078           0 :                                 (Delt_Delx2 + Delt_kt2_Rlayer + Cp2_fac);
    2079      193748 :                     } else if (state.dataHeatBalFiniteDiffMgr->CondFDSchemeType == CondFDScheme::FullyImplicitFirstOrder) {
    2080      193748 :                         Real64 const Two_Delt_Delx2(2.0 * Delt_Delx2);
    2081      193748 :                         Real64 const Two_Delt_kt2_Rlayer(2.0 * Delt_kt2_Rlayer);
    2082      193748 :                         TDT_i = (Two_Delt_Delx2 * (QSSFlux * Rlayer + TDT_m) + Cp2_fac * TD_i + Two_Delt_kt2_Rlayer * TDT_p) /
    2083      193748 :                                 (Two_Delt_Delx2 + Two_Delt_kt2_Rlayer + Cp2_fac);
    2084             :                     }
    2085             : 
    2086      193748 :                     CheckFDNodeTempLimits(state, Surf, i, TDT_i);
    2087             : 
    2088      193748 :                     state.dataHeatBalFiniteDiffMgr->SurfaceFD(Surf).CpDelXRhoS1(i) = 0.0; //  - rlayer has no capacitance, so this is zero
    2089      193748 :                     state.dataHeatBalFiniteDiffMgr->SurfaceFD(Surf).CpDelXRhoS2(i) =
    2090      193748 :                         (Cp2 * Delx2 * RhoS2) / 2.0; // Save this for computing node flux values
    2091             : 
    2092    83554283 :                 } else if (!RLayerPresent && RLayer2Present) { // R-layer second
    2093             : 
    2094             :                     // Check for PCM layer before R layer
    2095      193748 :                     if (mat->phaseChange) {
    2096           0 :                         adjustPropertiesForPhaseChange(state, i, Surf, mat, TD_i, TDT_i, Cp1, RhoS1, kt1);
    2097      193748 :                     } else if ((matFD_sum > 0.0) && (matFD2_sum < 0.0)) {           // Phase change material Layer1, Use TempEnth Data
    2098           0 :                         Real64 const Enth1Old(terpld(matFD_TempEnth, TD_i, 1, 2));  // 1: Temperature, 2: Thermal conductivity
    2099           0 :                         Real64 const Enth1New(terpld(matFD_TempEnth, TDT_i, 1, 2)); // 1: Temperature, 2: Thermal conductivity
    2100           0 :                         EnthNew(i) = Enth1New; // This node really doesn't have an enthalpy, this gives it a value
    2101           0 :                         if ((std::abs(Enth1New - Enth1Old) > smalldiff) && (std::abs(TDT_i - TD_i) > smalldiff)) {
    2102           0 :                             Cp1 = max(Cpo1, (Enth1New - Enth1Old) / (TDT_i - TD_i));
    2103             :                         }
    2104             :                     }
    2105             : 
    2106             :                     // EMS Conductivity 1 Override
    2107      193748 :                     if (condActuator1.isActuated) {
    2108           0 :                         kt1 = condActuator1.actuatedValue;
    2109             :                     }
    2110             : 
    2111             :                     // EMS Specific Heat 1 Override
    2112      193748 :                     if (specHeatActuator1.isActuated) {
    2113           0 :                         Cp1 = specHeatActuator1.actuatedValue;
    2114             :                     }
    2115             : 
    2116             :                     // Update EMS internal variables
    2117      193748 :                     surfFD.condNodeReport(i) = kt1;
    2118      193748 :                     surfFD.specHeatNodeReport(i) = Cp1;
    2119      193748 :                     surfFD.condNodeReport(i + 1) = kt2;
    2120      193748 :                     surfFD.specHeatNodeReport(i + 1) = Cp2;
    2121             : 
    2122      193748 :                     Real64 const Delt_Delx1(Delt * Delx1);
    2123      193748 :                     Real64 const Cp1_fac(Cp1 * pow_2(Delx1) * RhoS1 * Rlayer2);
    2124      193748 :                     Real64 const Delt_kt1_Rlayer2(Delt * kt1 * Rlayer2);
    2125      193748 :                     if (state.dataHeatBalFiniteDiffMgr->CondFDSchemeType == CondFDScheme::CrankNicholsonSecondOrder) {
    2126           0 :                         TDT_i = (2.0 * Delt_Delx1 * QSSFlux * Rlayer2 + (Cp1_fac - Delt_Delx1 - Delt_kt1_Rlayer2) * TD_i +
    2127           0 :                                  Delt_Delx1 * (TD(i + 1) + TDT_p) + Delt_kt1_Rlayer2 * (TD(i - 1) + TDT_m)) /
    2128           0 :                                 (Delt_Delx1 + Delt_kt1_Rlayer2 + Cp1_fac);
    2129      193748 :                     } else if (state.dataHeatBalFiniteDiffMgr->CondFDSchemeType == CondFDScheme::FullyImplicitFirstOrder) {
    2130      193748 :                         Real64 const Two_Delt_Delx1(2.0 * Delt_Delx1);
    2131      193748 :                         Real64 const Two_Delt_kt1_Rlayer2(2.0 * Delt_kt1_Rlayer2);
    2132      193748 :                         TDT_i = (Two_Delt_Delx1 * (QSSFlux * Rlayer2 + TDT_p) + Cp1_fac * TD_i + Two_Delt_kt1_Rlayer2 * TDT_m) /
    2133      193748 :                                 (Two_Delt_Delx1 + Two_Delt_kt1_Rlayer2 + Cp1_fac);
    2134             :                     }
    2135             : 
    2136      193748 :                     CheckFDNodeTempLimits(state, Surf, i, TDT_i);
    2137             : 
    2138      193748 :                     state.dataHeatBalFiniteDiffMgr->SurfaceFD(Surf).CpDelXRhoS1(i) =
    2139      193748 :                         (Cp1 * Delx1 * RhoS1) / 2.0;                                      // Save this for computing node flux values
    2140      193748 :                     state.dataHeatBalFiniteDiffMgr->SurfaceFD(Surf).CpDelXRhoS2(i) = 0.0; //  - rlayer has no capacitance, so this is zero
    2141             : 
    2142      193748 :                 } else { // Regular or Phase Change on both sides of interface
    2143             : 
    2144             :                     // Consider the various PCM material location cases
    2145    83166787 :                     if ((matFD_sum > 0.0) && (matFD2_sum > 0.0)) { // Phase change material both layers, Use TempEnth Data
    2146             : 
    2147           0 :                         Real64 const Enth1Old(terpld(matFD_TempEnth, TD_i, 1, 2));   // 1: Temperature, 2: Thermal conductivity
    2148           0 :                         Real64 const Enth2Old(terpld(matFD2_TempEnth, TD_i, 1, 2));  // 1: Temperature, 2: Thermal conductivity
    2149           0 :                         Real64 const Enth1New(terpld(matFD_TempEnth, TDT_i, 1, 2));  // 1: Temperature, 2: Thermal conductivity
    2150           0 :                         Real64 const Enth2New(terpld(matFD2_TempEnth, TDT_i, 1, 2)); // 1: Temperature, 2: Thermal conductivity
    2151             : 
    2152           0 :                         EnthNew(i) = Enth1New; // This node really doesn't have an enthalpy, this gives it a value
    2153             : 
    2154           0 :                         if ((std::abs(Enth1New - Enth1Old) > smalldiff) && (std::abs(TDT_i - TD_i) > smalldiff)) {
    2155           0 :                             Cp1 = max(Cpo1, (Enth1New - Enth1Old) / (TDT_i - TD_i));
    2156             :                         }
    2157             : 
    2158           0 :                         if ((std::abs(Enth2New - Enth2Old) > smalldiff) && (std::abs(TDT_i - TD_i) > smalldiff)) {
    2159           0 :                             Cp2 = max(Cpo2, (Enth2New - Enth2Old) / (TDT_i - TD_i));
    2160             :                         }
    2161             : 
    2162             :                         // if
    2163             : 
    2164    83166787 :                     } else if ((matFD_sum > 0.0) && (matFD2_sum < 0.0)) { // Phase change material Layer1, Use TempEnth Data
    2165             : 
    2166      139981 :                         Real64 const Enth1Old(terpld(matFD_TempEnth, TD_i, 1, 2));  // 1: Temperature, 2: Thermal conductivity
    2167      139981 :                         Real64 const Enth1New(terpld(matFD_TempEnth, TDT_i, 1, 2)); // 1: Temperature, 2: Thermal conductivity
    2168      139981 :                         EnthNew(i) = Enth1New; // This node really doesn't have an enthalpy, this gives it a value
    2169             : 
    2170      139981 :                         if ((std::abs(Enth1New - Enth1Old) > smalldiff) && (std::abs(TDT_i - TD_i) > smalldiff)) {
    2171       98121 :                             Cp1 = max(Cpo1, (Enth1New - Enth1Old) / (TDT_i - TD_i));
    2172             :                         }
    2173             : 
    2174    83166787 :                     } else if ((matFD_sum < 0.0) && (matFD2_sum > 0.0)) { // Phase change material Layer2, Use TempEnth Data
    2175             : 
    2176      297108 :                         Real64 const Enth2Old(terpld(matFD2_TempEnth, TD_i, 1, 2));  // 1: Temperature, 2: Thermal conductivity
    2177      297108 :                         Real64 const Enth2New(terpld(matFD2_TempEnth, TDT_i, 1, 2)); // 1: Temperature, 2: Thermal conductivity
    2178      297108 :                         EnthNew(i) = Enth2New; // This node really doesn't have an enthalpy, this gives it a value
    2179             : 
    2180      297108 :                         if ((std::abs(Enth2New - Enth2Old) > smalldiff) && (std::abs(TDT_i - TD_i) > smalldiff)) {
    2181      214156 :                             Cp2 = max(Cpo2, (Enth2New - Enth2Old) / (TDT_i - TD_i));
    2182             :                         }
    2183             : 
    2184             :                     } // Phase change material check
    2185             : 
    2186    83166787 :                     if (mat->phaseChange) {
    2187           0 :                         adjustPropertiesForPhaseChange(state, i, Surf, mat, TD_i, TDT_i, Cp1, RhoS1, kt1);
    2188             :                     }
    2189    83166787 :                     if (mat2->phaseChange) {
    2190           0 :                         adjustPropertiesForPhaseChange(state, i, Surf, mat2, TD_i, TDT_i, Cp2, RhoS2, kt2);
    2191             :                     }
    2192             : 
    2193             :                     // EMS Conductivity 1 Override
    2194    83166787 :                     if (condActuator1.isActuated) {
    2195      477915 :                         kt1 = condActuator1.actuatedValue;
    2196             :                     }
    2197             : 
    2198             :                     // EMS Conductivity 2 Override
    2199    83166787 :                     if (condActuator2.isActuated) {
    2200      477915 :                         kt2 = condActuator2.actuatedValue;
    2201             :                     }
    2202             : 
    2203             :                     // EMS Specific Heat 1 Override
    2204    83166787 :                     if (specHeatActuator1.isActuated) {
    2205      477915 :                         Cp1 = specHeatActuator1.actuatedValue;
    2206             :                     }
    2207             : 
    2208             :                     // EMS Specific Heat 2 Override
    2209    83166787 :                     if (specHeatActuator2.isActuated) {
    2210      477915 :                         Cp2 = specHeatActuator2.actuatedValue;
    2211             :                     }
    2212             : 
    2213             :                     // Update EMS internal variables
    2214    83166787 :                     surfFD.condNodeReport(i) = kt1;
    2215    83166787 :                     surfFD.specHeatNodeReport(i) = Cp1;
    2216    83166787 :                     surfFD.condNodeReport(i + 1) = kt2;
    2217    83166787 :                     surfFD.specHeatNodeReport(i + 1) = Cp2;
    2218             : 
    2219    83166787 :                     Real64 const Delt_Delx1(Delt * Delx1);
    2220    83166787 :                     Real64 const Delt_Delx2(Delt * Delx2);
    2221    83166787 :                     Real64 const Delt_Delx1_kt2(Delt_Delx1 * kt2);
    2222    83166787 :                     Real64 const Delt_Delx2_kt1(Delt_Delx2 * kt1);
    2223    83166787 :                     Real64 const Delt_sum(Delt_Delx1_kt2 + Delt_Delx2_kt1);
    2224    83166787 :                     Real64 const Cp1_fac(Cp1 * pow_2(Delx1) * Delx2 * RhoS1);
    2225    83166787 :                     Real64 const Cp2_fac(Cp2 * Delx1 * pow_2(Delx2) * RhoS2);
    2226    83166787 :                     Real64 const Cp_fac(Cp1_fac + Cp2_fac);
    2227    83166787 :                     if (state.dataHeatBalFiniteDiffMgr->CondFDSchemeType ==
    2228             :                         CondFDScheme::CrankNicholsonSecondOrder) { // Regular Internal Interface Node with Source/sink using Adams Moulton second
    2229             :                         // order
    2230           0 :                         TDT_i = (2.0 * Delt_Delx1 * Delx2 * QSSFlux + (Cp_fac - Delt_sum) * TD_i + Delt_Delx1_kt2 * (TD(i + 1) + TDT_p) +
    2231           0 :                                  Delt_Delx2_kt1 * (TD(i - 1) + TDT_m)) /
    2232           0 :                                 (Delt_sum + Cp_fac);
    2233    83166787 :                     } else if (state.dataHeatBalFiniteDiffMgr->CondFDSchemeType ==
    2234             :                                CondFDScheme::FullyImplicitFirstOrder) { // First order adams moulton
    2235    83166787 :                         TDT_i = (2.0 * (Delt_Delx1 * Delx2 * QSSFlux + Delt_Delx2_kt1 * TDT_m + Delt_Delx1_kt2 * TDT_p) + Cp_fac * TD_i) /
    2236    83166787 :                                 (2.0 * (Delt_Delx2_kt1 + Delt_Delx1_kt2) + Cp_fac);
    2237             :                     }
    2238             : 
    2239    83166787 :                     CheckFDNodeTempLimits(state, Surf, i, TDT_i);
    2240             : 
    2241    83166787 :                     state.dataHeatBalFiniteDiffMgr->SurfaceFD(Surf).CpDelXRhoS1(i) =
    2242    83166787 :                         (Cp1 * Delx1 * RhoS1) / 2.0; // Save this for computing node flux values
    2243    83166787 :                     state.dataHeatBalFiniteDiffMgr->SurfaceFD(Surf).CpDelXRhoS2(i) =
    2244    83166787 :                         (Cp2 * Delx2 * RhoS2) / 2.0; // Save this for computing node flux values
    2245             : 
    2246    83166787 :                     if (construct.SourceSinkPresent && (Lay == construct.SourceAfterLayer)) {
    2247     5787108 :                         state.dataHeatBalFanSys->TCondFDSourceNode(Surf) = TDT_i; // Transfer node temp to Radiant System
    2248     5787108 :                         state.dataHeatBalSurf->SurfTempSource(Surf) = TDT_i;      // Transfer node temp to DataHeatBalSurface module
    2249     5787108 :                         state.dataHeatBalFiniteDiffMgr->SurfaceFD(Surf).QSource = QSSFlux;
    2250     5787108 :                         state.dataHeatBalFiniteDiffMgr->SurfaceFD(Surf).SourceNodeNum = i;
    2251             :                     }
    2252             : 
    2253    83166787 :                     if (construct.SourceSinkPresent && (Lay == construct.TempAfterLayer)) {
    2254     5787108 :                         state.dataHeatBalSurf->SurfTempUserLoc(Surf) = TDT_i; // Transfer node temp to DataHeatBalSurface module
    2255             :                     }
    2256             : 
    2257             :                 } // End of R-layer and Regular check
    2258             : 
    2259    83554283 :                 TDT(i) = TDT_i;
    2260             :             }
    2261             : 
    2262             :         } // End of the CondFD if block
    2263    83554283 :     }
    2264             : 
    2265    46855557 :     void InteriorBCEqns(EnergyPlusData &state,
    2266             :                         int const Delt,                               // Time Increment
    2267             :                         int const i,                                  // Node Index
    2268             :                         int const Lay,                                // Layer Number for Construction
    2269             :                         int const Surf,                               // Surface number
    2270             :                         [[maybe_unused]] Array1D<Real64> const &T,    // INSIDE SURFACE TEMPERATURE OF EACH HEAT TRANSFER SURF (Old).
    2271             :                         [[maybe_unused]] Array1D<Real64> &TT,         // INSIDE SURFACE TEMPERATURE OF EACH HEAT TRANSFER SURF (New).
    2272             :                         [[maybe_unused]] Array1D<Real64> const &Rhov, // INSIDE SURFACE TEMPERATURE OF EACH HEAT TRANSFER SURF.
    2273             :                         [[maybe_unused]] Array1D<Real64> &RhoT,       // INSIDE SURFACE TEMPERATURE OF EACH HEAT TRANSFER SURF.
    2274             :                         [[maybe_unused]] Array1D<Real64> &RH,         // INSIDE SURFACE TEMPERATURE OF EACH HEAT TRANSFER SURF.
    2275             :                         Array1D<Real64> const &TD,                    // INSIDE SURFACE TEMPERATURE OF EACH HEAT TRANSFER SURF.
    2276             :                         Array1D<Real64> &TDT,                         // INSIDE SURFACE TEMPERATURE OF EACH HEAT TRANSFER SURF.
    2277             :                         Array1D<Real64> &EnthOld,                     // Old Nodal enthalpy
    2278             :                         Array1D<Real64> &EnthNew,                     // New Nodal enthalpy
    2279             :                         Array1D<Real64> &TDreport                     // Temperature value from previous HeatSurfaceHeatManager iteration's value
    2280             :     )
    2281             :     {
    2282             :         // SUBROUTINE INFORMATION:
    2283             :         //       AUTHOR         Richard Liesen
    2284             :         //       DATE WRITTEN   November, 2003
    2285             :         //       MODIFIED       B. Griffith, P. Tabares, May 2011, add first order fully implicit, bug fixes, cleanup
    2286             :         //                      November 2011 P. Tabares fixed problems with adiabatic walls/massless walls
    2287             :         //                      November 2011 P. Tabares fixed problems PCM stability problems
    2288             :         //       RE-ENGINEERED  C. O. Pedersen 2006
    2289             : 
    2290             :         // PURPOSE OF THIS SUBROUTINE:
    2291             :         // Calculate the heat transfer at the node on the surfaces inside face (facing zone)
    2292             : 
    2293    46855557 :         auto const &surface(state.dataSurface->Surface(Surf));
    2294             : 
    2295    46855557 :         int const ConstrNum(surface.Construction);
    2296             : 
    2297             :         // Set the internal conditions to local variables
    2298             :         Real64 const NetLWRadToSurfFD(
    2299    46855557 :             state.dataHeatBalSurf->SurfQdotRadNetLWInPerArea(Surf)); // Net interior long wavelength radiation to surface from other surfaces
    2300    46855557 :         Real64 const QRadSWInFD(state.dataHeatBalSurf->SurfOpaqQRadSWInAbs(Surf)); // Short wave radiation absorbed on inside of opaque surface
    2301             :         Real64 const SurfQdotRadHVACInPerAreaFD(
    2302    46855557 :             state.dataHeatBalSurf->SurfQdotRadHVACInPerArea(Surf));                        // Total current radiant heat flux at a surface
    2303    46855557 :         Real64 const QRadThermInFD(state.dataHeatBal->SurfQdotRadIntGainsInPerArea(Surf)); // Thermal radiation absorbed on inside surfaces
    2304             : 
    2305             :         // Boundary Conditions from Simulation for Interior
    2306    46855557 :         Real64 hconvi(state.dataMstBal->HConvInFD(Surf));
    2307             : 
    2308    46855557 :         Real64 const Tia(state.dataZoneTempPredictorCorrector->zoneHeatBalance(surface.Zone).MAT);
    2309             : 
    2310             :         //++++++++++++++++++++++++++++++++++++++++++++++++++++++
    2311             :         //    Do all the nodes in the surface   Else will switch to SigmaR,SigmaC
    2312    46855557 :         Real64 TDT_i(TDT(i));
    2313    46855557 :         Real64 const QFac(NetLWRadToSurfFD + QRadSWInFD + QRadThermInFD + SurfQdotRadHVACInPerAreaFD);
    2314    46855557 :         if (surface.HeatTransferAlgorithm == DataSurfaces::HeatTransferModel::CondFD) {
    2315    46855557 :             int const MatLay(state.dataConstruction->Construct(ConstrNum).LayerPoint(Lay));
    2316    46855557 :             auto const *mat(dynamic_cast<const Material::MaterialChild *>(state.dataMaterial->Material(MatLay)));
    2317    46855557 :             auto const &matFD(state.dataHeatBalFiniteDiffMgr->MaterialFD(MatLay));
    2318    46855557 :             auto const &condActuator(state.dataHeatBalFiniteDiffMgr->SurfaceFD(Surf).condMaterialActuators(Lay));
    2319    46855557 :             auto const &specHeatActuator(state.dataHeatBalFiniteDiffMgr->SurfaceFD(Surf).specHeatMaterialActuators(Lay));
    2320             : 
    2321             :             // Calculate the Dry Heat Conduction Equation
    2322             : 
    2323    46855557 :             if (mat->ROnly || mat->group == Material::Group::Air) { // R Layer or Air Layer
    2324             :                 // Use algebraic equation for TDT based on R
    2325     1116311 :                 Real64 constexpr IterDampConst(
    2326             :                     5.0); // Damping constant for inside surface temperature iterations. Only used for massless (R-value only) Walls
    2327     1116311 :                 Real64 const Rlayer(mat->Resistance);
    2328     1116311 :                 if ((i == 1) && (surface.ExtBoundCond > 0)) { // this is for an adiabatic partition
    2329           0 :                     TDT_i = (TDT(i + 1) + (QFac + hconvi * Tia + TDreport(i) * IterDampConst) * Rlayer) / (1.0 + (hconvi + IterDampConst) * Rlayer);
    2330             :                 } else { // regular wall
    2331     1116311 :                     TDT_i = (TDT(i - 1) + (QFac + hconvi * Tia + TDreport(i) * IterDampConst) * Rlayer) / (1.0 + (hconvi + IterDampConst) * Rlayer);
    2332             :                 }
    2333     1116311 :                 state.dataHeatBalFiniteDiffMgr->SurfaceFD(Surf).CpDelXRhoS1(i) =
    2334             :                     0.0; // Save this for computing node flux values - rlayer has no capacitance
    2335     1116311 :                 state.dataHeatBalFiniteDiffMgr->SurfaceFD(Surf).CpDelXRhoS2(i) = 0.0; // Inside face  does not have an inner half node
    2336             : 
    2337     1116311 :             } else { //  Regular or PCM
    2338    45739246 :                 Real64 const TDT_m(TDT(i - 1));
    2339             : 
    2340             :                 // Set Thermal Conductivity. Can be constant, simple linear temp dep or multiple linear segment temp function dep.
    2341    45739246 :                 auto const &matFD_TempCond(matFD.TempCond);
    2342    45739246 :                 assert(matFD_TempCond.u2() >= 3);
    2343    45739246 :                 Real64 const lTC(matFD_TempCond.index(2, 1));
    2344             :                 Real64 kt;
    2345    45739246 :                 if (matFD_TempCond[lTC] + matFD_TempCond[lTC + 1] + matFD_TempCond[lTC + 2] >= 0.0) { // Multiple Linear Segment Function
    2346             :                     // Use average of surface and first node temp for determining k
    2347       75304 :                     kt = terpld(matFD_TempCond, (TDT_i + TDT_m) / 2.0, 1, 2); // 1: Temperature, 2: Thermal conductivity
    2348             :                 } else {
    2349    45663942 :                     kt = mat->Conductivity;      // 20C base conductivity
    2350    45663942 :                     Real64 const kt1(matFD.tk1); // linear coefficient (normally zero)
    2351    45663942 :                     if (kt1 != 0.0) kt = +kt1 * ((TDT_i + TDT_m) / 2.0 - 20.0);
    2352             :                 }
    2353             : 
    2354    45739246 :                 Real64 RhoS(mat->Density);
    2355    45739246 :                 Real64 const TD_i(TD(i));
    2356    45739246 :                 Real64 const Cpo(mat->SpecHeat);
    2357    45739246 :                 Real64 Cp(Cpo); // Will be changed if PCM
    2358    45739246 :                 auto const &matFD_TempEnth(matFD.TempEnth);
    2359    45739246 :                 assert(matFD_TempEnth.u2() >= 3);
    2360    45739246 :                 Real64 const lTE(matFD_TempEnth.index(2, 1));
    2361    45739246 :                 if (mat->phaseChange) {
    2362      219940 :                     adjustPropertiesForPhaseChange(state, i, Surf, mat, TD_i, TDT_i, Cp, RhoS, kt);
    2363    45519306 :                 } else if (matFD_TempEnth[lTE] + matFD_TempEnth[lTE + 1] + matFD_TempEnth[lTE + 2] >=
    2364             :                            0.0) {                                     // Phase change material: Use TempEnth data
    2365      437089 :                     EnthOld(i) = terpld(matFD_TempEnth, TD_i, 1, 2);  // 1: Temperature, 2: Enthalpy
    2366      437089 :                     EnthNew(i) = terpld(matFD_TempEnth, TDT_i, 1, 2); // 1: Temperature, 2: Enthalpy
    2367      437089 :                     if ((std::abs(EnthNew(i) - EnthOld(i)) > smalldiff) && (std::abs(TDT_i - TD_i) > smalldiff)) {
    2368      331648 :                         Cp = max(Cpo, (EnthNew(i) - EnthOld(i)) / (TDT_i - TD_i));
    2369             :                     }
    2370             :                 } // Phase change material check
    2371             : 
    2372             :                 // EMS Conductivity Override
    2373    45739246 :                 if (condActuator.isActuated) {
    2374           0 :                     kt = condActuator.actuatedValue;
    2375             :                 }
    2376             : 
    2377             :                 // EMS Specific Heat Override
    2378    45739246 :                 if (specHeatActuator.isActuated) {
    2379           0 :                     Cp = specHeatActuator.actuatedValue;
    2380             :                 }
    2381             : 
    2382             :                 // Update EMS internal variables
    2383    45739246 :                 state.dataHeatBalFiniteDiffMgr->SurfaceFD(Surf).condNodeReport(i) = kt;
    2384    45739246 :                 state.dataHeatBalFiniteDiffMgr->SurfaceFD(Surf).specHeatNodeReport(i) = Cp;
    2385             : 
    2386    45739246 :                 Real64 const DelX(state.dataHeatBalFiniteDiffMgr->ConstructFD(ConstrNum).DelX(Lay));
    2387    45739246 :                 Real64 const Delt_DelX(Delt * DelX);
    2388    45739246 :                 Real64 const Two_Delt_DelX(2.0 * Delt_DelX);
    2389    45739246 :                 Real64 const Delt_kt(Delt * kt);
    2390    45739246 :                 Real64 const Cp_DelX2_RhoS(Cp * pow_2(DelX) * RhoS);
    2391    45739246 :                 if ((surface.ExtBoundCond > 0) && (i == 1)) { // this is for an adiabatic or interzone partition
    2392           0 :                     if (state.dataHeatBalFiniteDiffMgr->CondFDSchemeType == CondFDScheme::CrankNicholsonSecondOrder) { // Adams-Moulton second order
    2393           0 :                         TDT_i = (Two_Delt_DelX * (QFac + hconvi * Tia) + (Cp_DelX2_RhoS - Delt_DelX * hconvi - Delt_kt) * TD_i +
    2394           0 :                                  Delt_kt * (TD(i + 1) + TDT(i + 1))) /
    2395           0 :                                 (Delt_DelX * hconvi + Delt_kt + Cp_DelX2_RhoS);
    2396           0 :                     } else if (state.dataHeatBalFiniteDiffMgr->CondFDSchemeType ==
    2397             :                                CondFDScheme::FullyImplicitFirstOrder) { // Adams-Moulton First order
    2398           0 :                         Real64 const Two_Delt_kt(2.0 * Delt_kt);
    2399           0 :                         TDT_i = (Two_Delt_DelX * (QFac + hconvi * Tia) + Cp_DelX2_RhoS * TD_i + Two_Delt_kt * TDT(i + 1)) /
    2400           0 :                                 (Two_Delt_DelX * hconvi + Two_Delt_kt + Cp_DelX2_RhoS);
    2401             :                     }
    2402           0 :                 } else { // for regular or interzone walls
    2403    45739246 :                     if (state.dataHeatBalFiniteDiffMgr->CondFDSchemeType == CondFDScheme::CrankNicholsonSecondOrder) {
    2404           0 :                         TDT_i = (Two_Delt_DelX * (QFac + hconvi * Tia) + (Cp_DelX2_RhoS - Delt_DelX * hconvi - Delt_kt) * TD_i +
    2405           0 :                                  Delt_kt * (TD(i - 1) + TDT_m)) /
    2406           0 :                                 (Delt_DelX * hconvi + Delt_kt + Cp_DelX2_RhoS);
    2407    45739246 :                     } else if (state.dataHeatBalFiniteDiffMgr->CondFDSchemeType == CondFDScheme::FullyImplicitFirstOrder) {
    2408    45739246 :                         Real64 const Two_Delt_kt(2.0 * Delt_kt);
    2409    45739246 :                         TDT_i = (Two_Delt_DelX * (QFac + hconvi * Tia) + Cp_DelX2_RhoS * TD_i + Two_Delt_kt * TDT_m) /
    2410    45739246 :                                 (Two_Delt_DelX * hconvi + Two_Delt_kt + Cp_DelX2_RhoS);
    2411             :                     }
    2412             :                 }
    2413    45739246 :                 state.dataHeatBalFiniteDiffMgr->SurfaceFD(Surf).CpDelXRhoS1(i) = (Cp * DelX * RhoS) / 2.0; // Save this for computing node flux values
    2414    45739246 :                 state.dataHeatBalFiniteDiffMgr->SurfaceFD(Surf).CpDelXRhoS2(i) = 0.0; // Inside face  does not have an inner half node
    2415             : 
    2416             :             } // Regular or R layer
    2417             : 
    2418    46855557 :             CheckFDNodeTempLimits(state, Surf, i, TDT_i);
    2419             : 
    2420    46855557 :             TDT(i) = TDT_i;
    2421             : 
    2422             :         } //  End of Regular node or SigmaR SigmaC option
    2423             : 
    2424    46855557 :         Real64 const QNetSurfInside(-(QFac + hconvi * (-TDT_i + Tia)));
    2425             :         //  Pass inside conduction Flux [W/m2] to DataHeatBalanceSurface array
    2426    46855557 :         state.dataHeatBalSurf->SurfOpaqInsFaceCondFlux(Surf) = QNetSurfInside;
    2427    46855557 :     }
    2428             : 
    2429             :     // todo - function not used
    2430           0 :     void CheckFDSurfaceTempLimits(EnergyPlusData &state,
    2431             :                                   int const SurfNum,            // surface number
    2432             :                                   Real64 const CheckTemperature // calculated temperature, not reset
    2433             :     )
    2434             :     {
    2435             : 
    2436             :         // SUBROUTINE INFORMATION:
    2437             :         //       AUTHOR         Linda Lawrie
    2438             :         //       DATE WRITTEN   August 2012
    2439             : 
    2440             :         // PURPOSE OF THIS SUBROUTINE:
    2441             :         // Provides a single entry point for checking surface temperature limits as well as
    2442             :         // setting up for recurring errors if too low or too high.
    2443             : 
    2444             :         // METHODOLOGY EMPLOYED:
    2445             :         // Use methodology similar to HBSurfaceManager
    2446             : 
    2447           0 :         int ZoneNum = state.dataSurface->Surface(SurfNum).Zone;
    2448             : 
    2449           0 :         if (state.dataGlobal->WarmupFlag) ++state.dataHeatBalFiniteDiffMgr->WarmupSurfTemp;
    2450           0 :         if (!state.dataGlobal->WarmupFlag || state.dataHeatBalFiniteDiffMgr->WarmupSurfTemp > 10 || state.dataGlobal->DisplayExtraWarnings) {
    2451           0 :             if (CheckTemperature < DataHeatBalSurface::MinSurfaceTempLimit) {
    2452           0 :                 if (state.dataSurface->SurfLowTempErrCount(SurfNum) == 0) {
    2453           0 :                     ShowSevereMessage(state,
    2454           0 :                                       format("Temperature (low) out of bounds [{:.2R}] for zone=\"{}\", for surface=\"{}\"",
    2455             :                                              CheckTemperature,
    2456           0 :                                              state.dataHeatBal->Zone(ZoneNum).Name,
    2457           0 :                                              state.dataSurface->Surface(SurfNum).Name));
    2458           0 :                     ShowContinueErrorTimeStamp(state, "");
    2459           0 :                     if (!state.dataHeatBal->Zone(ZoneNum).TempOutOfBoundsReported) {
    2460           0 :                         ShowContinueError(state, format("Zone=\"{}\", Diagnostic Details:", state.dataHeatBal->Zone(ZoneNum).Name));
    2461           0 :                         if (state.dataHeatBal->Zone(ZoneNum).FloorArea > 0.0) {
    2462           0 :                             ShowContinueError(
    2463             :                                 state,
    2464           0 :                                 format("...Internal Heat Gain [{:.3R}] W/m2",
    2465           0 :                                        state.dataHeatBal->Zone(ZoneNum).InternalHeatGains / state.dataHeatBal->Zone(ZoneNum).FloorArea));
    2466             :                         } else {
    2467           0 :                             ShowContinueError(
    2468           0 :                                 state, format("...Internal Heat Gain (no floor) [{:.3R}] W", state.dataHeatBal->Zone(ZoneNum).InternalHeatGains));
    2469             :                         }
    2470           0 :                         if (state.afn->simulation_control.type == AirflowNetwork::ControlType::NoMultizoneOrDistribution) {
    2471           0 :                             ShowContinueError(state,
    2472           0 :                                               format("...Infiltration/Ventilation [{:.3R}] m3/s", state.dataHeatBal->Zone(ZoneNum).NominalInfilVent));
    2473           0 :                             ShowContinueError(state, format("...Mixing/Cross Mixing [{:.3R}] m3/s", state.dataHeatBal->Zone(ZoneNum).NominalMixing));
    2474             :                         } else {
    2475           0 :                             ShowContinueError(state, "...Airflow Network Simulation: Nominal Infiltration/Ventilation/Mixing not available.");
    2476             :                         }
    2477           0 :                         if (state.dataHeatBal->Zone(ZoneNum).IsControlled) {
    2478           0 :                             ShowContinueError(state, "...Zone is part of HVAC controlled system.");
    2479             :                         } else {
    2480           0 :                             ShowContinueError(state, "...Zone is not part of HVAC controlled system.");
    2481             :                         }
    2482           0 :                         state.dataHeatBal->Zone(ZoneNum).TempOutOfBoundsReported = true;
    2483             :                     }
    2484           0 :                     ShowRecurringSevereErrorAtEnd(state,
    2485           0 :                                                   "Temperature (low) out of bounds for zone=" + state.dataHeatBal->Zone(ZoneNum).Name +
    2486           0 :                                                       " for surface=" + state.dataSurface->Surface(SurfNum).Name,
    2487           0 :                                                   state.dataSurface->SurfLowTempErrCount(SurfNum),
    2488             :                                                   CheckTemperature,
    2489             :                                                   CheckTemperature,
    2490             :                                                   _,
    2491             :                                                   "C",
    2492             :                                                   "C");
    2493             :                 } else {
    2494           0 :                     ShowRecurringSevereErrorAtEnd(state,
    2495           0 :                                                   "Temperature (low) out of bounds for zone=" + state.dataHeatBal->Zone(ZoneNum).Name +
    2496           0 :                                                       " for surface=" + state.dataSurface->Surface(SurfNum).Name,
    2497           0 :                                                   state.dataSurface->SurfLowTempErrCount(SurfNum),
    2498             :                                                   CheckTemperature,
    2499             :                                                   CheckTemperature,
    2500             :                                                   _,
    2501             :                                                   "C",
    2502             :                                                   "C");
    2503             :                 }
    2504             :             } else {
    2505           0 :                 if (state.dataSurface->SurfHighTempErrCount(SurfNum) == 0) {
    2506           0 :                     ShowSevereMessage(state,
    2507           0 :                                       format("Temperature (high) out of bounds ({:.2R}] for zone=\"{}\", for surface=\"{}\"",
    2508             :                                              CheckTemperature,
    2509           0 :                                              state.dataHeatBal->Zone(ZoneNum).Name,
    2510           0 :                                              state.dataSurface->Surface(SurfNum).Name));
    2511           0 :                     ShowContinueErrorTimeStamp(state, "");
    2512           0 :                     if (!state.dataHeatBal->Zone(ZoneNum).TempOutOfBoundsReported) {
    2513           0 :                         ShowContinueError(state, format("Zone=\"{}\", Diagnostic Details:", state.dataHeatBal->Zone(ZoneNum).Name));
    2514           0 :                         if (state.dataHeatBal->Zone(ZoneNum).FloorArea > 0.0) {
    2515           0 :                             ShowContinueError(
    2516             :                                 state,
    2517           0 :                                 format("...Internal Heat Gain [{:.3R}] W/m2",
    2518           0 :                                        state.dataHeatBal->Zone(ZoneNum).InternalHeatGains / state.dataHeatBal->Zone(ZoneNum).FloorArea));
    2519             :                         } else {
    2520           0 :                             ShowContinueError(
    2521           0 :                                 state, format("...Internal Heat Gain (no floor) [{:.3R}] W", state.dataHeatBal->Zone(ZoneNum).InternalHeatGains));
    2522             :                         }
    2523           0 :                         if (state.afn->simulation_control.type == AirflowNetwork::ControlType::NoMultizoneOrDistribution) {
    2524           0 :                             ShowContinueError(state,
    2525           0 :                                               format("...Infiltration/Ventilation [{:.3R}] m3/s", state.dataHeatBal->Zone(ZoneNum).NominalInfilVent));
    2526           0 :                             ShowContinueError(state, format("...Mixing/Cross Mixing [{:.3R}] m3/s", state.dataHeatBal->Zone(ZoneNum).NominalMixing));
    2527             :                         } else {
    2528           0 :                             ShowContinueError(state, "...Airflow Network Simulation: Nominal Infiltration/Ventilation/Mixing not available.");
    2529             :                         }
    2530           0 :                         if (state.dataHeatBal->Zone(ZoneNum).IsControlled) {
    2531           0 :                             ShowContinueError(state, "...Zone is part of HVAC controlled system.");
    2532             :                         } else {
    2533           0 :                             ShowContinueError(state, "...Zone is not part of HVAC controlled system.");
    2534             :                         }
    2535           0 :                         state.dataHeatBal->Zone(ZoneNum).TempOutOfBoundsReported = true;
    2536             :                     }
    2537           0 :                     ShowRecurringSevereErrorAtEnd(state,
    2538           0 :                                                   "Temperature (high) out of bounds for zone=" + state.dataHeatBal->Zone(ZoneNum).Name +
    2539           0 :                                                       " for surface=" + state.dataSurface->Surface(SurfNum).Name,
    2540           0 :                                                   state.dataSurface->SurfHighTempErrCount(SurfNum),
    2541             :                                                   CheckTemperature,
    2542             :                                                   CheckTemperature,
    2543             :                                                   _,
    2544             :                                                   "C",
    2545             :                                                   "C");
    2546             :                 } else {
    2547           0 :                     ShowRecurringSevereErrorAtEnd(state,
    2548           0 :                                                   "Temperature (high) out of bounds for zone=" + state.dataHeatBal->Zone(ZoneNum).Name +
    2549           0 :                                                       " for surface=" + state.dataSurface->Surface(SurfNum).Name,
    2550           0 :                                                   state.dataSurface->SurfHighTempErrCount(SurfNum),
    2551             :                                                   CheckTemperature,
    2552             :                                                   CheckTemperature,
    2553             :                                                   _,
    2554             :                                                   "C",
    2555             :                                                   "C");
    2556             :                 }
    2557             :             }
    2558             :         }
    2559           0 :     }
    2560             : 
    2561   354902687 :     void CheckFDNodeTempLimits(EnergyPlusData &state,
    2562             :                                int surfNum,     // surface number
    2563             :                                int nodeNum,     // node number
    2564             :                                Real64 &nodeTemp // calculated temperature, not reset
    2565             :     )
    2566             :     {
    2567   354902687 :         auto &surfaceFD(state.dataHeatBalFiniteDiffMgr->SurfaceFD(surfNum));
    2568   354902687 :         auto &surfName = state.dataSurface->Surface(surfNum).Name;
    2569   354902687 :         auto &minTempLimit = DataHeatBalSurface::MinSurfaceTempLimit;
    2570   354902687 :         auto &maxTempLimit = state.dataHeatBalSurf->MaxSurfaceTempLimit;
    2571   354902687 :         if (nodeTemp < minTempLimit) {
    2572           0 :             if (surfaceFD.indexNodeMinTempLimit == 0) {
    2573           0 :                 ShowSevereMessage(state,
    2574           0 :                                   format("Node temperature (low) out of bounds [{:.2R}] for surface={}, node={}", nodeTemp, surfName, nodeNum));
    2575           0 :                 ShowContinueErrorTimeStamp(state, "");
    2576           0 :                 ShowContinueError(state, format("Value has been reset to the lower limit value of {:.2R}.", minTempLimit));
    2577             :             }
    2578           0 :             ShowRecurringSevereErrorAtEnd(state,
    2579           0 :                                           "Node temperature (low) out of bounds for surface=" + surfName,
    2580           0 :                                           surfaceFD.indexNodeMinTempLimit,
    2581             :                                           nodeTemp,
    2582             :                                           nodeTemp,
    2583             :                                           _,
    2584             :                                           "C",
    2585             :                                           "C");
    2586           0 :             nodeTemp = minTempLimit;
    2587   354902687 :         } else if (nodeTemp > maxTempLimit) {
    2588           0 :             if (surfaceFD.indexNodeMaxTempLimit == 0) {
    2589           0 :                 ShowSevereMessage(state,
    2590           0 :                                   format("Node temperature (high) out of bounds [{:.2R}] for surface={}, node={}", nodeTemp, surfName, nodeNum));
    2591           0 :                 ShowContinueErrorTimeStamp(state, "");
    2592           0 :                 ShowContinueError(state, format("Value has been reset to the upper limit value of {:.2R}.", maxTempLimit));
    2593             :             }
    2594           0 :             ShowRecurringSevereErrorAtEnd(state,
    2595           0 :                                           "Node temperature (high) out of bounds for surface=" + surfName,
    2596           0 :                                           surfaceFD.indexNodeMaxTempLimit,
    2597             :                                           nodeTemp,
    2598             :                                           nodeTemp,
    2599             :                                           _,
    2600             :                                           "C",
    2601             :                                           "C");
    2602           0 :             nodeTemp = maxTempLimit;
    2603             :         }
    2604   354902687 :     }
    2605             : 
    2606    10239636 :     void CalcNodeHeatFlux(EnergyPlusData &state,
    2607             :                           int const Surf,    // surface number
    2608             :                           int const TotNodes // number of nodes in surface
    2609             :     )
    2610             :     {
    2611             : 
    2612             :         // SUBROUTINE INFORMATION:
    2613             :         //       AUTHOR         M.J. Witte
    2614             :         //       DATE WRITTEN   Sept-Nov 2015
    2615             :         // PURPOSE OF THIS SUBROUTINE:
    2616             :         // Calculate flux at each condFD node
    2617             : 
    2618    10239636 :         auto &surfaceFD(state.dataHeatBalFiniteDiffMgr->SurfaceFD(Surf));
    2619             : 
    2620             :         // SurfaceFD.QDreport( n ) is the flux at node n
    2621             :         // When this is called TDT( NodeNum ) is the new node temp and TDpriortimestep( NodeNum ) holds the previous node temp
    2622             :         // For the TDT and TDpriortimestep arrays, Node 1 is the outside face, and Node TotNodes+1 is the inside face
    2623             : 
    2624             :         // Last node is always the surface inside face.  Start calculations here because the outside face is not defined for all surfaces.
    2625             :         // Note that TotNodes is the number of nodes in the surface including the outside face node, but not the inside face node
    2626             :         // so the arrays are all allocated to Totodes+1
    2627             : 
    2628             :         // Heat flux at the inside face node (TotNodes+1)
    2629    10239636 :         surfaceFD.QDreport(TotNodes + 1) = state.dataHeatBalSurf->SurfOpaqInsFaceCondFlux(Surf);
    2630             : 
    2631             :         // Heat flux for remaining nodes.
    2632   105109088 :         for (int node = TotNodes; node >= 1; --node) {
    2633             :             // Start with inside face (above) and work outward, positive value is flowing towards the inside face
    2634             :             // CpDelXRhoS1 is outer half-node heat capacity, CpDelXRhoS2 is inner half node heat capacity
    2635             :             Real64 interNodeFlux; // heat flux at the plane between node and node+1 [W/m2]
    2636             :             Real64 sourceFlux;    // Internal source flux [W/m2]
    2637    94869452 :             if (surfaceFD.SourceNodeNum == node) {
    2638     1184616 :                 sourceFlux = surfaceFD.QSource;
    2639             :             } else {
    2640    93684836 :                 sourceFlux = 0.0;
    2641             :             }
    2642    94869452 :             interNodeFlux = surfaceFD.QDreport(node + 1) + surfaceFD.CpDelXRhoS1(node + 1) *
    2643    94869452 :                                                                (surfaceFD.TDT(node + 1) - surfaceFD.TDpriortimestep(node + 1)) /
    2644    94869452 :                                                                state.dataGlobal->TimeStepZoneSec;
    2645    94869452 :             surfaceFD.QDreport(node) =
    2646   189738904 :                 interNodeFlux - sourceFlux +
    2647    94869452 :                 surfaceFD.CpDelXRhoS2(node) * (surfaceFD.TDT(node) - surfaceFD.TDpriortimestep(node)) / state.dataGlobal->TimeStepZoneSec;
    2648             :         }
    2649    10239636 :         if (state.dataEnvrn->IsRain)
    2650           0 :             state.dataHeatBalSurf->SurfOpaqOutFaceCondFlux(Surf) = -surfaceFD.QDreport(1); // Update the outside flux if it is raining
    2651    10239636 :     }
    2652             : 
    2653      549850 :     void adjustPropertiesForPhaseChange(EnergyPlusData &state,
    2654             :                                         int finiteDifferenceLayerIndex,
    2655             :                                         int surfaceIndex,
    2656             :                                         const Material::MaterialBase *materialDefinitionBase,
    2657             :                                         Real64 temperaturePrevious,
    2658             :                                         Real64 temperatureUpdated,
    2659             :                                         Real64 &updatedSpecificHeat,
    2660             :                                         Real64 &updatedDensity,
    2661             :                                         Real64 &updatedThermalConductivity)
    2662             :     {
    2663      549850 :         auto const *materialDefinition = dynamic_cast<const Material::MaterialChild *>(materialDefinitionBase);
    2664     3299100 :         updatedSpecificHeat = materialDefinition->phaseChange->getCurrentSpecificHeat(
    2665             :             temperaturePrevious,
    2666             :             temperatureUpdated,
    2667      549850 :             state.dataHeatBalFiniteDiffMgr->SurfaceFD(surfaceIndex).PhaseChangeTemperatureReverse(finiteDifferenceLayerIndex),
    2668      549850 :             state.dataHeatBalFiniteDiffMgr->SurfaceFD(surfaceIndex).PhaseChangeStateOld(finiteDifferenceLayerIndex),
    2669      549850 :             state.dataHeatBalFiniteDiffMgr->SurfaceFD(surfaceIndex).PhaseChangeState(finiteDifferenceLayerIndex));
    2670      549850 :         updatedDensity = materialDefinition->phaseChange->getDensity(temperaturePrevious);
    2671      549850 :         updatedThermalConductivity = materialDefinition->phaseChange->getConductivity(temperatureUpdated);
    2672      549850 :     }
    2673             : 
    2674          64 :     bool findAnySurfacesUsingConstructionAndCondFD(EnergyPlusData const &state, int const constructionNum)
    2675             :     {
    2676         269 :         for (auto const &thisSurface : state.dataSurface->Surface) {
    2677         263 :             if (thisSurface.Construction == constructionNum) {
    2678          68 :                 if (thisSurface.HeatTransferAlgorithm == DataSurfaces::HeatTransferModel::CondFD) return true;
    2679             :             }
    2680         122 :         }
    2681           6 :         return false;
    2682             :     }
    2683             : 
    2684             : } // namespace HeatBalFiniteDiffManager
    2685             : 
    2686             : } // namespace EnergyPlus

Generated by: LCOV version 1.14