LCOV - code coverage report
Current view: top level - EnergyPlus - Construction.cc (source / functions) Hit Total Coverage
Test: lcov.output.filtered Lines: 747 847 88.2 %
Date: 2023-01-17 19:17:23 Functions: 13 13 100.0 %

          Line data    Source code
       1             : // EnergyPlus, Copyright (c) 1996-2023, 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             : // EnergyPlus Headers
      49             : #include <EnergyPlus/BITF.hh>
      50             : #include <EnergyPlus/Construction.hh>
      51             : #include <EnergyPlus/Data/EnergyPlusData.hh>
      52             : #include <EnergyPlus/DataConversions.hh>
      53             : #include <EnergyPlus/DataHeatBalance.hh>
      54             : #include <EnergyPlus/DisplayRoutines.hh>
      55             : #include <EnergyPlus/Material.hh>
      56             : #include <EnergyPlus/UtilityRoutines.hh>
      57             : 
      58             : namespace EnergyPlus::Construction {
      59             : 
      60        5817 : void ConstructionProps::calculateTransferFunction(EnergyPlusData &state, bool &ErrorsFound, bool &DoCTFErrorReport)
      61             : {
      62             : 
      63             :     // SUBROUTINE INFORMATION:
      64             :     //       AUTHOR         Russ Taylor
      65             :     //       DATE WRITTEN   June 1990
      66             :     //       MODIFIED       July 1994, LKL, cosmetic and improve execution time
      67             :     //                      Dec 1995, Apr 1996, RKS, cosmetic and clean-up changes, changes to allow proper
      68             :     //                       handling of resistive layers
      69             :     //                      June 2000, RKS, addition of QTFs (both 1- and 2-D solutions for constructions
      70             :     //                       with embedded/internal heat sources/sinks)
      71             :     //                      July 2010-August 2011, RKS, R-value only layer enhancement
      72             :     //       RE-ENGINEERED  June 1996, February 1997, August-October 1997, RKS; Nov 1999, LKL
      73             : 
      74             :     // PURPOSE OF THIS SUBROUTINE:
      75             :     // This subroutine serves as the main drive for the
      76             :     // calculation of Conduction Transfer Functions (CTFs)
      77             :     // using the state space method.
      78             : 
      79             :     // METHODOLOGY EMPLOYED:
      80             :     // The basic steps of this routine (which may be a little difficult
      81             :     // to decipher until another major revision is done) are:
      82             :     //   1. Determine if enough material info has been entered
      83             :     //   2. Determine whether construct is (a) all resistive,
      84             :     //      (b) the reverse of a previously calculated construct, or
      85             :     //      (c) neither (a) nor (b), i.e. a layer for which CTFs must
      86             :     //      be calculated.
      87             :     //   3. If the answer to 2 is (a), calculate the overall resistance
      88             :     //      and use this as the CTF (steady state conduction).
      89             :     //   4. If the answer to 2 is (b), transfer the CTFs for the reverse
      90             :     //      construction to the CTF arrays for this construct (reversing
      91             :     //      the inside and outside terms).
      92             :     //   5. If the answer to 2 is (c), calculate the CTFs using the state
      93             :     //      space method described below.
      94             :     // The state space method of calculating CTFs involves
      95             :     // applying a finite difference grid to a multilayered
      96             :     // building element and performing linear algebra on the
      97             :     // resulting system of equations (in matrix form).
      98             :     // CTFs must be calculated for non-reversed layers which
      99             :     // have an appreciable thermal mass.  A conversion from
     100             :     // SI units to English units is made due to concerns
     101             :     // about round off problems noted in earlier version of
     102             :     // this subroutine.
     103             : 
     104             :     // REFERENCES:
     105             :     // Seem, J.E.  "Modeling of Heat Transfer in Buildings",
     106             :     //  Department of Mechanical Engineering, University of
     107             :     //  Wisconsin-Madison, 1987.
     108             :     // Strand, R.K. "Testing Design Description for the CTF
     109             :     //  Calculation Code in BEST", BSO internal document,
     110             :     //  May/June 1996.
     111             :     // Strand, R.K. "Heat Source Transfer Functions and Their
     112             :     //  Applicatoin to Low Temperature Radiant Heating System",
     113             :     //  Ph.D. Dissertation, Department of Mechanical and
     114             :     //  Industrial Engineering, University of Illinois at
     115             :     //  Urbana-Champaign, 1995.
     116             : 
     117             :     // SUBROUTINE PARAMETER DEFINITIONS:
     118        5817 :     constexpr Real64 PhysPropLimit(1.0e-6); // Physical properties limit.
     119             :     // This is more or less the traditional value from BLAST.
     120             : 
     121        5817 :     constexpr Real64 RValueLowLimit(1.0e-3); // Physical properties limit for R-value only layers
     122             :     // This value was based on trial and error related to CR 7791 where a
     123             :     // user had entered a "no insulation" layer with an R-value of 1.0E-05.
     124             :     // Some trial and error established this as a potential value though
     125             :     // there is no guarantee that this is a good value.
     126             : 
     127        5817 :     constexpr int MinNodes(6); // Minimum number of state space nodes
     128             :     // per layer.  This value was chosen based on experience with IBLAST.
     129             : 
     130        5817 :     constexpr Real64 MaxAllowedCTFSumError(0.01); // Allow a 1 percent
     131             :     // difference between the CTF series summations.  If the difference is
     132             :     // greater than this, then the coefficients will not yield a valid steady
     133             :     // state solution.
     134             : 
     135        5817 :     constexpr Real64 MaxAllowedTimeStep(4.0); // Sets the maximum allowed time step
     136             :     // for CTF calculations to be 4 hours.  This is done in response to some
     137             :     // rare situations where odd or faulty input will cause the routine to
     138             :     // go off and get some huge time step (in excess of 20 hours).  This value
     139             :     // is a compromise that does not really solve any input problems.  One run
     140             :     // indicated that 2 meters of concrete will result in a time step of slightly
     141             :     // more than 3 hours.  So, 4 hours was arbitrarily picked as a ceiling for
     142             :     // time steps so that an error message can be produced to warn the user
     143             :     // that something isn't right.  Note that the 4 hour limit does not guarantee
     144             :     // that problems won't exist and it does not necessarily avoid any problems
     145             :     // that interpolated temperature histories might cause.
     146             : 
     147        5817 :     this->CTFCross = 0.0;
     148        5817 :     this->CTFFlux = 0.0;
     149        5817 :     this->CTFInside = 0.0;
     150        5817 :     this->CTFOutside = 0.0;
     151        5817 :     this->CTFSourceIn = 0.0;
     152        5817 :     this->CTFSourceOut = 0.0;
     153        5817 :     this->CTFTimeStep = 0.0;
     154        5817 :     this->CTFTSourceOut = 0.0;
     155        5817 :     this->CTFTSourceIn = 0.0;
     156        5817 :     this->CTFTSourceQ = 0.0;
     157        5817 :     this->CTFTUserOut = 0.0;
     158        5817 :     this->CTFTUserIn = 0.0;
     159        5817 :     this->CTFTUserSource = 0.0;
     160        5817 :     this->NumHistories = 0;
     161        5817 :     this->NumCTFTerms = 0;
     162        5817 :     this->UValue = 0.0;
     163             : 
     164        5817 :     if (!this->IsUsedCTF) {
     165        3590 :         return;
     166             :     }
     167             : 
     168        8044 :     Array1D<Real64> cp(Construction::MaxLayersInConstruct);              // Specific heat of a material layer
     169        8044 :     Array1D<Real64> dl(Construction::MaxLayersInConstruct);              // Thickness of a material layer
     170        8044 :     Array1D<Real64> dx(Construction::MaxLayersInConstruct);              // Distance between nodes in a particular material layer
     171        8044 :     Array1D<Real64> lr(Construction::MaxLayersInConstruct);              // R value of a material layer
     172        8044 :     Array1D_int Nodes(Construction::MaxLayersInConstruct);               // Array containing the number of nodes per layer
     173        8044 :     Array1D_bool ResLayer(Construction::MaxLayersInConstruct);           // Set true if the layer must be handled as a resistive
     174        8044 :     Array1D<Real64> rho(Construction::MaxLayersInConstruct);             // Density of a material layer
     175        8044 :     Array1D<Real64> rk(Construction::MaxLayersInConstruct);              // Thermal conductivity of a material layer
     176        8044 :     Array1D_int AdjacentResLayerNum(Construction::MaxLayersInConstruct); // Layers that are adjacent to each other which are resistive
     177             : 
     178             :     Real64 amatx;      // Intermediate calculation variable
     179             :     Real64 amatxx;     // Intermediate calculation variable
     180             :     Real64 amaty;      // Intermediate calculation variable
     181             :     Real64 BiggestSum; // Largest CTF series summation (maximum of SumXi, SumYi, and SumZi)
     182             :     Real64 cap;        // Thermal capacitance of a node (intermediate calculation)
     183             :     Real64 capavg;     // Thermal capacitance of a node (average value for a node at an interface)
     184             :     Real64 cnd;        // Total thermal conductance (1/Rtot) of the bldg element
     185             :     Real64 dtn;        // Intermediate calculation of the time step
     186             :     Real64 dxn;        // Intermediate calculation of nodal spacing
     187             :     Real64 dxtmp;      // Intermediate calculation variable ( = 1/dx/cap)
     188             :     Real64 dyn;        // Nodal spacing in the direction perpendicular to the main direction
     189             :     bool CTFConvrg;    // Set after CTFs are calculated, based on whether there are too many CTF terms
     190             :     Real64 SumXi;      // Summation of all of the Xi terms (inside CTFs) for a construction
     191             :     Real64 SumYi;      // Summation of all of the Xi terms (cross CTFs) for a construction
     192             :     Real64 SumZi;      // Summation of all of the Xi terms (outside CTFs) for a construction
     193             : 
     194             :     int ipts1; // Intermediate calculation for number of nodes per layer
     195             : 
     196             :     Real64 DeltaTimestep; // zone timestep in seconds, for local check of properties
     197             : 
     198        4022 :     this->CTFTimeStep = state.dataGlobal->TimeStepZone;
     199        4022 :     Real64 rs = 0.0;
     200        4022 :     int LayersInConstruct = 0;
     201        4022 :     int NumResLayers = 0;
     202        4022 :     ResLayer = false;
     203        4022 :     AdjacentResLayerNum = 0; // Zero this out for each construct
     204             : 
     205       13957 :     for (int Layer = 1; Layer <= this->TotLayers; ++Layer) { // Begin layer loop ...
     206             : 
     207             :         // Loop through all of the layers in the current construct. The purpose
     208             :         // of this loop is to define the thermal properties necessary to
     209             :         // calculate the CTFs.
     210             : 
     211        9935 :         int CurrentLayer = this->LayerPoint(Layer);
     212             : 
     213        9935 :         ++LayersInConstruct;
     214             : 
     215             :         // Obtain thermal properties from the Material derived type
     216             : 
     217        9935 :         dl(Layer) = state.dataMaterial->Material(CurrentLayer).Thickness;
     218        9935 :         rk(Layer) = state.dataMaterial->Material(CurrentLayer).Conductivity;
     219        9935 :         rho(Layer) = state.dataMaterial->Material(CurrentLayer).Density;
     220        9935 :         cp(Layer) = state.dataMaterial->Material(CurrentLayer).SpecHeat; // Must convert
     221             :         // from kJ/kg-K to J/kg-k due to rk units
     222             : 
     223        9935 :         if (this->SourceSinkPresent && !state.dataMaterial->Material(CurrentLayer).WarnedForHighDiffusivity) {
     224             :             // check for materials that are too conductive or thin
     225         182 :             if ((rho(Layer) * cp(Layer)) > 0.0) {
     226         178 :                 Real64 Alpha = rk(Layer) / (rho(Layer) * cp(Layer));
     227         178 :                 if (Alpha > DataHeatBalance::HighDiffusivityThreshold) {
     228           0 :                     DeltaTimestep = state.dataGlobal->TimeStepZoneSec;
     229           0 :                     Real64 const ThicknessThreshold = std::sqrt(Alpha * DeltaTimestep * 3.0);
     230           0 :                     if (state.dataMaterial->Material(CurrentLayer).Thickness < ThicknessThreshold) {
     231           0 :                         ShowSevereError(state,
     232             :                                         "InitConductionTransferFunctions: Found Material that is too thin and/or too highly conductive, "
     233           0 :                                         "material name = " +
     234           0 :                                             state.dataMaterial->Material(CurrentLayer).Name);
     235           0 :                         ShowContinueError(state,
     236           0 :                                           format("High conductivity Material layers are not well supported for internal source constructions, "
     237             :                                                  "material conductivity = {:.3R} [W/m-K]",
     238           0 :                                                  state.dataMaterial->Material(CurrentLayer).Conductivity));
     239           0 :                         ShowContinueError(state, format("Material thermal diffusivity = {:.3R} [m2/s]", Alpha));
     240           0 :                         ShowContinueError(state,
     241           0 :                                           format("Material with this thermal diffusivity should have thickness > {:.5R} [m]", ThicknessThreshold));
     242           0 :                         if (state.dataMaterial->Material(CurrentLayer).Thickness < DataHeatBalance::ThinMaterialLayerThreshold) {
     243           0 :                             ShowContinueError(state,
     244           0 :                                               format("Material may be too thin to be modeled well, thickness = {:.5R} [m]",
     245           0 :                                                      state.dataMaterial->Material(CurrentLayer).Thickness));
     246           0 :                             ShowContinueError(state,
     247           0 :                                               format("Material with this thermal diffusivity should have thickness > {:.5R} [m]",
     248           0 :                                                      DataHeatBalance::ThinMaterialLayerThreshold));
     249             :                         }
     250           0 :                         state.dataMaterial->Material(CurrentLayer).WarnedForHighDiffusivity = true;
     251             :                     }
     252             :                 }
     253             :             }
     254             :         }
     255        9935 :         if (state.dataMaterial->Material(CurrentLayer).Thickness > 3.0) {
     256           0 :             ShowSevereError(state, "InitConductionTransferFunctions: Material too thick for CTF calculation");
     257           0 :             ShowContinueError(state, "material name = " + state.dataMaterial->Material(CurrentLayer).Name);
     258           0 :             ErrorsFound = true;
     259             :         }
     260             : 
     261        9935 :         if (rk(Layer) <= PhysPropLimit) { // Thermal conductivity too small,
     262             :             // thus this must be handled as a resistive layer
     263        1439 :             ResLayer(Layer) = true;
     264             :         } else {
     265        8496 :             lr(Layer) = dl(Layer) / rk(Layer);
     266        8496 :             ResLayer(Layer) = (dl(Layer) * std::sqrt(rho(Layer) * cp(Layer) / rk(Layer))) < PhysPropLimit;
     267             :         }
     268             : 
     269             :         // If not a resistive layer, nothing further is required
     270             :         // for this layer.
     271             : 
     272        9935 :         if (ResLayer(Layer)) {                                                 // Resistive layer-check for R-value, etc.
     273        1439 :             ++NumResLayers;                                                    // Increment number of resistive layers
     274        1439 :             lr(Layer) = state.dataMaterial->Material(CurrentLayer).Resistance; // User defined thermal resistivity
     275        1439 :             if (lr(Layer) < RValueLowLimit) {                                  // User didn't define enough
     276             :                 // parameters to calculate CTFs for a building element
     277             :                 // containing this layer.
     278             : 
     279           0 :                 ShowSevereError(state,
     280           0 :                                 "InitConductionTransferFunctions: Material=" + state.dataMaterial->Material(CurrentLayer).Name +
     281             :                                     "R Value below lowest allowed value");
     282           0 :                 ShowContinueError(state, format("Lowest allowed value=[{:.3R}], Material R Value=[{:.3R}].", RValueLowLimit, lr(Layer)));
     283           0 :                 ErrorsFound = true;
     284             : 
     285             :             } else { // A valid user defined R-value is available.
     286             :                 // If this is either the first or last layer in the construction,
     287             :                 // then assign other properties based on air at 1 atm, 300K.
     288             :                 // Reference for air properties:  Incropera and DeWitt,
     289             :                 // Introduction to Heat Transfer, Appendix A, Table A.4,
     290             :                 // John Wiley & Sons, New York, 1985.
     291             :                 // If this is not the first or last layer in the construction,
     292             :                 // then use the "exact" approach to model a massless layer
     293             :                 // based on the node equations for the state space method.
     294             : 
     295        1439 :                 if ((Layer == 1) || (Layer == this->TotLayers) || (!state.dataMaterial->Material(this->LayerPoint(Layer)).ROnly)) {
     296         940 :                     cp(Layer) = 1.007;
     297         940 :                     rho(Layer) = 1.1614;
     298         940 :                     rk(Layer) = 0.0263;
     299         940 :                     dl(Layer) = rk(Layer) * lr(Layer);
     300             :                 } else {
     301         499 :                     cp(Layer) = 0.0;
     302         499 :                     rho(Layer) = 0.0;
     303         499 :                     rk(Layer) = 1.0;
     304         499 :                     dl(Layer) = lr(Layer);
     305             :                 }
     306             :             }
     307             :         } // ... end of resistive layer determination IF-THEN block.
     308             :     }     // ... end of layer loop.
     309             : 
     310             :     // If errors have been found, just return
     311             : 
     312        4022 :     if (ErrorsFound) return;
     313             : 
     314             :     // Combine any adjacent resistive-only (no mass) layers together
     315             :     // to avoid a divide by zero error in the CTF calculations below.
     316             :     // Since the inner and outer layers cannot be resistive layers
     317             :     // (inner and outer layer still converted to equivalent air layer)
     318             :     // there can only be resistive layers adjacent to one another if
     319             :     // there are more than three total layers and more than one
     320             :     // resistive layer.
     321        4022 :     if ((LayersInConstruct > 3) && (NumResLayers > 1)) {
     322          81 :         int NumAdjResLayers = 0;
     323         251 :         for (int Layer = 2; Layer <= LayersInConstruct - 2; ++Layer) {
     324         170 :             if ((ResLayer(Layer)) && (ResLayer(Layer + 1))) {
     325          27 :                 ++NumAdjResLayers;
     326             :                 // There is method to the next assignment statement.  As the layers get shifted, the layer
     327             :                 // numbers will also shift.  Thus, we have to also shift which layer we are dealing with.
     328          27 :                 AdjacentResLayerNum(NumAdjResLayers) = Layer + 1 - NumAdjResLayers;
     329             :             }
     330             :         }
     331         108 :         for (int AdjLayer = 1; AdjLayer <= NumAdjResLayers; ++AdjLayer) {
     332          27 :             int Layer = AdjacentResLayerNum(AdjLayer);
     333             :             // Double check to make sure we are in the right place...
     334          27 :             if ((ResLayer(Layer)) && (ResLayer(Layer + 1))) {
     335             :                 // Shift layers forward after combining two adjacent layers.  Then
     336             :                 // restart the do loop.
     337          27 :                 cp(Layer) = 0.0;
     338          27 :                 rho(Layer) = 0.0;
     339          27 :                 rk(Layer) = 1.0;
     340          27 :                 lr(Layer) += lr(Layer + 1);
     341          27 :                 dl(Layer) = lr(Layer);
     342          27 :                 --NumResLayers; // Combining layers so decrease number of resistive layers
     343          68 :                 for (int Layer1 = Layer + 1; Layer1 <= LayersInConstruct - 1; ++Layer1) {
     344          41 :                     lr(Layer1) = lr(Layer1 + 1);
     345          41 :                     dl(Layer1) = dl(Layer1 + 1);
     346          41 :                     rk(Layer1) = rk(Layer1 + 1);
     347          41 :                     rho(Layer1) = rho(Layer1 + 1);
     348          41 :                     cp(Layer1) = cp(Layer1 + 1);
     349          41 :                     ResLayer(Layer1) = ResLayer(Layer1 + 1);
     350             :                 }
     351             :                 // Then zero out the layer that got shifted forward
     352          27 :                 cp(LayersInConstruct) = 0.0;
     353          27 :                 rho(LayersInConstruct) = 0.0;
     354          27 :                 rk(LayersInConstruct) = 0.0;
     355          27 :                 lr(LayersInConstruct) = 0.0;
     356          27 :                 dl(LayersInConstruct) = 0.0;
     357             :                 // Now reduce the number of layers in construct since merger is complete
     358          27 :                 --LayersInConstruct;
     359             :                 // Also adjust layers with source/sinks if two layers are merged
     360          27 :                 if (this->SourceSinkPresent) {
     361           0 :                     --this->SourceAfterLayer;
     362           0 :                     --this->TempAfterLayer;
     363             :                 }
     364             :             } else { // These are not adjacent layers and there is a logic flaw here (should not happen)
     365           0 :                 ShowFatalError(state, "Combining resistance layers failed for " + this->Name);
     366           0 :                 ShowContinueError(state, "This should never happen.  Contact EnergyPlus Support for further assistance.");
     367             :             }
     368             :         }
     369             :     }
     370             : 
     371             :     // Convert SI units to English.  In theory, conversion to English
     372             :     // units is not necessary; however, Russ Taylor noted that some
     373             :     // numerical problems when SI units were used and decided to continue
     374             :     // calculating CTFs in English units.
     375             : 
     376       13930 :     for (int Layer = 1; Layer <= LayersInConstruct; ++Layer) { // Begin units conversion loop ...
     377             : 
     378        9908 :         lr(Layer) *= DataConversions::CFU;
     379        9908 :         dl(Layer) /= DataConversions::CFL;
     380        9908 :         rk(Layer) /= DataConversions::CFK;
     381        9908 :         rho(Layer) /= DataConversions::CFD;
     382        9908 :         cp(Layer) /= (DataConversions::CFC * 1000.0);
     383             : 
     384             :     } // ... end of layer loop for units conversion.
     385             : 
     386        4022 :     if (this->SolutionDimensions == 1) {
     387        4020 :         dyn = 0.0;
     388             :     } else {
     389           2 :         dyn = (this->ThicknessPerpend / DataConversions::CFL) / double(NumOfPerpendNodes - 1);
     390             :     }
     391             : 
     392             :     // Compute total construct conductivity and resistivity.
     393             : 
     394       13930 :     for (int Layer = 1; Layer <= LayersInConstruct; ++Layer) {
     395        9908 :         rs += lr(Layer); // Resistances in series sum algebraically
     396             :     }
     397             : 
     398        4022 :     cnd = 1.0 / rs; // Conductivity is the inverse of resistivity
     399             : 
     400        4022 :     bool RevConst = false;
     401             : 
     402        4022 :     if (LayersInConstruct > NumResLayers) {
     403             : 
     404             :         // One or more are not simple resistive layers so CTFs will have to be
     405             :         // calculated unless this is a reverse of a previously defined
     406             :         // this->
     407             : 
     408             :         // Check for reversed construction of interzone surfaces by checking
     409             :         // previous constructions for same number of layers as first indicator.
     410             : 
     411             :         // previously this loop would go from 1..currentConstructionIndex-1
     412             :         // instead of that, we'll loop through the list and stop when we get to the current construction
     413             :         // should be the same behavior, we're just checking it by address
     414       23342 :         for (auto &otherConstruction : state.dataConstruction->Construct) {
     415       23342 :             if (&otherConstruction == this) break;
     416             : 
     417             :             // If a source or sink is present in this construction, do not allow any
     418             :             // checks for reversed constructions, i.e., always force EnergyPlus to
     419             :             // calculate CTF/QTFs.  So, don't even check for reversed constructions.
     420       19847 :             if (this->SourceSinkPresent) break; // Constr DO loop
     421             : 
     422       19810 :             if (this->TotLayers == otherConstruction.TotLayers) { // Same number of layers--now | check for reversed construct.
     423             : 
     424        6674 :                 RevConst = true;
     425             : 
     426        7517 :                 for (int Layer = 1; Layer <= this->TotLayers; ++Layer) { // Begin layers loop ...
     427             : 
     428             :                     // RevConst is set to FALSE anytime a mismatch in materials is found.
     429             :                     // This will exit this DO immediately and go on to the next construct
     430             :                     // (if any remain).
     431             : 
     432        7355 :                     int OppositeLayer = this->TotLayers - Layer + 1;
     433             : 
     434        7355 :                     if (this->LayerPoint(Layer) != otherConstruction.LayerPoint(OppositeLayer)) {
     435             : 
     436        6512 :                         RevConst = false;
     437        6512 :                         break; // Layer DO loop
     438             :                     }
     439             : 
     440             :                 } // ... end of layers loop.
     441             : 
     442             :                 // If the reverse construction isn't used by any surfaces then the CTFs
     443             :                 // still need to be defined.
     444        6674 :                 if (RevConst && !otherConstruction.IsUsedCTF) {
     445           2 :                     RevConst = false;
     446             :                 }
     447             : 
     448        6674 :                 if (RevConst) { // Curent construction is a reverse of
     449             :                     // construction Constr.  Thus, CTFs do not need to be re-
     450             :                     // calculated.  Copy CTF info for construction Constr to
     451             :                     // construction ConstrNum.
     452             : 
     453         160 :                     this->CTFTimeStep = otherConstruction.CTFTimeStep;
     454         160 :                     this->NumHistories = otherConstruction.NumHistories;
     455         160 :                     this->NumCTFTerms = otherConstruction.NumCTFTerms;
     456             : 
     457             :                     // Transfer the temperature and flux history terms to CTF arrays.
     458             :                     // Loop through the number of CTF history terms ...
     459        1366 :                     for (int HistTerm = 0; HistTerm <= this->NumCTFTerms; ++HistTerm) {
     460             : 
     461        1206 :                         this->CTFInside(HistTerm) = otherConstruction.CTFOutside(HistTerm);
     462        1206 :                         this->CTFCross(HistTerm) = otherConstruction.CTFCross(HistTerm);
     463        1206 :                         this->CTFOutside(HistTerm) = otherConstruction.CTFInside(HistTerm);
     464        1206 :                         if (HistTerm != 0) this->CTFFlux(HistTerm) = otherConstruction.CTFFlux(HistTerm);
     465             : 
     466             :                     } // ... end of CTF history terms loop.
     467             : 
     468         160 :                     break; // Constr DO loop
     469             : 
     470             :                 } // ... end of reversed construction found block
     471             : 
     472             :             } // ... end of reversed construct (same number of layers) block.
     473             : 
     474             :         } // ... end of construct loop (check reversed--Constr)
     475             : 
     476        3692 :         if (!RevConst) { // Calculate CTFs (non-reversed constr)
     477             : 
     478             :             // Estimate number of nodes each layer of the construct will require
     479             :             // and calculate the nodal spacing from that
     480             : 
     481       12768 :             for (int Layer = 1; Layer <= LayersInConstruct; ++Layer) { // Begin loop thru layers ...
     482             : 
     483             :                 // The calculation of dxn used here is based on a standard stability
     484             :                 // criteria for explicit finite difference solutions.  This criteria
     485             :                 // was chosen not because it is viewed to be correct, but rather for
     486             :                 // lack of any better criteria at this time.  The use of a Fourier
     487             :                 // number based criteria such as this is probably physically correct,
     488             :                 // though the coefficient (2.0) may not be.
     489             : 
     490             :                 // If this is a "resistive" layer, only need a single node
     491        9236 :                 if ((ResLayer(Layer)) && (Layer > 1) && (Layer < LayersInConstruct)) {
     492         455 :                     Nodes(Layer) = 1;
     493         455 :                     dx(Layer) = dl(Layer);
     494             :                 } else {
     495        8781 :                     dxn = std::sqrt(2.0 * (rk(Layer) / rho(Layer) / cp(Layer)) * this->CTFTimeStep);
     496             : 
     497        8781 :                     ipts1 = int(dl(Layer) / dxn); // number of nodes=thickness/spacing
     498             : 
     499             :                     // Limit the upper and lower bounds of the number of
     500             :                     // nodes to MaxCTFTerms and MinNodes respectively.
     501             : 
     502        8781 :                     if (ipts1 > Construction::MaxCTFTerms) { // Too many nodes
     503          21 :                         Nodes(Layer) = Construction::MaxCTFTerms;
     504        8760 :                     } else if (ipts1 < MinNodes) { // Too few nodes
     505        7969 :                         Nodes(Layer) = MinNodes;
     506             :                     } else { // Calculated number of nodes ok
     507         791 :                         Nodes(Layer) = ipts1;
     508             :                     }
     509             : 
     510        8781 :                     if (this->SolutionDimensions > 1) {
     511          10 :                         if (ipts1 > Construction::MaxCTFTerms / 2) ipts1 = Construction::MaxCTFTerms / 2;
     512             :                     }
     513             : 
     514        8781 :                     dx(Layer) = dl(Layer) / double(Nodes(Layer)); // calc node spacing
     515             :                 }
     516             : 
     517             :             } // . .. end of layers in construction loop (calculating #nodes per layer)
     518             : 
     519             :             // Determine the total number of nodes (rcmax)
     520             : 
     521        3532 :             this->rcmax = 0;
     522       12768 :             for (int Layer = 1; Layer <= LayersInConstruct; ++Layer) {
     523        9236 :                 this->rcmax += Nodes(Layer);
     524             :             }
     525             : 
     526             :             // Nodes are placed throughout layers and at the interface between
     527             :             // layers.  As a result, the end layers share a node with the adjacent
     528             :             // layer-leaving one less node total for all layers.
     529             : 
     530        3532 :             --this->rcmax;
     531        3532 :             if (this->SolutionDimensions > 1) this->rcmax *= NumOfPerpendNodes;
     532             : 
     533             :             // This section no longer needed as rcmax/number of total nodes is allowed to float.
     534             :             // If reinstated, this node reduction section would have to be modified to account for
     535             :             // the possibility that a 2-D solution is potentially being performed.
     536             :             // Check to see if the maximum number of nodes for the construct has
     537             :             // been exceeded.  Reduce the nodes per layer if necessary, but only
     538             :             // if the number of nodes in a particular layer is greater than the
     539             :             // minimum node limit.
     540             : 
     541             :             //        DO WHILE (rcmax > MaxTotNodes)     ! Begin total node reduction loop ...
     542             : 
     543             :             //          rcmax = 0
     544             : 
     545             :             //          DO Layer = 1, LayersInConstruct   ! Begin layer node reduction ...
     546             : 
     547             :             //          ! If more nodes than the minimum limit for a layer, reduce the
     548             :             //          ! number of nodes.
     549             : 
     550             :             //            IF (Nodes(Layer) > MinNodes) THEN
     551             :             //              Nodes(Layer) = Nodes(Layer)-1
     552             :             //              dx(Layer) = dl(Layer)/DBLE(Nodes(Layer)) ! Recalc node spacing
     553             :             //            END IF
     554             : 
     555             :             //            rcmax = rcmax + Nodes(Layer) ! Recalculate total number of nodes
     556             : 
     557             :             //          END DO        ! ... end of layer loop for node reduction.
     558             : 
     559             :             //          rcmax = rcmax-1 ! See note above on counting rcmax
     560             : 
     561             :             //        END DO      ! ... end of total node reduction loop.
     562             : 
     563             :             // For constructions that have sources or sinks present, determine which
     564             :             // node the source/sink is applied at and also where the temperature
     565             :             // calculation has been requested.
     566        3532 :             this->setNodeSourceAndUserTemp(Nodes);
     567             : 
     568             :             // "Adjust time step to ensure stability."  If the time step is too
     569             :             // small, it will result in too many history terms which can lead to
     570             :             // solution instability.  The method used here to determine whether or
     571             :             // not the time step will produce a stable solution is based on a pure
     572             :             // Fourier number calculation (Fo = 1) and has not proven to be
     573             :             // completely effective.  If too many history terms are calculated,
     574             :             // the time step is adjusted and the CTFs end up being recalculated
     575             :             // (see later code in this routine).
     576             : 
     577        3532 :             dtn = 0.0;
     578        3532 :             this->CTFTimeStep = 0.0;
     579       12768 :             for (int Layer = 1; Layer <= LayersInConstruct; ++Layer) {
     580        9236 :                 if (Nodes(Layer) >= Construction::MaxCTFTerms) {
     581          24 :                     if (this->SolutionDimensions == 1) {
     582          24 :                         dtn = rho(Layer) * cp(Layer) * pow_2(dx(Layer)) / rk(Layer);
     583             :                     } else { // 2-D solution requested-->this changes length parameter in Fourier number calculation
     584           0 :                         dtn = rho(Layer) * cp(Layer) * (pow_2(dx(Layer)) + pow_2(dyn)) / rk(Layer);
     585             :                     }
     586          24 :                     if (dtn > this->CTFTimeStep) this->CTFTimeStep = dtn;
     587             :                 }
     588             :             }
     589             : 
     590             :             // If the user defined time step is significantly different than the
     591             :             // calculated time step for this construct, then CTFTimeStep must be
     592             :             // revised.
     593             : 
     594        3532 :             if (std::abs((state.dataGlobal->TimeStepZone - this->CTFTimeStep) / state.dataGlobal->TimeStepZone) > 0.1) {
     595             : 
     596        3532 :                 if (this->CTFTimeStep > state.dataGlobal->TimeStepZone) {
     597             : 
     598             :                     // CTFTimeStep larger than TimeStepZone:  Make sure TimeStepZone
     599             :                     // divides evenly into CTFTimeStep
     600          22 :                     this->NumHistories = int((this->CTFTimeStep / state.dataGlobal->TimeStepZone) + 0.5);
     601          22 :                     this->CTFTimeStep = state.dataGlobal->TimeStepZone * double(this->NumHistories);
     602             : 
     603             :                 } else {
     604             : 
     605             :                     // CTFTimeStep smaller than TimeStepZone:  Set to TimeStepZone
     606        3510 :                     this->CTFTimeStep = state.dataGlobal->TimeStepZone;
     607        3510 :                     this->NumHistories = 1;
     608             :                 }
     609             :             }
     610             : 
     611             :             // Calculate the CTFs using the state space method
     612             :             // outlined in Seem's dissertation.  The main matrices
     613             :             // AMat, BMat, CMat, and DMat must be derived from
     614             :             // applying a finite difference network to the layers of
     615             :             // each bldg element.
     616             : 
     617             :             // This section must continue looping until the CTFs
     618             :             // calculated here will produce a stable solution (less
     619             :             // history terms than MaxCTFTerms).
     620             : 
     621             :             // This first subsection calculates the elements of AMat
     622             :             // which characterizes the heat transfer inside the
     623             :             // building element.
     624             : 
     625        3532 :             CTFConvrg = false; // Initialize loop control logical
     626             : 
     627        3532 :             this->AExp.allocate(this->rcmax, this->rcmax);
     628        3532 :             this->AExp = 0.0;
     629        3532 :             this->AMat.allocate(this->rcmax, this->rcmax);
     630        3532 :             this->AMat = 0.0;
     631        3532 :             this->AInv.allocate(this->rcmax, this->rcmax);
     632        3532 :             this->AInv = 0.0;
     633        3532 :             this->IdenMatrix.allocate(this->rcmax, this->rcmax);
     634        3532 :             this->IdenMatrix = 0.0;
     635       54886 :             for (int ir = 1; ir <= this->rcmax; ++ir) {
     636       51354 :                 this->IdenMatrix(ir, ir) = 1.0;
     637             :             }
     638        3532 :             this->e.dimension(this->rcmax, 0.0);
     639        3532 :             this->Gamma1.allocate(3, this->rcmax);
     640        3532 :             this->Gamma1 = 0.0;
     641        3532 :             this->Gamma2.allocate(3, this->rcmax);
     642        3532 :             this->Gamma2 = 0.0;
     643        3532 :             this->s.allocate(3, 4, this->rcmax);
     644        3532 :             this->s = 0.0;
     645             : 
     646       10790 :             while (!CTFConvrg) { // Begin CTF calculation loop ...
     647             : 
     648        3629 :                 this->BMat(3) = 0.0;
     649             : 
     650        3629 :                 if (this->SolutionDimensions == 1) {
     651             : 
     652             :                     // Set up intermediate calculations for the first layer.
     653        3625 :                     cap = rho(1) * cp(1) * dx(1);
     654        3625 :                     cap *= 1.5; // For the first node, account for the fact that the
     655             :                     // half-node at the surface results in a "loss" of some
     656             :                     // thermal mass.  Therefore, for simplicity, include it
     657             :                     // at this node.  Same thing done at the last node...
     658        3625 :                     dxtmp = 1.0 / dx(1) / cap;
     659             : 
     660        3625 :                     this->AMat(1, 1) = -2.0 * rk(1) * dxtmp; // Assign the matrix values for the
     661        3625 :                     this->AMat(2, 1) = rk(1) * dxtmp;        // first node.
     662        3625 :                     this->BMat(1) = rk(1) * dxtmp;           // Assign non-zero value of BMat.
     663             : 
     664        3625 :                     int Layer = 1; // Initialize the "layer" counter
     665             : 
     666        3625 :                     int NodeInLayer = 2; // Initialize the node (in a layer) counter (already
     667             :                     // on the second node for the first layer
     668             : 
     669       49744 :                     for (int Node = 2; Node <= this->rcmax - 1; ++Node) { // Begin nodes loop (includes all nodes except the
     670             :                         // first/last which have special equations) ...
     671             : 
     672       46119 :                         if ((NodeInLayer == Nodes(Layer)) && (LayersInConstruct != 1)) { // For a node at
     673             :                             // the interface between two adjacent layers, the
     674             :                             // capacitance of the node must be calculated from the 2
     675             :                             // halves which may be made up of 2 different materials.
     676             : 
     677        5895 :                             cap = (rho(Layer) * cp(Layer) * dx(Layer) + rho(Layer + 1) * cp(Layer + 1) * dx(Layer + 1)) * 0.5;
     678             : 
     679        5895 :                             this->AMat(Node - 1, Node) = rk(Layer) / dx(Layer) / cap;                                      // Assign matrix
     680        5895 :                             this->AMat(Node, Node) = -1.0 * (rk(Layer) / dx(Layer) + rk(Layer + 1) / dx(Layer + 1)) / cap; // values for | the current
     681        5895 :                             this->AMat(Node + 1, Node) = rk(Layer + 1) / dx(Layer + 1) / cap;                              // node.
     682             : 
     683        5895 :                             NodeInLayer = 0; // At an interface, reset nodes in layer counter
     684        5895 :                             ++Layer;         // Also increment the layer counter
     685             : 
     686             :                         } else { // Standard node within any layer
     687             : 
     688       40224 :                             cap = rho(Layer) * cp(Layer) * dx(Layer);          // Intermediate
     689       40224 :                             dxtmp = 1.0 / dx(Layer) / cap;                     // calculations.
     690       40224 :                             this->AMat(Node - 1, Node) = rk(Layer) * dxtmp;    // Assign matrix
     691       40224 :                             this->AMat(Node, Node) = -2.0 * rk(Layer) * dxtmp; // values for the
     692       40224 :                             this->AMat(Node + 1, Node) = rk(Layer) * dxtmp;    // current node.
     693             :                         }
     694             : 
     695       46119 :                         ++NodeInLayer; // Increment nodes in layer counter
     696       46119 :                         if (Node == this->NodeSource) this->BMat(3) = 1.0 / cap;
     697             : 
     698             :                     } // ... end of nodes loop.
     699             : 
     700             :                     // Intermediate calculations for the last node.
     701        3625 :                     cap = rho(LayersInConstruct) * cp(LayersInConstruct) * dx(LayersInConstruct);
     702        3625 :                     cap *= 1.5; // For the last node, account for the fact that the
     703             :                     // half-node at the surface results in a "loss" of some
     704             :                     // thermal mass.  Therefore, for simplicity, include it
     705             :                     // at this node.  Same thing done at the first node...
     706        3625 :                     dxtmp = 1.0 / dx(LayersInConstruct) / cap;
     707             : 
     708        3625 :                     this->AMat(this->rcmax, this->rcmax) = -2.0 * rk(LayersInConstruct) * dxtmp; // Assign matrix
     709        3625 :                     this->AMat(this->rcmax - 1, this->rcmax) = rk(LayersInConstruct) * dxtmp;    // values for the
     710        3625 :                     this->BMat(2) = rk(LayersInConstruct) * dxtmp;                               // last node.
     711             : 
     712        3625 :                     this->CMat(1) = -rk(1) / dx(1);                                 // Compute the necessary elements
     713        3625 :                     this->CMat(2) = rk(LayersInConstruct) / dx(LayersInConstruct);  // of all other
     714        3625 :                     this->DMat(1) = rk(1) / dx(1);                                  // matrices for the state
     715        3625 :                     this->DMat(2) = -rk(LayersInConstruct) / dx(LayersInConstruct); // space method
     716             : 
     717             :                 } else { // 2-D solution requested (assign matrices appropriately)
     718             : 
     719             :                     // As with the 1-D solution, we are accounting for the thermal mass
     720             :                     // of the half-node at the surface by adding it to the first row
     721             :                     // of interior nodes at both sides of the this->  This is not
     722             :                     // exact, but it does take all of the thermal mass into account.
     723           4 :                     amatx = rk(1) / (1.5 * rho(1) * cp(1) * dx(1) * dx(1));
     724           4 :                     amaty = rk(1) / (1.5 * rho(1) * cp(1) * dyn * dyn);
     725             : 
     726             :                     // FIRST ROW OF NODES: This first row within the first material layer
     727             :                     // is special in that it is exposed to a boundary condition.  Thus,
     728             :                     // the equations are slightly different.
     729             :                     // Note also that the first and last nodes in a row are slightly
     730             :                     // different from the rest since they are on an adiabatic plane in
     731             :                     // the direction perpendicular to the main direction of heat transfer.
     732           4 :                     this->AMat(1, 1) = -2.0 * (amatx + amaty);
     733           4 :                     this->AMat(2, 1) = 2.0 * amaty;
     734           4 :                     this->AMat(this->NumOfPerpendNodes + 1, 1) = amatx;
     735             : 
     736          24 :                     for (int Node = 2; Node <= this->NumOfPerpendNodes - 1; ++Node) {
     737          20 :                         this->AMat(Node - 1, Node) = amaty;
     738          20 :                         this->AMat(Node, Node) = -2.0 * (amatx + amaty);
     739          20 :                         this->AMat(Node + 1, Node) = amaty;
     740          20 :                         this->AMat(Node + this->NumOfPerpendNodes, Node) = amatx;
     741             :                     }
     742             : 
     743           4 :                     this->AMat(this->NumOfPerpendNodes, this->NumOfPerpendNodes) = -2.0 * (amatx + amaty);
     744           4 :                     this->AMat(this->NumOfPerpendNodes - 1, this->NumOfPerpendNodes) = 2.0 * amaty;
     745           4 :                     this->AMat(this->NumOfPerpendNodes + this->NumOfPerpendNodes, this->NumOfPerpendNodes) = amatx;
     746             : 
     747           4 :                     BMat(1) = amatx;
     748             : 
     749           4 :                     int Layer = 1;
     750           4 :                     int NodeInLayer = 2;
     751           4 :                     amatx = rk(1) / (rho(1) * cp(1) * dx(1) * dx(1)); // Reset these to the normal capacitance
     752           4 :                     amaty = rk(1) / (rho(1) * cp(1) * dyn * dyn);     // Reset these to the normal capacitance
     753           4 :                     assert(this->NumOfPerpendNodes > 0);              // Autodesk:F2C++ Loop setup assumption
     754           4 :                     int const Node_stop(this->rcmax + 1 - 2 * this->NumOfPerpendNodes);
     755         112 :                     for (int Node = this->NumOfPerpendNodes + 1; Node <= Node_stop; Node += this->NumOfPerpendNodes) {
     756             :                         // INTERNAL ROWS OF NODES: This is the majority of nodes which are all within
     757             :                         // a solid layer and not exposed to a boundary condition.
     758         108 :                         if ((LayersInConstruct == 1) || (NodeInLayer != Nodes(Layer))) {
     759             :                             // Single material row: This row of nodes are all contained within a material
     760             :                             // and thus there is no special considerations necessary.
     761          92 :                             if (NodeInLayer == 1) {
     762             :                                 // These intermediate variables only need to be reassigned when a new layer is started.
     763             :                                 // When this is simply another row of the same material, these have already been assigned correctly.
     764          16 :                                 amatx = rk(Layer) / (rho(Layer) * cp(Layer) * dx(Layer) * dx(Layer));
     765          16 :                                 amaty = rk(Layer) / (rho(Layer) * cp(Layer) * dyn * dyn);
     766             :                             }
     767             : 
     768             :                             // Note that the first and last layers in a row are slightly different
     769             :                             // from the rest since they are on an adiabatic plane in the direction
     770             :                             // perpendicular to the main direction of heat transfer.
     771          92 :                             this->AMat(Node, Node) = -2.0 * (amatx + amaty);
     772          92 :                             this->AMat(Node + 1, Node) = 2.0 * amaty;
     773          92 :                             this->AMat(Node - this->NumOfPerpendNodes, Node) = amatx;
     774          92 :                             this->AMat(Node + this->NumOfPerpendNodes, Node) = amatx;
     775             : 
     776         552 :                             for (int NodeInRow = 2; NodeInRow <= this->NumOfPerpendNodes - 1; ++NodeInRow) {
     777         460 :                                 int Node2 = Node + NodeInRow - 1;
     778         460 :                                 this->AMat(Node2 - 1, Node2) = amaty;
     779         460 :                                 this->AMat(Node2, Node2) = -2.0 * (amatx + amaty);
     780         460 :                                 this->AMat(Node2 + 1, Node2) = amaty;
     781         460 :                                 this->AMat(Node2 - this->NumOfPerpendNodes, Node2) = amatx;
     782         460 :                                 this->AMat(Node2 + this->NumOfPerpendNodes, Node2) = amatx;
     783             :                             }
     784             : 
     785          92 :                             int Node2 = Node - 1 + this->NumOfPerpendNodes;
     786          92 :                             this->AMat(Node2, Node2) = -2.0 * (amatx + amaty);
     787          92 :                             this->AMat(Node2 - 1, Node2) = 2.0 * amaty;
     788          92 :                             this->AMat(Node2 - this->NumOfPerpendNodes, Node2) = amatx;
     789          92 :                             this->AMat(Node2 + this->NumOfPerpendNodes, Node2) = amatx;
     790             : 
     791             :                         } else { // Row at a two-layer interface (half of node consists of one layer's materials
     792             :                             // and the other half consist of the next layer's materials)
     793          16 :                             capavg = 0.5 * (rho(Layer) * cp(Layer) * dx(Layer) + rho(Layer + 1) * cp(Layer + 1) * dx(Layer + 1));
     794          16 :                             amatx = rk(Layer) / (capavg * dx(Layer));
     795          16 :                             amatxx = rk(Layer + 1) / (capavg * dx(Layer + 1));
     796          16 :                             amaty = (rk(Layer) * dx(Layer) + rk(Layer + 1) * dx(Layer + 1)) / (capavg * dyn * dyn);
     797             : 
     798          16 :                             this->AMat(Node, Node) = -amatx - amatxx - 2.0 * amaty;
     799          16 :                             this->AMat(Node + 1, Node) = 2.0 * amaty;
     800          16 :                             this->AMat(Node - this->NumOfPerpendNodes, Node) = amatx;
     801          16 :                             this->AMat(Node + this->NumOfPerpendNodes, Node) = amatxx;
     802             : 
     803          96 :                             for (int NodeInRow = 2; NodeInRow <= this->NumOfPerpendNodes - 1; ++NodeInRow) {
     804          80 :                                 int Node2 = Node + NodeInRow - 1;
     805          80 :                                 this->AMat(Node2 - 1, Node2) = amaty;
     806          80 :                                 this->AMat(Node2, Node2) = -amatx - amatxx - 2.0 * amaty;
     807          80 :                                 this->AMat(Node2 + 1, Node2) = amaty;
     808          80 :                                 this->AMat(Node2 - this->NumOfPerpendNodes, Node2) = amatx;
     809          80 :                                 this->AMat(Node2 + this->NumOfPerpendNodes, Node2) = amatxx;
     810             :                             }
     811             : 
     812          16 :                             int Node2 = Node - 1 + this->NumOfPerpendNodes;
     813          16 :                             this->AMat(Node2, Node2) = -amatx - amatxx - 2.0 * amaty;
     814          16 :                             this->AMat(Node2 - 1, Node2) = 2.0 * amaty;
     815          16 :                             this->AMat(Node2 - this->NumOfPerpendNodes, Node2) = amatx;
     816          16 :                             this->AMat(Node2 + this->NumOfPerpendNodes, Node2) = amatxx;
     817             : 
     818          16 :                             if (Node == this->NodeSource) BMat(3) = 2.0 * double(this->NumOfPerpendNodes - 1) / capavg;
     819          16 :                             NodeInLayer = 0;
     820          16 :                             ++Layer;
     821             :                         }
     822         108 :                         ++NodeInLayer;
     823             :                     }
     824             : 
     825             :                     // LAST ROW OF NODES: Like the first row of nodes, this row is exposed to a boundary
     826             :                     // condition and thus has slightly modified nodal equations.
     827             : 
     828             :                     // As with the 1-D solution, we are accounting for the thermal mass
     829             :                     // of the half-node at the surface by adding it to the first row
     830             :                     // of interior nodes at both sides of the this->  This is not
     831             :                     // exact, but it does take all of the thermal mass into account.
     832           4 :                     amatx /= 1.5;
     833           4 :                     amaty /= 1.5;
     834             : 
     835           4 :                     int Node = this->rcmax + 1 - this->NumOfPerpendNodes;
     836           4 :                     this->AMat(Node, Node) = -2.0 * (amatx + amaty);
     837           4 :                     this->AMat(Node + 1, Node) = 2.0 * amaty;
     838           4 :                     this->AMat(Node - this->NumOfPerpendNodes, Node) = amatx;
     839             : 
     840          24 :                     for (int thisNode = this->rcmax + 2 - this->NumOfPerpendNodes; thisNode <= this->rcmax - 1; ++thisNode) {
     841          20 :                         this->AMat(thisNode - 1, thisNode) = amaty;
     842          20 :                         this->AMat(thisNode, thisNode) = -2.0 * (amatx + amaty);
     843          20 :                         this->AMat(thisNode + 1, thisNode) = amaty;
     844          20 :                         this->AMat(thisNode - this->NumOfPerpendNodes, thisNode) = amatx;
     845             :                     }
     846             : 
     847           4 :                     this->AMat(this->rcmax, this->rcmax) = -2.0 * (amatx + amaty);
     848           4 :                     this->AMat(this->rcmax - 1, this->rcmax) = 2.0 * amaty;
     849           4 :                     this->AMat(this->rcmax - this->NumOfPerpendNodes, this->rcmax) = amatx;
     850             : 
     851           4 :                     this->BMat(2) = amatx;
     852             : 
     853           4 :                     this->CMat(1) = -rk(1) / dx(1) / double(this->NumOfPerpendNodes - 1);
     854           4 :                     this->CMat(2) = rk(LayersInConstruct) / dx(LayersInConstruct) / double(this->NumOfPerpendNodes - 1);
     855             : 
     856           4 :                     this->DMat(1) = rk(1) / dx(1) / double(this->NumOfPerpendNodes - 1);
     857           4 :                     this->DMat(2) = -rk(LayersInConstruct) / dx(LayersInConstruct) / double(this->NumOfPerpendNodes - 1);
     858             :                 }
     859             : 
     860             :                 // Calculation of the CTFs based on the state space
     861             :                 // method.  This process involves finding the exponential
     862             :                 // and inverse of AMat and using these results to
     863             :                 // determine the CTFs.  The Gammas are an intermediate
     864             :                 // calculations which are necessary before the CTFs can
     865             :                 // be computed in TransFuncCoeffs.
     866        3629 :                 DisplayString(state, "Calculating CTFs for \"" + this->Name + "\"");
     867             : 
     868             :                 //          CALL DisplayNumberAndString(ConstrNum,'Matrix exponential for Construction #')
     869        3629 :                 this->calculateExponentialMatrix(); // Compute exponential of AMat
     870             : 
     871             :                 //          CALL DisplayNumberAndString(ConstrNum,'Invert Matrix for Construction #')
     872        3629 :                 this->calculateInverseMatrix(); // Compute inverse of AMat
     873             : 
     874             :                 //          CALL DisplayNumberAndString(ConstrNum,'Gamma calculation for Construction #')
     875        3629 :                 this->calculateGammas();
     876             :                 // Compute "gamma"s from AMat, AExp, and AInv
     877             : 
     878             :                 //          CALL DisplayNumberAndString(ConstrNum,'Compute CTFs for Construction #')
     879        3629 :                 this->calculateFinalCoefficients(); // Compute CTFs
     880             : 
     881             :                 // Now check to see if the number of transfer functions
     882             :                 // is greater than MaxCTFTerms.  If it is, then increase the
     883             :                 // time step and the number of history terms and
     884             :                 // recalculate.  Whether or not it will be necessary to
     885             :                 // recalculate the CTFs is controlled by this DO WHILE
     886             :                 // loop and the logical CTFConvrg.
     887             : 
     888        3629 :                 CTFConvrg = true; // Assume solution convergence
     889             : 
     890             :                 // If too many terms, then solution did not converge.  Increase the
     891             :                 // number of histories and the time step.  Reset CTFConvrg to continue
     892             :                 // the DO loop.
     893        3629 :                 if (this->NumCTFTerms > (Construction::MaxCTFTerms - 1)) {
     894          41 :                     ++this->NumHistories;
     895          41 :                     this->CTFTimeStep += state.dataGlobal->TimeStepZone;
     896          41 :                     CTFConvrg = false;
     897             :                 }
     898             : 
     899             :                 // If the number of terms is okay, then do a further check on the summation of
     900             :                 // the various series summations.  In theory, Sum(Xi) = Sum(Yi) = Sum(Zi).  If
     901             :                 // this is not the case, then the terms have not reached a valid solution, and
     902             :                 // we need to increase the number of histories and the time step as above.
     903        3629 :                 if (CTFConvrg) {
     904        3588 :                     SumXi = this->s0(2, 2);
     905        3588 :                     SumYi = this->s0(1, 2);
     906        3588 :                     SumZi = this->s0(1, 1);
     907       28990 :                     for (int HistTerm = 1; HistTerm <= this->NumCTFTerms; ++HistTerm) {
     908       25402 :                         SumXi += this->s(2, 2, HistTerm);
     909       25402 :                         SumYi += this->s(1, 2, HistTerm);
     910       25402 :                         SumZi += this->s(1, 1, HistTerm);
     911             :                     }
     912        3588 :                     SumXi = std::abs(SumXi);
     913        3588 :                     SumYi = std::abs(SumYi);
     914        3588 :                     SumZi = std::abs(SumZi);
     915        3588 :                     BiggestSum = max(SumXi, SumYi, SumZi);
     916        3588 :                     if (BiggestSum > 0.0) {
     917        7121 :                         if (((std::abs(SumXi - SumYi) / BiggestSum) > MaxAllowedCTFSumError) ||
     918        3533 :                             ((std::abs(SumZi - SumYi) / BiggestSum) > MaxAllowedCTFSumError)) {
     919          56 :                             ++this->NumHistories;
     920          56 :                             this->CTFTimeStep += state.dataGlobal->TimeStepZone;
     921          56 :                             CTFConvrg = false;
     922             :                         }
     923             :                     } else { // Something terribly wrong--the surface has no CTFs, not even an R-value
     924           0 :                         ShowFatalError(state, "Illegal construction definition, no CTFs calculated for " + this->Name);
     925             :                     }
     926             :                 }
     927             : 
     928             :                 // Once the time step has reached a certain point, it is highly likely that
     929             :                 // there is either a problem with the input or the solution.  This should
     930             :                 // be extremely rare since other checks should flag most bad user input.
     931             :                 // Thus, if the time step reaches a certain point, error out and let the
     932             :                 // user know that something needs to be checked in the input file.
     933        3629 :                 if (this->CTFTimeStep >= MaxAllowedTimeStep) {
     934           0 :                     ShowSevereError(state, "CTF calculation convergence problem for Construction=\"" + this->Name + "\".");
     935           0 :                     ShowContinueError(state, "...with Materials (outside layer to inside)");
     936           0 :                     ShowContinueError(state, "(outside)=\"" + state.dataMaterial->Material(this->LayerPoint(1)).Name + "\"");
     937           0 :                     for (int Layer = 2; Layer <= this->TotLayers; ++Layer) {
     938           0 :                         if (Layer != this->TotLayers) {
     939           0 :                             ShowContinueError(state, "(next)=\"" + state.dataMaterial->Material(this->LayerPoint(Layer)).Name + "\"");
     940             :                         } else {
     941           0 :                             ShowContinueError(state, "(inside)=\"" + state.dataMaterial->Material(this->LayerPoint(Layer)).Name + "\"");
     942             :                         }
     943             :                     }
     944           0 :                     ShowContinueError(state,
     945             :                                       "The Construction report will be produced. This will show more details on Constructions and their materials.");
     946           0 :                     ShowContinueError(state, "Attempts will be made to complete the CTF process but the report may be incomplete.");
     947           0 :                     ShowContinueError(state, "Constructs reported after this construction may appear to have all 0 CTFs.");
     948           0 :                     ShowContinueError(state, "The potential causes of this problem are related to the input for the construction");
     949           0 :                     ShowContinueError(state, "listed in the severe error above.  The CTF calculate routine is unable to come up");
     950           0 :                     ShowContinueError(state, "with a series of CTF terms that have a reasonable time step and this indicates an");
     951           0 :                     ShowContinueError(state, "error.  Check the definition of this construction and the materials that make up");
     952           0 :                     ShowContinueError(state, "the this->  Very thin, highly conductive materials may cause problems.");
     953           0 :                     ShowContinueError(state, "This may be avoided by ignoring the presence of those materials since they probably");
     954           0 :                     ShowContinueError(state, "do not effect the heat transfer characteristics of the this->  Highly");
     955           0 :                     ShowContinueError(state, "conductive or highly resistive layers that are alternated with high mass layers");
     956           0 :                     ShowContinueError(state, "may also result in problems.  After confirming that the input is correct and");
     957           0 :                     ShowContinueError(state, "realistic, the user should contact the EnergyPlus support team.");
     958           0 :                     DoCTFErrorReport = true;
     959           0 :                     ErrorsFound = true;
     960           0 :                     break;
     961             :                     //            CALL ShowFatalError(state, 'Program terminated for reasons listed (InitConductionTransferFunctions) ')
     962             :                 }
     963             : 
     964             :             } // ... end of CTF calculation loop.
     965             : 
     966             :         } // ... end of IF block for non-reversed constructs.
     967             : 
     968             :     } else { // Construct has only resistive layers (no thermal mass).
     969             :         // CTF calculation not necessary, overall resistance
     970             :         // (R-value) is all that is needed.
     971             : 
     972             :         // Set time step for construct to user time step and the number of
     973             :         // inter-time step interpolations to 1
     974         330 :         this->CTFTimeStep = state.dataGlobal->TimeStepZone;
     975         330 :         this->NumHistories = 1;
     976         330 :         this->NumCTFTerms = 1;
     977             : 
     978         330 :         this->s0(1, 1) = cnd;  // CTFs for current time
     979         330 :         this->s0(2, 1) = -cnd; // step are set to the
     980         330 :         this->s0(1, 2) = cnd;  // overall conductance
     981         330 :         this->s0(2, 2) = -cnd; // of the this->
     982             : 
     983         330 :         this->e.allocate(1);
     984         330 :         this->e = 0.0;
     985         330 :         this->s.allocate(2, 2, 1);
     986         330 :         this->s = 0.0;
     987         330 :         this->s(1, 1, 1) = 0.0; // CTF temperature
     988         330 :         this->s(2, 1, 1) = 0.0; // and flux
     989         330 :         this->s(1, 2, 1) = 0.0; // history terms
     990         330 :         this->s(2, 2, 1) = 0.0; // are all
     991         330 :         this->e(1) = 0.0;       // zero.
     992             : 
     993         330 :         if (this->SourceSinkPresent) {
     994           0 :             ShowSevereError(state, "Sources/sinks not allowed in purely resistive constructions --> " + this->Name);
     995           0 :             ErrorsFound = true;
     996             :         }
     997             : 
     998         330 :         RevConst = false; // In the code that follows, handle a resistive
     999             :         // layer as a non-reversed this->
    1000             : 
    1001             :     } // ... end of resistive construction IF block.
    1002             : 
    1003             :     // Transfer the CTFs to the storage arrays for all non-reversed
    1004             :     // constructions.  This transfer was done earlier in the routine for
    1005             :     // reversed constructions.
    1006             : 
    1007        4022 :     if (!RevConst) { // If this is either a new construction or a non-
    1008             :         // reversed construction, the CTFs must be stored
    1009             :         // in the proper arrays.  If this is a reversed
    1010             :         // construction, nothing further needs to be done.
    1011             : 
    1012             :         // Copy the CTFs into the storage arrays, converting them back to SI
    1013             :         // units in the process.  First the "zero" terms and then the history terms...
    1014        3862 :         this->CTFOutside(0) = this->s0(1, 1) * DataConversions::CFU;
    1015        3862 :         this->CTFCross(0) = this->s0(1, 2) * DataConversions::CFU;
    1016        3862 :         this->CTFInside(0) = -this->s0(2, 2) * DataConversions::CFU;
    1017        3862 :         if (this->SourceSinkPresent) {
    1018             :             // QTFs...
    1019          39 :             this->CTFSourceOut(0) = this->s0(3, 1);
    1020          39 :             this->CTFSourceIn(0) = this->s0(3, 2);
    1021             :             // QTFs for temperature calculation at source/sink location
    1022          39 :             this->CTFTSourceOut(0) = this->s0(1, 3);
    1023          39 :             this->CTFTSourceIn(0) = this->s0(2, 3);
    1024          39 :             this->CTFTSourceQ(0) = this->s0(3, 3) / DataConversions::CFU;
    1025          39 :             if (this->TempAfterLayer != 0) {
    1026             :                 // QTFs for user specified interior temperature calculations...
    1027          39 :                 this->CTFTUserOut(0) = this->s0(1, 4);
    1028          39 :                 this->CTFTUserIn(0) = this->s0(2, 4);
    1029          39 :                 this->CTFTUserSource(0) = this->s0(3, 4) / DataConversions::CFU;
    1030             :             }
    1031             :         }
    1032             : 
    1033       28939 :         for (int HistTerm = 1; HistTerm <= this->NumCTFTerms; ++HistTerm) {
    1034             :             // "REGULAR" CTFs...
    1035       25077 :             this->CTFOutside(HistTerm) = this->s(1, 1, HistTerm) * DataConversions::CFU;
    1036       25077 :             this->CTFCross(HistTerm) = this->s(1, 2, HistTerm) * DataConversions::CFU;
    1037       25077 :             this->CTFInside(HistTerm) = -this->s(2, 2, HistTerm) * DataConversions::CFU;
    1038       25077 :             if (HistTerm != 0) this->CTFFlux(HistTerm) = -e(HistTerm);
    1039       25077 :             if (this->SourceSinkPresent) {
    1040             :                 // QTFs...
    1041         379 :                 this->CTFSourceOut(HistTerm) = this->s(3, 1, HistTerm);
    1042         379 :                 this->CTFSourceIn(HistTerm) = this->s(3, 2, HistTerm);
    1043             :                 // QTFs for temperature calculation at source/sink location
    1044         379 :                 this->CTFTSourceOut(HistTerm) = this->s(1, 3, HistTerm);
    1045         379 :                 this->CTFTSourceIn(HistTerm) = this->s(2, 3, HistTerm);
    1046         379 :                 this->CTFTSourceQ(HistTerm) = this->s(3, 3, HistTerm) / DataConversions::CFU;
    1047         379 :                 if (this->TempAfterLayer != 0) {
    1048             :                     // QTFs for user specified interior temperature calculations...
    1049         379 :                     this->CTFTUserOut(HistTerm) = this->s(1, 4, HistTerm);
    1050         379 :                     this->CTFTUserIn(HistTerm) = this->s(2, 4, HistTerm);
    1051         379 :                     this->CTFTUserSource(HistTerm) = this->s(3, 4, HistTerm) / DataConversions::CFU;
    1052             :                 }
    1053             :             }
    1054             :         }
    1055             : 
    1056             :     } // ... end of the reversed construction IF block.
    1057             : 
    1058        4022 :     this->UValue = cnd * DataConversions::CFU;
    1059             : 
    1060        4022 :     if (allocated(this->AExp)) this->AExp.deallocate();
    1061        4022 :     if (allocated(this->AMat)) AMat.deallocate();
    1062        4022 :     if (allocated(this->AInv)) this->AInv.deallocate();
    1063        4022 :     if (allocated(this->IdenMatrix)) this->IdenMatrix.deallocate();
    1064        4022 :     if (allocated(this->e)) this->e.deallocate();
    1065        4022 :     if (allocated(this->Gamma1)) this->Gamma1.deallocate();
    1066        4022 :     if (allocated(this->Gamma2)) this->Gamma2.deallocate();
    1067        4022 :     if (allocated(this->s)) this->s.deallocate();
    1068             : }
    1069             : 
    1070        3629 : void ConstructionProps::calculateExponentialMatrix()
    1071             : {
    1072             : 
    1073             :     // SUBROUTINE INFORMATION:
    1074             :     //       AUTHOR         Russ Taylor
    1075             :     //       DATE WRITTEN   June 1990
    1076             :     //       MODIFIED       Dec 1995, Apr 1996, RKS; June 2000 RKS
    1077             :     //       RE-ENGINEERED  June 1996, RKS; Nov 1999, LKL;
    1078             : 
    1079             :     // PURPOSE OF THIS SUBROUTINE:
    1080             :     // This subroutine computes the exponential matrix exp(AMat*delt) for
    1081             :     // use in the state space method for the calculation of CTFs.
    1082             : 
    1083             :     // METHODOLOGY EMPLOYED:
    1084             :     // Uses the method of Taylor expansion combined with scaling and
    1085             :     // squaring to most efficiently compute the exponential matrix.  The
    1086             :     // steps in the procedure are outlined in Seem's dissertation in
    1087             :     // Appendix A, page 128.  Exponential matrix multiplication modified
    1088             :     // to take advantage of the characteristic form of AMat.  AMat starts
    1089             :     // out as a tri-diagonal matrix.  Each time AMat is raised to a higher
    1090             :     // power two extra non-zero diagonals are added.  ExponMatrix now
    1091             :     // recognizes this.  This should speed up the calcs somewhat.  Also, a
    1092             :     // new cut-off criteria based on the significant figures of double-
    1093             :     // precision variables has been added.  The main loop for higher powers
    1094             :     // of AMat is now stopped whenever these powers of AMat will no longer
    1095             :     // add to the summation (AExp) instead ofstopping potentially at the
    1096             :     // artifical limit of AMat**100.
    1097             : 
    1098             :     // REFERENCES:
    1099             :     // Seem, J.E.  "Modeling of Heat Transfer in Buildings",
    1100             :     //  Department of Mechanical Engineering, University of
    1101             :     //  Wisconsin-Madison, 1987.
    1102             :     // Strand, R.K. "Testing Design Description for the CTF
    1103             :     //  Calculation Code in BEST", BSO internal document,
    1104             :     //  May/June 1996.
    1105             : 
    1106        3629 :     constexpr Real64 DPLimit(1.0e-20);
    1107             :     // This argument is nice, but not sure it's accurate -- LKL Nov 1999.
    1108             :     // Parameter set to the significant figures limit of double
    1109             :     // precision variables plus a safety factor.- The argument for setting this parameter to 1E-20 involves the
    1110             :     // number of significant figures for REAL(r64) variables which is 16 and the largest power to which
    1111             :     // AMat will be raised which is 100.  This would be a factor of 1E-18.  A factor of "safety" of another 100
    1112             :     // arrives at the value chosen.  It is argued that if one number is 1E-16 larger than a second number, then
    1113             :     // adding the second to the first will not effect the first.  However, on the conservative side, there could
    1114             :     // be up to 100 numbers which might, added together, still could effect the original number.  Each
    1115             :     // successive power of AMat will have terms smaller than the previous power.  Thus, when the ratio between
    1116             :     // the terms of the latest power of AMat and the total (AExp) is less than DPLim, all further powers of
    1117             :     // AMat will have absolutely no effect on the REAL(r64) value of AExp.  Thus, there is no need to
    1118             :     // continue the calculation.  In effect, AExp has "converged".  In REAL(r64)ity, 1E-16 would probably guarantee
    1119             :     // convergence since AMat terms drop off quickly, but the extra powers allows for differences between
    1120             :     // computer platforms.
    1121             : 
    1122             :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    1123             :     Real64 AMatRowNorm;    // Row norm for AMat
    1124             :     Real64 AMatRowNormMax; // Largest row norm for AMat
    1125        7258 :     Array2D<Real64> AMat1; // AMat factored by (delt/2^k)
    1126        7258 :     Array2D<Real64> AMato; // AMat raised to the previous power (power of AMat1-1)
    1127        7258 :     Array2D<Real64> AMatN; // Current value of AMat raised to power n (n = 1,2...)
    1128             :     bool Backup;           // Used when numerics get to small in Exponentiation
    1129             :     Real64 CheckVal;       // Used to avoid possible overflow from Double->REAL(r64)->Integer
    1130             :     Real64 fact;           // Intermediate calculation variable (delt/2^k)
    1131             :     int i;                 // Loop counter
    1132             :     int ic;                // Loop counter
    1133             :     int ict;               // Loop counter
    1134             :     int idm;               // Loop counter
    1135             :     int ir;                // Loop counter
    1136             :     int isq;               // Loop counter
    1137             :     int j;                 // Loop counter
    1138             :     int k;                 // Power of 2 which is used to factor AMat
    1139             :     int l;                 // Theoretical power to which the A matrix must be
    1140             :     // raised to accurately calculate the exponential matrix
    1141             :     bool SigFigLimit; // Significant figure limit logical, true
    1142             :     // when exponential calculation loop can be exited (i.e.
    1143             :     // the significant figure limit for REAL(r64)
    1144             :     // variables reached)
    1145             : 
    1146        3629 :     AMat1.allocate(this->rcmax, this->rcmax);
    1147        3629 :     AMato.allocate(this->rcmax, this->rcmax);
    1148        3629 :     AMatN.allocate(this->rcmax, this->rcmax);
    1149             : 
    1150             :     // Subroutine initializations.  AMat is assigned to local variable AMat1 to
    1151             :     // avoid the corruption of the original AMat 2-d array.
    1152        3629 :     AMat1 = AMat;
    1153             : 
    1154             :     //  Other arrays are initialized to zero.
    1155        3629 :     this->AExp = 0.0;
    1156        3629 :     AMato = 0.0;
    1157        3629 :     AMatN = 0.0;
    1158             : 
    1159             :     // Step 1, page 128 (Seem's thesis):  Compute the matrix row norm.
    1160             :     // See equation (A.3) which states that the matrix row norm is the
    1161             :     // maximum summation of the elements in a row of AMat multiplied by
    1162             :     // the time step.
    1163             : 
    1164             :     // Note With change to row-major arrays "row" here now means "column"
    1165             : 
    1166        3629 :     AMatRowNormMax = 0.0; // Start of Step 1 ...
    1167             : 
    1168       57810 :     for (i = 1; i <= this->rcmax; ++i) {
    1169             : 
    1170       54181 :         AMatRowNorm = 0.0;
    1171     1195806 :         for (j = 1; j <= this->rcmax; ++j) {
    1172     1141625 :             AMatRowNorm += std::abs(AMat1(j, i));
    1173             :         }
    1174             : 
    1175       54181 :         AMatRowNorm *= this->CTFTimeStep;
    1176             : 
    1177       54181 :         AMatRowNormMax = max(AMatRowNormMax, AMatRowNorm);
    1178             : 
    1179             :     } // ... end of Step 1.
    1180             : 
    1181             :     // Step 2, page 128:  Find smallest integer k such that
    1182             :     // AMatRowNormMax< = 2^k
    1183             : 
    1184        3629 :     k = int(std::log(AMatRowNormMax) / std::log(2.0)) + 1; // Autodesk:Num Handle AMatRowNormMax=0
    1185             : 
    1186             :     // Step 3, page 128:  Divide (AMat*delt) by 2^k.  This section of code
    1187             :     // takes advantage of the fact that AMat is tridiagonal.  Thus, it
    1188             :     // only factors the elements of the AMat that are known to be non-zero.
    1189             : 
    1190        3629 :     fact = this->CTFTimeStep / std::pow(2.0, k); // Start of Step 3 ...
    1191        3629 :     AMat1 *= fact;                               // ... end of Step 3.
    1192             : 
    1193             :     // Step 4, page 128:  Calculate l, the highest power to which AMat
    1194             :     // must be taken theoretically to accurately calculate its exponential.
    1195             :     // This is based on a paper by Cadzow and Martens ("Discrete-Time and
    1196             :     // Computer Control Systems",Prentice-Hall, pp. 389-390, 1970).  This
    1197             :     // number is now used as the maximum power to which AMat must be
    1198             :     // raised in order to calculate the exponential matrix.  A new cut-off
    1199             :     // criteria based on the number of significant figures in a double-
    1200             :     // precision variable is used as a more practical limit on the
    1201             :     // exponentiation algorithm.  One thing to note is that the EnergyPlus
    1202             :     // code is slightly different here than what is presented in Seem's
    1203             :     // dissertation.  In Seem's dissertation, AMatRowNormMax (delta, the
    1204             :     // timestep, is already factored into this term above) is divided by
    1205             :     // 2^k with k defined in the code above.  Dividing AMatRowNormMax by
    1206             :     // 2^k would have the net effect of decreasing the value of
    1207             :     // AMatRowNormMax and thus potentially CheckVal as well.  l, the integer
    1208             :     // value of CheckVal, is used to define the number of terms in the
    1209             :     // exponential matrix of AMat that are required for an accurate estimation
    1210             :     // of that matrix.  In practice, additional terms probably won't have
    1211             :     // a large effect but in general should make the calculation MORE accurate.
    1212             :     // Also, if the new terms are too small, then as noted above the cut-off
    1213             :     // criteria will not use them.
    1214             : 
    1215        3629 :     CheckVal = min(3.0 * AMatRowNormMax + 6.0, 100.0);
    1216        3629 :     l = int(CheckVal);
    1217             : 
    1218             :     // Step 5, page 128:  Calculate the exponential.  First, add the
    1219             :     // linear term to the identity matrix.
    1220        3629 :     this->AExp = AMat1 + this->IdenMatrix; // Start of Step 5 ...
    1221             : 
    1222             :     // Now, add successive terms to the expansion as per the standard
    1223             :     // exponential formula.  AMato contains the last "power" of AMat
    1224             :     // which saves the program from having to remultiply the entire power
    1225             :     // of AMat each time.  Since this is still the linear power of AMat,
    1226             :     // AMat1 is still tridiagonal in nature.
    1227        3629 :     AMato = AMat1;
    1228             : 
    1229        3629 :     i = 1; // Initialize the counter for the following DO loop
    1230             : 
    1231             :     // The following DO WHILE loop continues to raise AMat to successive
    1232             :     // powers and add it to the exponential matrix (AExp).
    1233      546757 :     while (i < l) { // Begin power raising loop ...
    1234             : 
    1235      271564 :         ++i;                // Increment the loop counter
    1236      271564 :         SigFigLimit = true; // Set the significant factor limit flag
    1237             : 
    1238     5065165 :         for (ir = 1; ir <= this->rcmax; ++ir) { // Begin matrix multiplication loop ...
    1239             :             // The following matrix multiplication could be "optimized" since
    1240             :             // for one-dimensional heat transfer AMat is 3-diagonal, AMat squared
    1241             :             // is 5-diagonal, etc.  However, the code can be much simpler if we
    1242             :             // ignore this fact and just do a generic matrix multiplication.
    1243             :             // For 2-D heat transfer, the number of off-diagonal non-zero terms
    1244             :             // is slightly more complicated as well.
    1245   113271714 :             for (ic = 1; ic <= this->rcmax; ++ic) {
    1246   108478113 :                 AMatN(ic, ir) = 0.0;
    1247  5409811692 :                 for (ict = 1; ict <= this->rcmax; ++ict) {
    1248             :                     // Make sure the next term won't cause an underflow.  If it will end up being
    1249             :                     // so small as to go below TinyLimit, then ignore it since it won't add anything
    1250             :                     // to AMatN anyway.
    1251  5301333579 :                     if (std::abs(AMat1(ic, ict)) > DataGlobalConstants::rTinyValue) {
    1252   342857505 :                         if (std::abs(AMato(ict, ir)) > std::abs(double(i) * DataGlobalConstants::rTinyValue / AMat1(ic, ict)))
    1253    10341411 :                             AMatN(ic, ir) += AMato(ict, ir) * AMat1(ic, ict) / double(i);
    1254             :                     }
    1255             :                 }
    1256             :             }
    1257             :         } // ... end of matrix multiplication loop.
    1258             : 
    1259             :         // Update AMato and AExp matrices
    1260      271564 :         AMato = AMatN;
    1261      271564 :         this->AExp += AMato;
    1262             : 
    1263             :         // The next DO loop tests the significant figures limit criteria to
    1264             :         // see if any values in AExp are still changing appreciably.
    1265      275739 :         for (ir = 1; ir <= this->rcmax; ++ir) {
    1266     2090082 :             for (ic = 1; ic <= this->rcmax; ++ic) {
    1267             :                 // Test of limit criteria:
    1268     2085907 :                 if (std::abs(this->AExp(ic, ir)) > DataGlobalConstants::rTinyValue) { // Next line divides by AExp entry so it
    1269             :                     // must be checked to avoid dividing by zero.
    1270             :                     // If the ratio between any current element in the power
    1271             :                     // of AMat and its corresponding element in AExp is
    1272             :                     // greater than the number which might effect the overall
    1273             :                     // exponential matrix based on stability criteria, then
    1274             :                     // continue raising AMat to another power (SigFigLimit = false).
    1275             : 
    1276     1852016 :                     if (std::abs(AMato(ic, ir) / this->AExp(ic, ir)) > DPLimit) {
    1277       37048 :                         SigFigLimit = false;
    1278       37048 :                         break; // DO loop (anytime SigFigLimit is false, AMat must continue to be raised another power)
    1279             :                     }
    1280             : 
    1281             :                 } else { // There are still elements of AExp which are zero, so
    1282             :                     // the raising of AMat to higher powers should continue.
    1283             : 
    1284      233891 :                     SigFigLimit = false;
    1285      233891 :                     break; // DO loop (anytime SigFigLimit is false, AMat must continue to be raised another power)
    1286             :                 }
    1287             :             }
    1288      275114 :             if (!SigFigLimit) break; // DO loop (anytime SigFigLimit is false, AMat must continue to be raised another power)
    1289             :         }
    1290             : 
    1291             :         // Compute next term, only if necessary.  If SigFigLimit is still true,
    1292             :         // then all of the new terms being added to AExp are too small to
    1293             :         // affect it.  Thus, there is no need to continue this do loop further.
    1294             : 
    1295      271564 :         if (SigFigLimit) i = 100; // SigFigLimit is still true, set i to maximum possible
    1296             :         // value of l (100).
    1297             : 
    1298             :     } // ... end of power raising loop and Step 5.
    1299             : 
    1300             :     // Step 6, page 128:
    1301             :     // Square AExp "k times" to obtain the actual exponential matrix
    1302             :     // (remember that AExp was scaled earlier in this routine).
    1303             : 
    1304       39581 :     for (isq = 1; isq <= k; ++isq) { // Begin squaring DO loop and Step 6 ...
    1305             : 
    1306             :         // Use AMato to store the old values of AExp
    1307       35992 :         AMato = this->AExp;
    1308       35992 :         Backup = true;
    1309       35992 :         this->AExp = 0.0;
    1310             : 
    1311             :         // Multiply the old value of AExp (AMato) by itself and store in AExp.
    1312      618689 :         for (ir = 1; ir <= this->rcmax; ++ir) {
    1313    13343508 :             for (ic = 1; ic <= this->rcmax; ++ic) {
    1314   659278350 :                 for (idm = 1; idm <= this->rcmax; ++idm) {
    1315   646517539 :                     if (std::abs(AMato(idm, ir) * AMato(ic, idm)) > DataGlobalConstants::rTinyValue) {
    1316   206009555 :                         this->AExp(ic, ir) += AMato(idm, ir) * AMato(ic, idm);
    1317   206009555 :                         Backup = false;
    1318             :                     }
    1319             :                 }
    1320             :             }
    1321             :         }
    1322             :         // Backup is true when every item of AExp didnt pass the TinyLimit test
    1323       35992 :         if (Backup) {
    1324          40 :             this->AExp = AMato;
    1325          40 :             break;
    1326             :         }
    1327             : 
    1328             :     } // ... end of squaring loop and Step 6.
    1329             : 
    1330        3629 :     AMat1.deallocate();
    1331        3629 :     AMato.deallocate();
    1332        3629 :     AMatN.deallocate();
    1333        3629 : }
    1334             : 
    1335        3629 : void ConstructionProps::calculateInverseMatrix()
    1336             : {
    1337             : 
    1338             :     // SUBROUTINE INFORMATION:
    1339             :     //       AUTHOR         Rick Strand
    1340             :     //       DATE WRITTEN   Dec 1995
    1341             :     //       MODIFIED       June 2000 RKS (made routine generic to allow for 2-D solutions)
    1342             :     //       RE-ENGINEERED  June 1996, February 1997 RKS
    1343             : 
    1344             :     // PURPOSE OF THIS SUBROUTINE:
    1345             :     // This subroutine computes the inverse of AMat for use
    1346             :     // in the calculation of the CTFs.
    1347             : 
    1348             :     // METHODOLOGY EMPLOYED:
    1349             :     // Uses row elimination to zero the off-diagonal terms of
    1350             :     // AMat while performing the same operations on another
    1351             :     // matrix which starts as the identity matrix.  Once AMat
    1352             :     // has been converted to an identity matrix(I), the other
    1353             :     // matrix which started as the I will then be the inverse
    1354             :     // of A.  This algorithm has been customized for a
    1355             :     // tri-diagonal matrix.
    1356             : 
    1357             :     // REFERENCES:
    1358             :     // Any linear algebra test (this is a generic routine).
    1359             : 
    1360             :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    1361        7258 :     Array2D<Real64> AMat1; // Intermediate calculation matrix equivalent at first to AMat
    1362             :     int ic;                // Loop counter
    1363             :     int ir;                // Loop counter
    1364             :     int irr;               // Loop counter
    1365             : 
    1366             :     // Subroutine initializations ...
    1367        3629 :     AMat1.allocate(this->rcmax, this->rcmax);
    1368             : 
    1369        3629 :     AMat1 = AMat;                  // Set AMat1 = AMat to avoid AMat changes
    1370        3629 :     this->AInv = this->IdenMatrix; // Set AInv to Identity Matrix
    1371             : 
    1372             :     // Use Gaussian elimination to zero all of the elements of AMat left
    1373             :     // of the diagonal.
    1374             :     // This DO loop will cycle through each of the rows in AMat except the
    1375             :     // last row which is handled later because it does not have to be used
    1376             :     // to eliminate any other rows.  The index ir is the current row
    1377             :     // number and also the column of the current diagonal element.
    1378             : 
    1379       54181 :     for (ir = 1; ir <= this->rcmax - 1; ++ir) { // Begin forward elimination loop ...
    1380             : 
    1381             :         // Factor all of the elements of the row being used to zero the next
    1382             :         // row in both AMat and AInv by the diagonal element of this row.
    1383             :         // We should only need to factor the elements to the right of the
    1384             :         // diagonal since those to the right of it should be zero.
    1385      594274 :         for (ic = ir + 1; ic <= this->rcmax; ++ic) {
    1386      543722 :             AMat1(ic, ir) /= AMat1(ir, ir);
    1387             :         }
    1388             : 
    1389             :         // In the forward elimination process, all the elements in AInv to the
    1390             :         // right of the diagonal are zero so they do not need to be factored.
    1391      594274 :         for (ic = 1; ic <= ir; ++ic) {
    1392      543722 :             this->AInv(ic, ir) /= AMat1(ir, ir);
    1393             :         }
    1394             : 
    1395       50552 :         AMat1(ir, ir) = 1.0; // By definition, the diagonal of AMat is now 1.
    1396             : 
    1397             :         // Use this factored row to eliminate the off-diagonal element of the
    1398             :         // rows below the current one (ir)...
    1399             : 
    1400      594274 :         for (irr = ir + 1; irr <= this->rcmax; ++irr) { // Start of row reduction loop...
    1401             : 
    1402    17991072 :             for (ic = ir + 1; ic <= this->rcmax; ++ic) {
    1403    17447350 :                 AMat1(ic, irr) -= AMat1(ir, irr) * AMat1(ic, ir);
    1404             :             }
    1405             : 
    1406             :             // Now, determine the effect on the next row of AInv.  Again, all of
    1407             :             // the elements in AInv to the right of the diagonal are zero, so they
    1408             :             // can be ignored.
    1409             : 
    1410     9539258 :             for (ic = 1; ic <= ir; ++ic) {
    1411     8995536 :                 this->AInv(ic, irr) -= AMat1(ir, irr) * this->AInv(ic, ir);
    1412             :             }
    1413             : 
    1414      543722 :             AMat1(ir, irr) = 0.0; // By definition, the element to the left of the
    1415             :             // diagonal in the next row of AMat is now zero.
    1416             : 
    1417             :         } // ...end of row reduction loop
    1418             : 
    1419             :     } // ... end of the forward elimination loop.
    1420             : 
    1421             :     // Factor the last row of AInv by the current value of the last
    1422             :     // diagonal element of AMat. After this is done, all of the diagonal
    1423             :     // elements of AMat are unity and all of the elements in AMat left of
    1424             :     // the diagonal are zero.
    1425             : 
    1426       57810 :     for (ic = 1; ic <= this->rcmax; ++ic) {
    1427       54181 :         this->AInv(ic, this->rcmax) /= AMat1(this->rcmax, this->rcmax);
    1428             :     }
    1429        3629 :     AMat1(this->rcmax, this->rcmax) = 1.0;
    1430             : 
    1431             :     // Now, use back substitution to eliminate the elements to the right
    1432             :     // of the diagonal in AMat.  The procedure is similar to the forward
    1433             :     // elimination process except that we only have to operate on AInv,
    1434             :     // though now all of the columns of AInv may be non-zero.
    1435             : 
    1436             :     // This DO loop will cycle through the remaining rows which are not
    1437             :     // yet diagonalized in reverse order.  Note that the only effect on
    1438             :     // AMat is that the off-diagonal element is zeroed.  The diagonal
    1439             :     // (which has already been set to unity) is not effected by this row
    1440             :     // elimination process.
    1441             :     // In the following code ir is the column being zeroed and irr is the
    1442             :     // row being worked on
    1443             : 
    1444       54181 :     for (ir = this->rcmax; ir >= 2; --ir) { // Begin reverse elimination loop ...
    1445      594274 :         for (irr = 1; irr <= ir - 1; ++irr) {
    1446    26986608 :             for (ic = 1; ic <= this->rcmax; ++ic) {
    1447    26442886 :                 this->AInv(ic, irr) -= AMat1(ir, irr) * this->AInv(ic, ir);
    1448             :             }
    1449      543722 :             AMat1(ir, irr) = 0.0;
    1450             :         }
    1451             :     } // ... end of reverse elimination loop.
    1452             : 
    1453             :     // At this point, AMat1 is equal to the identity matrix (I)
    1454             :     // and AInv is equal to the inverse of AMat.
    1455             : 
    1456        3629 :     AMat1.deallocate();
    1457        3629 : }
    1458             : 
    1459        3629 : void ConstructionProps::calculateGammas()
    1460             : {
    1461             : 
    1462             :     // SUBROUTINE INFORMATION:
    1463             :     //       AUTHOR         Russ Taylor
    1464             :     //       DATE WRITTEN   June 1990
    1465             :     //       MODIFIED       na
    1466             :     //       RE-ENGINEERED  July 1996, RKS
    1467             : 
    1468             :     // PURPOSE OF THIS SUBROUTINE:
    1469             :     // Compute gammas as defined in Seem's dissertation.
    1470             :     // Runs as a subroutine of the conduction transfer
    1471             :     // function solver (InitializeCTFs).
    1472             : 
    1473             :     // METHODOLOGY EMPLOYED:
    1474             :     // Determine the Gamma1 and Gamma2 based on the results
    1475             :     // from the ExponMatrix and InvertMatrix subroutines.
    1476             :     // This routine is specialized to take advantage of the
    1477             :     // fact that most of BMat consists of zeroes.
    1478             : 
    1479             :     // REFERENCES:
    1480             :     // The state space method of calculating CTFs is
    1481             :     // outlined in the doctoral dissertation of John Seem,
    1482             :     // "Modeling of Heat Transfer in Buildings", Department
    1483             :     // of Mechanical Engineering, University of Wisconsin-
    1484             :     // Madison, 1987.
    1485             : 
    1486             :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    1487        7258 :     Array2D<Real64> ATemp; // Intermediate variable equal to AExp - I
    1488             :     int i;                 // Loop counter
    1489             :     int is1;               // Loop counter
    1490             :     int j;                 // Loop counter
    1491             :     int SurfNode;          // Loop counter
    1492             : 
    1493             :     // Compute Gamma1 from equation (2.1.12) in Seem's dissertation which
    1494             :     // states that:  Gamma1  =  [AInv] * ([AExp]-[I]) * [BMat]
    1495             :     // noting that BMat contains only the non-zero values of the B Matrix.
    1496             : 
    1497        3629 :     ATemp.allocate(this->rcmax, this->rcmax);
    1498        3629 :     ATemp = this->AExp - this->IdenMatrix;
    1499        3629 :     Gamma1 = 0.0;
    1500             : 
    1501       57810 :     for (i = 1; i <= this->rcmax; ++i) {
    1502             : 
    1503     1195806 :         for (is1 = 1; is1 <= this->rcmax; ++is1) {
    1504             : 
    1505     1141625 :             if (this->SolutionDimensions == 1) {
    1506      976789 :                 this->Gamma1(1, i) += this->AInv(is1, i) * ATemp(1, is1) * this->BMat(1);
    1507      976789 :                 this->Gamma1(2, i) += this->AInv(is1, i) * ATemp(this->rcmax, is1) * this->BMat(2);
    1508             :             } else { // SolutionDimensions = 2
    1509     1318688 :                 for (SurfNode = 1; SurfNode <= this->NumOfPerpendNodes; ++SurfNode) {
    1510     1153852 :                     this->Gamma1(1, i) += this->AInv(is1, i) * ATemp(SurfNode, is1) * this->BMat(1);
    1511     1153852 :                     this->Gamma1(2, i) += this->AInv(is1, i) * ATemp(this->rcmax + 1 - SurfNode, is1) * this->BMat(2);
    1512             :                 }
    1513             :             }
    1514             : 
    1515     1141625 :             if (this->NodeSource > 0) {
    1516      192113 :                 this->Gamma1(3, i) += this->AInv(is1, i) * ATemp(this->NodeSource, is1) * this->BMat(3);
    1517             :             }
    1518             :         }
    1519             :     }
    1520             : 
    1521        3629 :     ATemp.deallocate();
    1522             :     // Compute Gamma2 from equation (2.1.13) in Seem's dissertation which
    1523             :     // states that:  Gamma2  =  [AInv] * ([Gamma1]/delt - [BMat])
    1524             :     // again noting that BMat contains only the non-zero values of B.
    1525        3629 :     Gamma2 = 0.0;
    1526             : 
    1527       57810 :     for (i = 1; i <= this->rcmax; ++i) {
    1528             : 
    1529      216724 :         for (j = 1; j <= 3; ++j) {
    1530             : 
    1531     3587418 :             for (is1 = 1; is1 <= this->rcmax; ++is1) {
    1532             : 
    1533     3424875 :                 if (this->SolutionDimensions == 1) {
    1534     2930367 :                     if ((j == 1) && (is1 == 1)) {
    1535       53369 :                         this->Gamma2(j, i) += this->AInv(is1, i) * (this->Gamma1(j, is1) / this->CTFTimeStep - this->BMat(1));
    1536     2876998 :                     } else if ((j == 2) && (is1 == this->rcmax)) {
    1537       53369 :                         this->Gamma2(j, i) += this->AInv(is1, i) * (this->Gamma1(j, is1) / this->CTFTimeStep - this->BMat(2));
    1538     2823629 :                     } else if ((j == 3) && (is1 == this->NodeSource)) {
    1539         995 :                         this->Gamma2(j, i) += this->AInv(is1, i) * (this->Gamma1(j, is1) / this->CTFTimeStep - this->BMat(3));
    1540             :                     } else { // the element of the actual BMat is zero
    1541     2822634 :                         this->Gamma2(j, i) += this->AInv(is1, i) * (this->Gamma1(j, is1) / this->CTFTimeStep);
    1542             :                     }
    1543             :                 } else { // SolutionDimensions = 2
    1544      494508 :                     if ((j == 1) && ((is1 >= 1) && (is1 <= this->NumOfPerpendNodes))) {
    1545        5684 :                         this->Gamma2(j, i) += this->AInv(is1, i) * (this->Gamma1(j, is1) / this->CTFTimeStep - this->BMat(1));
    1546      488824 :                     } else if ((j == 2) && ((is1 <= this->rcmax) && (is1 >= this->rcmax + 1 - this->NumOfPerpendNodes))) {
    1547        5684 :                         this->Gamma2(j, i) += this->AInv(is1, i) * (this->Gamma1(j, is1) / this->CTFTimeStep - this->BMat(2));
    1548      483140 :                     } else if ((j == 3) && (is1 == this->NodeSource)) {
    1549         812 :                         this->Gamma2(j, i) += this->AInv(is1, i) * (this->Gamma1(j, is1) / this->CTFTimeStep - this->BMat(3));
    1550             :                     } else { // the element of the actual BMat is zero
    1551      482328 :                         this->Gamma2(j, i) += this->AInv(is1, i) * (this->Gamma1(j, is1) / this->CTFTimeStep);
    1552             :                     }
    1553             :                 }
    1554             :             }
    1555             :         }
    1556             :     }
    1557        3629 : }
    1558             : 
    1559        3629 : void ConstructionProps::calculateFinalCoefficients()
    1560             : {
    1561             : 
    1562             :     // SUBROUTINE INFORMATION:
    1563             :     //       AUTHOR         Russ Taylor
    1564             :     //       DATE WRITTEN   June 1990
    1565             :     //       MODIFIED       Apr 96, RKS, cosmetic, algorithm neutral changes
    1566             :     //       RE-ENGINEERED  July 1996, RKS; Nov 1999, LKL (allocatable arrays)
    1567             : 
    1568             :     // PURPOSE OF THIS SUBROUTINE:
    1569             :     // Subprogram to calculate the Sj and ej coefficients of Seem's
    1570             :     // dissertation.  Follows Seem's technique to compute the coefficients
    1571             :     // in order with minimum storage requirements.
    1572             : 
    1573             :     // METHODOLOGY EMPLOYED:
    1574             :     // Combine the results of the ExponMatrix, InvertMatrix, and
    1575             :     // CalculateGammas routines together to arrive at the temperature
    1576             :     // coefficients (s, s0) and the heat flux history coefficients (e) of
    1577             :     // the CTFs.  The outline of this subroutine is based on step 5 of
    1578             :     // Seem's suggested implementation of the state space method found on
    1579             :     // pages 26+27 of his dissertation.
    1580             : 
    1581             :     // REFERENCES:
    1582             :     // The state space method of calculating CTFs is outlined in the
    1583             :     // doctoral dissertation of John Seem, "Modeling of Heat Transfer in
    1584             :     // Buildings", Department of Mechanical Engineering, University of
    1585             :     // Wisconsin-Madison, 1987.  In particular, the equations used for
    1586             :     // these calculations are equations (2.1.24) through (2.1.26) in Seem's
    1587             :     // dissertation.
    1588             : 
    1589             :     // SUBROUTINE PARAMETER DEFINITIONS:
    1590        3629 :     constexpr Real64 ConvrgLim(1.0e-13); // Convergence limit (ratio) for cutting off the calculation of further
    1591             :     // CTFs.  This value was found to give suitable accuracy in IBLAST.
    1592             : 
    1593             :     Real64 avg;     // Intermediate calculation variable (average)
    1594             :     bool CTFConvrg; // Set after CTFs are calculated, based on whether there are
    1595             :     // too many CTFs terms
    1596             :     int i;                 // Loop counter
    1597             :     int ic;                // Loop counter
    1598             :     int inum;              // Loop counter
    1599             :     int ir;                // Loop counter
    1600             :     int is;                // Loop counter
    1601             :     int is2;               // Loop counter
    1602             :     int j;                 // Loop counter
    1603        7258 :     Array2D<Real64> PhiR0; // Product of Phi( = AExp) and R0 matrices from the state
    1604             :     // space method
    1605             :     Real64 rat; // Intermediate calculation variable (ratio of flux history
    1606             :     // terms)
    1607        7258 :     Array2D<Real64> Rnew; // Current R matrix
    1608        7258 :     Array2D<Real64> Rold; // R matrix from the last iteration
    1609             :     int SurfNode;         // Loop counter (for nodes at a surface)
    1610             :     Real64 SurfNodeFac;   // Multiplying factor applied to various surface nodes
    1611             :     Real64 trace;         // Trace of the product of Phi( = AExp) and R0
    1612             : 
    1613             :     // Subroutine initializations
    1614        3629 :     PhiR0.allocate(this->rcmax, this->rcmax);
    1615        3629 :     Rnew.allocate(this->rcmax, this->rcmax);
    1616        3629 :     Rold.allocate(this->rcmax, this->rcmax);
    1617        3629 :     PhiR0 = 0.0;
    1618        3629 :     Rold = 0.0;
    1619             : 
    1620        3629 :     this->s0 = 0.0;
    1621        3629 :     this->s = 0.0;
    1622        3629 :     this->e = 0.0;
    1623        3629 :     Rnew = this->IdenMatrix; // Rnew initialized to the identity matrix
    1624             : 
    1625             :     // Calculate Gamma1-Gamma2.  Gamma1 is not used by itself in the
    1626             :     // equations, only Gamma1-Gamma2.  Thus, reset Gamma1 to:
    1627             :     // Gamma1-Gamma2
    1628       57810 :     for (i = 1; i <= this->rcmax; ++i) {
    1629      216724 :         for (j = 1; j <= 3; ++j) {
    1630      162543 :             this->Gamma1(j, i) -= this->Gamma2(j, i);
    1631             :         }
    1632             :     }
    1633             : 
    1634             :     // Compute s0.  See Seem's thesis equation (2.1.24) which states that:
    1635             :     // s0  =  (CMat*R0*Gamma2) + (DMat)
    1636             :     // Note that for a two-dimensional solution, there is more than one
    1637             :     // node at the surface and the effect of each of these must be added
    1638             :     // together.
    1639        3629 :     if (this->SolutionDimensions == 1) {
    1640        3625 :         this->s0(1, 1) = this->CMat(1) * this->Gamma2(1, 1) + this->DMat(1);
    1641        3625 :         this->s0(2, 1) = this->CMat(1) * this->Gamma2(2, 1);
    1642        3625 :         this->s0(3, 1) = this->CMat(1) * this->Gamma2(3, 1);
    1643        3625 :         this->s0(1, 2) = this->CMat(2) * this->Gamma2(1, this->rcmax);
    1644        3625 :         this->s0(2, 2) = this->CMat(2) * this->Gamma2(2, this->rcmax) + this->DMat(2);
    1645        3625 :         this->s0(3, 2) = this->CMat(2) * this->Gamma2(3, this->rcmax);
    1646             :     } else { // SolutionDimensions = 2
    1647          32 :         for (SurfNode = 1; SurfNode <= this->NumOfPerpendNodes; ++SurfNode) {
    1648          28 :             if ((SurfNode == 1) || (SurfNode == this->NumOfPerpendNodes)) {
    1649           8 :                 SurfNodeFac = 0.5;
    1650             :             } else {
    1651          20 :                 SurfNodeFac = 1.0;
    1652             :             }
    1653          28 :             this->s0(1, 1) += SurfNodeFac * this->CMat(1) * this->Gamma2(1, SurfNode);
    1654          28 :             this->s0(2, 1) += SurfNodeFac * this->CMat(1) * this->Gamma2(2, SurfNode);
    1655          28 :             this->s0(3, 1) += SurfNodeFac * this->CMat(1) * this->Gamma2(3, SurfNode);
    1656          28 :             this->s0(1, 2) += SurfNodeFac * this->CMat(2) * this->Gamma2(1, this->rcmax + 1 - SurfNode);
    1657          28 :             this->s0(2, 2) += SurfNodeFac * this->CMat(2) * this->Gamma2(2, this->rcmax + 1 - SurfNode);
    1658          28 :             this->s0(3, 2) += SurfNodeFac * this->CMat(2) * this->Gamma2(3, this->rcmax + 1 - SurfNode);
    1659             :         }
    1660           4 :         this->s0(1, 1) += double(this->NumOfPerpendNodes - 1) * this->DMat(1);
    1661           4 :         this->s0(2, 2) += double(this->NumOfPerpendNodes - 1) * this->DMat(2);
    1662             :     }
    1663             : 
    1664        3629 :     if (this->NodeSource > 0) {
    1665          41 :         this->s0(1, 3) = this->Gamma2(1, this->NodeSource);
    1666          41 :         this->s0(2, 3) = this->Gamma2(2, this->NodeSource);
    1667          41 :         this->s0(3, 3) = this->Gamma2(3, this->NodeSource);
    1668             :     }
    1669        3629 :     if (this->NodeUserTemp > 0) {
    1670          41 :         this->s0(1, 4) = this->Gamma2(1, this->NodeUserTemp);
    1671          41 :         this->s0(2, 4) = this->Gamma2(2, this->NodeUserTemp);
    1672          41 :         this->s0(3, 4) = this->Gamma2(3, this->NodeUserTemp);
    1673             :     }
    1674             : 
    1675             :     // Check for and enforce symmetry in the cross term (Y)
    1676        3629 :     if (std::abs(this->s0(2, 1)) != std::abs(this->s0(1, 2))) {
    1677        3582 :         avg = (std::abs(this->s0(2, 1)) + std::abs(this->s0(1, 2))) * 0.5;
    1678        3582 :         this->s0(2, 1) *= avg / std::abs(this->s0(2, 1));
    1679        3582 :         this->s0(1, 2) *= avg / std::abs(this->s0(1, 2));
    1680             :     }
    1681             : 
    1682             :     // Compute S's and e's from 1 to n-1.  See equations (2.1.25) and
    1683             :     // (2.1.26) and Appendix C.
    1684        3629 :     inum = 1;          // Set history term counter
    1685        3629 :     CTFConvrg = false; // Set the convergence logical to false
    1686             : 
    1687             :     // The following DO WHILE loop calculates each successive set of time
    1688             :     // history terms until there are rcmax number of history terms or the
    1689             :     // latest flux history term is negligibly small compared to the first
    1690             :     // flux history term.
    1691       54669 :     while ((!CTFConvrg) && (inum < this->rcmax)) { // Begin CTF calculation loop ...
    1692             : 
    1693             :         // Compute e(inum) based on Appendix C (Seem's dissertation). First,
    1694             :         // compute the new PhiR0 and its trace.
    1695             : 
    1696       25520 :         trace = 0.0;
    1697             : 
    1698      470824 :         for (ir = 1; ir <= this->rcmax; ++ir) {
    1699             : 
    1700    11872204 :             for (ic = 1; ic <= this->rcmax; ++ic) {
    1701    11426900 :                 PhiR0(ic, ir) = 0.0;
    1702   762774034 :                 for (is = 1; is <= this->rcmax; ++is) {
    1703             :                     // Make sure the next term won't cause an underflow.  If it will end up being
    1704             :                     // so small as to go below TinyLimit, then ignore it since it won't add anything
    1705             :                     // to PhiR0 anyway.
    1706   751347134 :                     if (std::abs(Rnew(ic, is)) > DataGlobalConstants::rTinyValue) {
    1707   679524962 :                         if (std::abs(this->AExp(is, ir)) > std::abs(DataGlobalConstants::rTinyValue / Rnew(ic, is)))
    1708   545468111 :                             PhiR0(ic, ir) += this->AExp(is, ir) * Rnew(ic, is);
    1709             :                     }
    1710             :                 }
    1711             :             }
    1712             : 
    1713      445304 :             trace += PhiR0(ir, ir);
    1714             :         }
    1715             : 
    1716             :         // Now calculate ej from the trace.  According to Appendix C:
    1717             :         // e(j) = -Trace[AExp*R(j-1)]/j
    1718             : 
    1719       25520 :         this->e(inum) = -trace / double(inum);
    1720             : 
    1721             :         // Update Rold and compute Rnew.  Note:  PhiR0 = AExp*R(j-1) here.
    1722             :         // According to Appendix C:  R(j) = AExp*R(j-1) + e(j-1)
    1723             : 
    1724      470824 :         for (ir = 1; ir <= this->rcmax; ++ir) {
    1725    11872204 :             for (ic = 1; ic <= this->rcmax; ++ic) {
    1726    11426900 :                 Rold(ic, ir) = Rnew(ic, ir);
    1727    11426900 :                 Rnew(ic, ir) = PhiR0(ic, ir);
    1728             :             }
    1729      445304 :             Rnew(ir, ir) += this->e(inum);
    1730             :         }
    1731             : 
    1732             :         // Compute S(inum) based on eq.(2.1.25) which states:
    1733             :         // S(j)  =  CMat*[R(j-1)*(Gamma1-Gamma2)+R(j)*Gamma2]
    1734             :         //          + e(j)*DMat
    1735       25520 :         if (this->SolutionDimensions == 1) {
    1736      101816 :             for (j = 1; j <= 3; ++j) {
    1737     1372080 :                 for (is2 = 1; is2 <= this->rcmax; ++is2) {
    1738     1295718 :                     this->s(j, 1, inum) += this->CMat(1) * (Rold(is2, 1) * this->Gamma1(j, is2) + Rnew(is2, 1) * this->Gamma2(j, is2));
    1739     1295718 :                     this->s(j, 2, inum) +=
    1740     1295718 :                         this->CMat(2) * (Rold(is2, this->rcmax) * this->Gamma1(j, is2) + Rnew(is2, this->rcmax) * this->Gamma2(j, is2));
    1741     1295718 :                     if (this->NodeSource > 0) {
    1742       29181 :                         this->s(j, 3, inum) +=
    1743       29181 :                             (Rold(is2, this->NodeSource) * this->Gamma1(j, is2) + Rnew(is2, this->NodeSource) * this->Gamma2(j, is2));
    1744             :                     }
    1745     1295718 :                     if (this->NodeUserTemp > 0) {
    1746       29181 :                         this->s(j, 4, inum) +=
    1747       29181 :                             (Rold(is2, this->NodeUserTemp) * this->Gamma1(j, is2) + Rnew(is2, this->NodeUserTemp) * this->Gamma2(j, is2));
    1748             :                     }
    1749             :                 }
    1750       76362 :                 if (j != 3) this->s(j, j, inum) += this->e(inum) * this->DMat(j);
    1751             :             }
    1752             :         } else { // SolutionDimensions = 2
    1753         264 :             for (j = 1; j <= 3; ++j) {
    1754       40392 :                 for (is2 = 1; is2 <= this->rcmax; ++is2) {
    1755      321552 :                     for (SurfNode = 1; SurfNode <= this->NumOfPerpendNodes; ++SurfNode) {
    1756      281358 :                         if ((SurfNode == 1) || (SurfNode == this->NumOfPerpendNodes)) {
    1757       80388 :                             SurfNodeFac = 0.5;
    1758             :                         } else {
    1759      200970 :                             SurfNodeFac = 1.0;
    1760             :                         }
    1761      281358 :                         this->s(j, 1, inum) +=
    1762      281358 :                             SurfNodeFac * this->CMat(1) * (Rold(is2, SurfNode) * this->Gamma1(j, is2) + Rnew(is2, SurfNode) * this->Gamma2(j, is2));
    1763      562716 :                         this->s(j, 2, inum) += SurfNodeFac * this->CMat(2) *
    1764      562716 :                                                (Rold(is2, this->rcmax + 1 - SurfNode) * this->Gamma1(j, is2) +
    1765      281358 :                                                 Rnew(is2, this->rcmax + 1 - SurfNode) * this->Gamma2(j, is2));
    1766             :                     }
    1767       40194 :                     if (this->NodeSource > 0) {
    1768       40194 :                         this->s(j, 3, inum) +=
    1769       40194 :                             (Rold(is2, this->NodeSource) * this->Gamma1(j, is2) + Rnew(is2, this->NodeSource) * this->Gamma2(j, is2));
    1770             :                     }
    1771       40194 :                     if (this->NodeUserTemp > 0) {
    1772       40194 :                         this->s(j, 4, inum) +=
    1773       40194 :                             (Rold(is2, this->NodeUserTemp) * this->Gamma1(j, is2) + Rnew(is2, this->NodeUserTemp) * this->Gamma2(j, is2));
    1774             :                     }
    1775             :                 }
    1776             :             }
    1777          66 :             this->s(1, 1, inum) += this->e(inum) * this->DMat(1) * double(this->NumOfPerpendNodes - 1);
    1778          66 :             this->s(2, 2, inum) += this->e(inum) * this->DMat(2) * double(this->NumOfPerpendNodes - 1);
    1779             :         }
    1780             : 
    1781             :         // Check for and enforce symmetry in the cross term (Y)
    1782       25520 :         if (std::abs(s(2, 1, inum)) != std::abs(s(1, 2, inum))) {
    1783       25452 :             avg = (std::abs(s(2, 1, inum)) + std::abs(s(1, 2, inum))) * 0.5;
    1784       25452 :             this->s(2, 1, inum) *= avg / std::abs(s(2, 1, inum));
    1785       25452 :             this->s(1, 2, inum) *= avg / std::abs(s(1, 2, inum));
    1786             :         }
    1787             : 
    1788             :         // Check for convergence of the CTFs.
    1789       25520 :         if (e(1) == 0.0) {
    1790             : 
    1791           0 :             this->NumCTFTerms = 1; // e(1) is zero, so there are no history terms.
    1792           0 :             CTFConvrg = true;      // CTF calculations have converged--set logical.
    1793             : 
    1794             :         } else {
    1795             :             // e(1) is non-zero -- Calculate and compare the ratio of the flux
    1796             :             // terms to the convergence limit.
    1797       25520 :             rat = std::abs(e(inum) / this->e(1));
    1798             : 
    1799       25520 :             if (rat < ConvrgLim) {
    1800             : 
    1801             :                 // If the ratio is less than the convergence limit, then any other
    1802             :                 // terms would have a neglible impact on the CTF-based energy balances.
    1803        2902 :                 this->NumCTFTerms = inum;
    1804        2902 :                 CTFConvrg = true; // CTF calculations have converged--set logical.
    1805             :             }
    1806             :         } // ... end of convergence check block.
    1807             : 
    1808       25520 :         ++inum;
    1809             : 
    1810             :     } // ... end of CTF calculation loop.
    1811             :     // Continue to the next coefficient if the solution has not converged
    1812        3629 :     if (!CTFConvrg) { // Compute last e and S, if still unconverged.
    1813             : 
    1814             :         // Compute e(inum) based on Appendix C (Seem's dissertation) or see
    1815             :         // equation above.  First compute the new PhiR0 and its trace.
    1816             : 
    1817         727 :         trace = 0.0;
    1818             : 
    1819        4919 :         for (ir = 1; ir <= this->rcmax; ++ir) {
    1820       31438 :             for (is = 1; is <= this->rcmax; ++is) {
    1821       27246 :                 trace += this->AExp(is, ir) * Rnew(ir, is);
    1822             :             }
    1823             :         }
    1824             : 
    1825         727 :         this->e(this->rcmax) = -trace / double(this->rcmax); // Now calculate ej from the trace.
    1826             : 
    1827             :         // Compute S(inum) based on eq.(2.1.25) which states:
    1828             :         //   S(last) = CMat*R(last-1)*(Gamma1-Gamma2)+e(last)*DMat
    1829             : 
    1830         727 :         if (this->SolutionDimensions == 1) {
    1831        2908 :             for (j = 1; j <= 3; ++j) {
    1832       14757 :                 for (is2 = 1; is2 <= this->rcmax; ++is2) {
    1833       12576 :                     this->s(j, 1, this->rcmax) += this->CMat(1) * Rnew(is2, 1) * this->Gamma1(j, is2);
    1834       12576 :                     this->s(j, 2, this->rcmax) += this->CMat(2) * Rnew(is2, this->rcmax) * this->Gamma1(j, is2);
    1835       12576 :                     if (this->NodeSource > 0) {
    1836           0 :                         this->s(j, 3, this->rcmax) += Rnew(is2, this->NodeSource) * this->Gamma1(j, is2);
    1837             :                     }
    1838       12576 :                     if (this->NodeUserTemp > 0) {
    1839           0 :                         this->s(j, 4, this->rcmax) += Rnew(is2, this->NodeUserTemp) * this->Gamma1(j, is2);
    1840             :                     }
    1841             :                 }
    1842             :             }
    1843         727 :             this->s(1, 1, this->rcmax) += this->e(this->rcmax) * this->DMat(1);
    1844         727 :             this->s(2, 2, this->rcmax) += this->e(this->rcmax) * this->DMat(2);
    1845         727 :             this->NumCTFTerms = this->rcmax;
    1846             :         } else { // SolutionDimensions = 2
    1847           0 :             for (j = 1; j <= 3; ++j) {
    1848           0 :                 for (is2 = 1; is2 <= this->rcmax; ++is2) {
    1849           0 :                     for (SurfNode = 1; SurfNode <= this->NumOfPerpendNodes; ++SurfNode) {
    1850           0 :                         if ((SurfNode == 1) || (SurfNode == this->NumOfPerpendNodes)) {
    1851           0 :                             SurfNodeFac = 0.5;
    1852             :                         } else {
    1853           0 :                             SurfNodeFac = 1.0;
    1854             :                         }
    1855           0 :                         this->s(j, 1, this->rcmax) += SurfNodeFac * this->CMat(1) * Rnew(is2, SurfNode) * this->Gamma1(j, is2);
    1856           0 :                         this->s(j, 2, this->rcmax) += SurfNodeFac * this->CMat(2) * Rnew(is2, this->rcmax + 1 - SurfNode) * this->Gamma1(j, is2);
    1857             :                     }
    1858           0 :                     if (this->NodeSource > 0) {
    1859           0 :                         this->s(j, 3, this->rcmax) += Rnew(is2, this->NodeSource) * this->Gamma1(j, is2);
    1860             :                     }
    1861           0 :                     if (this->NodeUserTemp > 0) {
    1862           0 :                         this->s(j, 4, this->rcmax) += Rnew(is2, this->NodeUserTemp) * this->Gamma1(j, is2);
    1863             :                     }
    1864             :                 }
    1865             :             }
    1866           0 :             this->s(1, 1, this->rcmax) += this->e(this->rcmax) * this->DMat(1) * double(this->NumOfPerpendNodes - 1);
    1867           0 :             this->s(2, 2, this->rcmax) += this->e(this->rcmax) * this->DMat(2) * double(this->NumOfPerpendNodes - 1);
    1868             :         }
    1869             : 
    1870             :         // Check for and enforce symmetry in the cross term (Y)
    1871             : 
    1872         727 :         if (std::abs(s(2, 1, this->rcmax)) != std::abs(s(1, 2, this->rcmax))) {
    1873         727 :             avg = (std::abs(s(2, 1, this->rcmax)) + std::abs(s(1, 2, this->rcmax))) * 0.5;
    1874         727 :             this->s(2, 1, this->rcmax) *= avg / std::abs(s(2, 1, this->rcmax));
    1875         727 :             this->s(1, 2, this->rcmax) *= avg / std::abs(s(1, 2, this->rcmax));
    1876             :         }
    1877             : 
    1878             :     } // ... end of IF block for calculation of last e and S.
    1879             : 
    1880        3629 :     PhiR0.deallocate();
    1881        3629 :     Rnew.deallocate();
    1882        3629 :     Rold.deallocate();
    1883        3629 : }
    1884             : 
    1885        1819 : void ConstructionProps::reportTransferFunction(EnergyPlusData &state, int const cCounter)
    1886             : {
    1887             : 
    1888             :     static constexpr std::string_view Format_700{" Construction CTF,{},{:4},{:4},{:4},{:8.3F},{:15.4N},{:8.3F},{:8.3F},{:8.3F},{:8.3F},{}\n"};
    1889        1819 :     print(state.files.eio,
    1890             :           Format_700,
    1891             :           this->Name,
    1892             :           cCounter,
    1893             :           this->TotLayers,
    1894             :           this->NumCTFTerms,
    1895             :           this->CTFTimeStep,
    1896             :           this->UValue,
    1897             :           this->OutsideAbsorpThermal,
    1898             :           this->InsideAbsorpThermal,
    1899             :           this->OutsideAbsorpSolar,
    1900             :           this->InsideAbsorpSolar,
    1901        3638 :           DataHeatBalance::DisplayMaterialRoughness(this->OutsideRoughness));
    1902             : 
    1903        6093 :     for (int I = 1; I <= this->TotLayers; ++I) {
    1904        4274 :         int Layer = this->LayerPoint(I);
    1905        4274 :         switch (state.dataMaterial->Material(Layer).Group) {
    1906         123 :         case DataHeatBalance::MaterialGroup::Air: {
    1907             :             static constexpr std::string_view Format_702(" Material:Air,{},{:12.4N}\n");
    1908         123 :             print(state.files.eio, Format_702, state.dataMaterial->Material(Layer).Name, state.dataMaterial->Material(Layer).Resistance);
    1909         123 :         } break;
    1910        4151 :         default: {
    1911             :             static constexpr std::string_view Format_701(" Material CTF Summary,{},{:8.4F},{:14.3F},{:11.3F},{:13.3F},{:12.4N}\n");
    1912        4151 :             Material::MaterialProperties &mp = state.dataMaterial->Material(Layer);
    1913        4151 :             print(state.files.eio, Format_701, mp.Name, mp.Thickness, mp.Conductivity, mp.Density, mp.SpecHeat, mp.Resistance);
    1914        4151 :         } break;
    1915             :         }
    1916             :     }
    1917             : 
    1918       16374 :     for (int I = this->NumCTFTerms; I >= 0; --I) {
    1919       14555 :         if (I != 0) {
    1920             :             static constexpr std::string_view Format_703(" CTF,{:4},{:20.8N},{:20.8N},{:20.8N},{:20.8N}\n");
    1921       12736 :             print(state.files.eio, Format_703, I, this->CTFOutside(I), this->CTFCross(I), this->CTFInside(I), this->CTFFlux(I));
    1922             :         } else {
    1923             :             static constexpr std::string_view Format_704(" CTF,{:4},{:20.8N},{:20.8N},{:20.8N}\n");
    1924        1819 :             print(state.files.eio, Format_704, I, this->CTFOutside(I), this->CTFCross(I), this->CTFInside(I));
    1925             :         }
    1926             :     }
    1927             : 
    1928        1819 :     if (this->SourceSinkPresent) {
    1929             :         // QTFs...
    1930           0 :         for (int I = this->NumCTFTerms; I >= 0; --I) {
    1931             :             static constexpr std::string_view Format_705(" QTF,{:4},{:20.8N},{:20.8N}\n");
    1932           0 :             print(state.files.eio, Format_705, I, this->CTFSourceOut(I), this->CTFSourceIn(I));
    1933             :         }
    1934             :         // QTFs for source/sink location temperature calculation...
    1935           0 :         for (int I = this->NumCTFTerms; I >= 0; --I) {
    1936             :             static constexpr std::string_view Format_706(" Source/Sink Loc Internal Temp QTF,{:4},{:20.8N},{:20.8N},{:20.8N}\n");
    1937           0 :             print(state.files.eio, Format_706, I, this->CTFTSourceOut(I), this->CTFTSourceIn(I), this->CTFTSourceQ(I));
    1938             :         }
    1939           0 :         if (this->TempAfterLayer != 0) {
    1940             :             // QTFs for user specified interior temperature calculation...
    1941           0 :             for (int I = this->NumCTFTerms; I >= 0; --I) {
    1942             :                 static constexpr std::string_view Format_707(" User Loc Internal Temp QTF,{:4},{:20.8N},{:20.8N},{:20.8N}\n");
    1943           0 :                 print(state.files.eio, Format_707, I, this->CTFTUserOut(I), this->CTFTUserIn(I), this->CTFTUserSource(I));
    1944             :             }
    1945             :         }
    1946             :     }
    1947        1819 : }
    1948             : 
    1949          20 : bool ConstructionProps::isGlazingConstruction(EnergyPlusData &state) const
    1950             : {
    1951             :     // SUBROUTINE INFORMATION:
    1952             :     //       AUTHOR         Simon Vidanovic
    1953             :     //       DATE WRITTEN   September 2016
    1954             :     //       MODIFIED       na
    1955             :     //       RE-ENGINEERED  na
    1956             : 
    1957             :     // PURPOSE OF THIS SUBROUTINE:
    1958             :     // Commonly used routine in several places in EnergyPlus which examines if current
    1959             :     // construction is glazing construction
    1960          20 :     DataHeatBalance::MaterialGroup const MaterialGroup = state.dataMaterial->Material(LayerPoint(1)).Group;
    1961          20 :     return BITF_TEST_ANY(BITF(MaterialGroup),
    1962             :                          BITF(DataHeatBalance::MaterialGroup::WindowGlass) | BITF(DataHeatBalance::MaterialGroup::Shade) |
    1963             :                              BITF(DataHeatBalance::MaterialGroup::Screen) | BITF(DataHeatBalance::MaterialGroup::WindowBlind) |
    1964             :                              BITF(DataHeatBalance::MaterialGroup::WindowSimpleGlazing));
    1965             : }
    1966             : 
    1967          44 : Real64 ConstructionProps::setThicknessPerpendicular(EnergyPlusData &state, Real64 userValue)
    1968             : {
    1969             :     // Limits set here are similar to limits used in HydronicSystemBaseData::sizeRadiantSystemTubeLength
    1970             :     // which is for autosizing tube length.  The limits are not as strictly enforced here because they are
    1971             :     // not being used for autosizing so more latitude with the values is desired.  Warnings will still alert
    1972             :     // the user if values seem outside of the ordinary range anticipated.
    1973          44 :     Real64 returnValue = userValue / 2.0; // Divide by two because internally the half distance will be used to calculate CTFs
    1974          44 :     if (returnValue <= 0.001) {           // lowest reasonable value for "half" the tube spacing in meters
    1975           0 :         ShowWarningError(state, "ConstructionProperty:InternalHeatSource has a tube spacing that is less than 2 mm.  This is not allowed.");
    1976           0 :         ShowContinueError(
    1977           0 :             state, "Construction=" + this->Name + " has this problem.  The tube spacing has been reset to 0.15m (~6 inches) for this construction.");
    1978           0 :         ShowContinueError(state, "As per the Input Output Reference, tube spacing is only used for 2-D solutions and autosizing.");
    1979           0 :         returnValue = 0.075;          // default "half" tube spacing in meters (roughly equivalent to 15cm or 6 inches of tube spacing)
    1980          44 :     } else if (returnValue < 0.005) { // below this value for "half" the tube spacing in meters throw a warning
    1981           0 :         ShowWarningError(state, "ConstructionProperty:InternalHeatSource has a tube spacing that is less than 1 cm (0.4 inch).");
    1982           0 :         ShowContinueError(state, "Construction=" + this->Name + " has this concern.  Please check this construction to make sure it is correct.");
    1983           0 :         ShowContinueError(state, "As per the Input Output Reference, tube spacing is only used for 2-D solutions and autosizing.");
    1984          44 :     } else if (returnValue > 0.5) { // above this value for "half" the tube spacing in meters throw a warning
    1985           0 :         ShowWarningError(state, "ConstructionProperty:InternalHeatSource has a tube spacing that is greater than 1 meter (39.4 inches).");
    1986           0 :         ShowContinueError(state, "Construction=" + this->Name + " has this concern.  Please check this construction to make sure it is correct.");
    1987           0 :         ShowContinueError(state, "As per the Input Output Reference, tube spacing is only used for 2-D solutions and autosizing.");
    1988             :     }
    1989          44 :     return returnValue;
    1990             : }
    1991             : 
    1992          44 : Real64 ConstructionProps::setUserTemperatureLocationPerpendicular(EnergyPlusData &state, Real64 userValue)
    1993             : {
    1994          44 :     if (userValue < 0.0) {
    1995           0 :         ShowWarningError(state, "ConstructionProperty:InternalHeatSource has a perpendicular temperature location parameter that is less than zero.");
    1996           0 :         ShowContinueError(state, "Construction=" + this->Name + " has this error.  The parameter has been reset to 0.");
    1997           0 :         return 0.0;
    1998          44 :     } else if (userValue > 1.0) {
    1999           0 :         ShowWarningError(state,
    2000             :                          "ConstructionProperty:InternalHeatSource has a perpendicular temperature location parameter that is greater than one.");
    2001           0 :         ShowContinueError(state, "Construction=" + this->Name + " has this error.  The parameter has been reset to 1.");
    2002           0 :         return 1.0;
    2003             :     } else { // Valid value between 0 and 1
    2004          44 :         return userValue;
    2005             :     }
    2006             : }
    2007             : 
    2008        3532 : void ConstructionProps::setNodeSourceAndUserTemp(Array1D_int &Nodes)
    2009             : {
    2010        3532 :     this->NodeSource = 0;
    2011        3532 :     this->NodeUserTemp = 0;
    2012        3532 :     if (!this->SourceSinkPresent) return;
    2013             : 
    2014         164 :     for (int Layer = 1; Layer <= this->SourceAfterLayer; ++Layer) {
    2015         125 :         this->NodeSource += Nodes(Layer);
    2016             :     }
    2017             : 
    2018          39 :     if ((this->NodeSource > 0) && (this->SolutionDimensions > 1)) this->NodeSource = (this->NodeSource - 1) * this->NumOfPerpendNodes + 1;
    2019             : 
    2020         165 :     for (int Layer = 1; Layer <= this->TempAfterLayer; ++Layer) {
    2021         126 :         this->NodeUserTemp += Nodes(Layer);
    2022             :     }
    2023             : 
    2024          39 :     if ((this->NodeUserTemp > 0) && (this->SolutionDimensions > 1))
    2025           6 :         this->NodeUserTemp = (this->NodeUserTemp - 1) * this->NumOfPerpendNodes +
    2026           4 :                              round(this->userTemperatureLocationPerpendicular * (this->NumOfPerpendNodes - 1)) + 1;
    2027             : }
    2028             : 
    2029        5845 : void ConstructionProps::setArraysBasedOnMaxSolidWinLayers(EnergyPlusData &state)
    2030             : {
    2031        5845 :     this->AbsDiff.allocate(state.dataHeatBal->MaxSolidWinLayers);
    2032        5845 :     this->AbsDiffBack.allocate(state.dataHeatBal->MaxSolidWinLayers);
    2033        5845 :     this->BlAbsDiff.allocate(DataSurfaces::MaxSlatAngs, state.dataHeatBal->MaxSolidWinLayers);
    2034        5845 :     this->BlAbsDiffGnd.allocate(DataSurfaces::MaxSlatAngs, state.dataHeatBal->MaxSolidWinLayers);
    2035        5845 :     this->BlAbsDiffSky.allocate(DataSurfaces::MaxSlatAngs, state.dataHeatBal->MaxSolidWinLayers);
    2036        5845 :     this->BlAbsDiffBack.allocate(DataSurfaces::MaxSlatAngs, state.dataHeatBal->MaxSolidWinLayers);
    2037        5845 :     this->AbsBeamCoef.allocate(state.dataHeatBal->MaxSolidWinLayers);
    2038        5845 :     this->AbsBeamBackCoef.allocate(state.dataHeatBal->MaxSolidWinLayers);
    2039        5845 :     this->tBareSolCoef.allocate(state.dataHeatBal->MaxSolidWinLayers);
    2040        5845 :     this->tBareVisCoef.allocate(state.dataHeatBal->MaxSolidWinLayers);
    2041        5845 :     this->rfBareSolCoef.allocate(state.dataHeatBal->MaxSolidWinLayers);
    2042        5845 :     this->rfBareVisCoef.allocate(state.dataHeatBal->MaxSolidWinLayers);
    2043        5845 :     this->rbBareSolCoef.allocate(state.dataHeatBal->MaxSolidWinLayers);
    2044        5845 :     this->rbBareVisCoef.allocate(state.dataHeatBal->MaxSolidWinLayers);
    2045        5845 :     this->afBareSolCoef.allocate(state.dataHeatBal->MaxSolidWinLayers);
    2046        5845 :     this->abBareSolCoef.allocate(state.dataHeatBal->MaxSolidWinLayers);
    2047       46989 :     for (int Layer = 1; Layer <= state.dataHeatBal->MaxSolidWinLayers; ++Layer) {
    2048       41144 :         this->AbsBeamCoef(Layer).allocate(DataSurfaces::MaxPolyCoeff);
    2049       41144 :         this->AbsBeamBackCoef(Layer).allocate(DataSurfaces::MaxPolyCoeff);
    2050       41144 :         this->tBareSolCoef(Layer).allocate(DataSurfaces::MaxPolyCoeff);
    2051       41144 :         this->tBareVisCoef(Layer).allocate(DataSurfaces::MaxPolyCoeff);
    2052       41144 :         this->rfBareSolCoef(Layer).allocate(DataSurfaces::MaxPolyCoeff);
    2053       41144 :         this->rfBareVisCoef(Layer).allocate(DataSurfaces::MaxPolyCoeff);
    2054       41144 :         this->rbBareSolCoef(Layer).allocate(DataSurfaces::MaxPolyCoeff);
    2055       41144 :         this->rbBareVisCoef(Layer).allocate(DataSurfaces::MaxPolyCoeff);
    2056       41144 :         this->afBareSolCoef(Layer).allocate(DataSurfaces::MaxPolyCoeff);
    2057       41144 :         this->abBareSolCoef(Layer).allocate(DataSurfaces::MaxPolyCoeff);
    2058             :     }
    2059             : 
    2060       46989 :     for (int Layer = 1; Layer <= state.dataHeatBal->MaxSolidWinLayers; ++Layer) {
    2061       41144 :         this->AbsDiff(Layer) = 0.0;
    2062       41144 :         this->AbsDiffBack(Layer) = 0.0;
    2063             :     }
    2064      116900 :     for (int Index = 1; Index <= DataSurfaces::MaxSlatAngs; ++Index) {
    2065      892791 :         for (int Layer = 1; Layer <= state.dataHeatBal->MaxSolidWinLayers; ++Layer) {
    2066      781736 :             this->BlAbsDiff(Index, Layer) = 0.0;
    2067      781736 :             this->BlAbsDiffGnd(Index, Layer) = 0.0;
    2068      781736 :             this->BlAbsDiffSky(Index, Layer) = 0.0;
    2069      781736 :             this->BlAbsDiffBack(Index, Layer) = 0.0;
    2070             :         }
    2071             :     }
    2072       46989 :     for (int Layer = 1; Layer <= state.dataHeatBal->MaxSolidWinLayers; ++Layer) {
    2073      288008 :         for (int Index = 1; Index <= DataSurfaces::MaxPolyCoeff; ++Index) {
    2074      246864 :             this->AbsBeamCoef(Layer)(Index) = 0.0;
    2075      246864 :             this->AbsBeamBackCoef(Layer)(Index) = 0.0;
    2076      246864 :             this->tBareSolCoef(Layer)(Index) = 0.0;
    2077      246864 :             this->tBareVisCoef(Layer)(Index) = 0.0;
    2078      246864 :             this->rfBareSolCoef(Layer)(Index) = 0.0;
    2079      246864 :             this->rfBareVisCoef(Layer)(Index) = 0.0;
    2080      246864 :             this->rbBareSolCoef(Layer)(Index) = 0.0;
    2081      246864 :             this->rbBareVisCoef(Layer)(Index) = 0.0;
    2082      246864 :             this->afBareSolCoef(Layer)(Index) = 0.0;
    2083      246864 :             this->abBareSolCoef(Layer)(Index) = 0.0;
    2084             :         }
    2085             :     }
    2086        5845 : }
    2087             : 
    2088        2313 : } // namespace EnergyPlus::Construction

Generated by: LCOV version 1.13