LCOV - code coverage report
Current view: top level - EnergyPlus - WindowComplexManager.cc (source / functions) Hit Total Coverage
Test: lcov.output.filtered Lines: 1228 1644 74.7 %
Date: 2023-01-17 19:17:23 Functions: 26 28 92.9 %

          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             : // C++ Headers
      49             : #include <cassert>
      50             : #include <cmath>
      51             : #include <cstdint>
      52             : 
      53             : // ObjexxFCL Headers
      54             : #include <ObjexxFCL/Array.functions.hh>
      55             : #include <ObjexxFCL/ArrayS.functions.hh>
      56             : #include <ObjexxFCL/Fmath.hh>
      57             : 
      58             : // EnergyPlus Headers
      59             : #include <EnergyPlus/Construction.hh>
      60             : #include <EnergyPlus/Data/EnergyPlusData.hh>
      61             : #include <EnergyPlus/DataComplexFenestration.hh>
      62             : #include <EnergyPlus/DataEnvironment.hh>
      63             : #include <EnergyPlus/DataHeatBalSurface.hh>
      64             : #include <EnergyPlus/DataHeatBalance.hh>
      65             : #include <EnergyPlus/DataLoopNode.hh>
      66             : #include <EnergyPlus/DataShadowingCombinations.hh>
      67             : #include <EnergyPlus/DataSurfaces.hh>
      68             : #include <EnergyPlus/DataSystemVariables.hh>
      69             : #include <EnergyPlus/DataZoneEquipment.hh>
      70             : #include <EnergyPlus/HeatBalanceSurfaceManager.hh>
      71             : #include <EnergyPlus/Material.hh>
      72             : #include <EnergyPlus/PierceSurface.hh>
      73             : #include <EnergyPlus/Psychrometrics.hh>
      74             : #include <EnergyPlus/ScheduleManager.hh>
      75             : #include <EnergyPlus/TARCOGGassesParams.hh>
      76             : #include <EnergyPlus/TARCOGMain.hh>
      77             : #include <EnergyPlus/TARCOGParams.hh>
      78             : #include <EnergyPlus/UtilityRoutines.hh>
      79             : #include <EnergyPlus/Vectors.hh>
      80             : #include <EnergyPlus/WindowComplexManager.hh>
      81             : #include <EnergyPlus/ZoneTempPredictorCorrector.hh>
      82             : 
      83             : namespace EnergyPlus {
      84             : 
      85             : namespace WindowComplexManager {
      86             : 
      87             :     // Module containing the routines dealing with complex fenestration
      88             : 
      89             :     // MODULE INFORMATION:
      90             :     //       AUTHOR         Joe Klems
      91             :     //       DATE WRITTEN   ???
      92             :     //       MODIFIED       November 2011, Simon Vidanovic
      93             :     //       RE-ENGINEERED  na
      94             : 
      95             :     // PURPOSE OF THIS MODULE:
      96             :     //  Initialize data for solar and thermal calculations and also performs thermal calculations for BSDF window
      97             : 
      98             :     using namespace DataComplexFenestration;
      99             :     using namespace DataVectorTypes;
     100             :     using namespace DataBSDFWindow;
     101             :     using namespace DataSurfaces; // , ONLY: TotSurfaces,TotWindows,Surface,SurfaceWindow   !update this later
     102             :     using namespace DataHeatBalance;
     103             :     using namespace DataShadowingCombinations;
     104             :     using namespace Vectors;
     105             : 
     106             :     // Parameters for gas definitions
     107             :     enum class GasCoeffs
     108             :     {
     109             :         Invalid = -1,
     110             :         Custom,
     111             :         Air,
     112             :         Argon,
     113             :         Krypton,
     114             :         Xenon,
     115             :         Num
     116             :     };
     117             : 
     118         771 :     void InitBSDFWindows(EnergyPlusData &state)
     119             :     {
     120             : 
     121             :         // SUBROUTINE INFORMATION:
     122             :         //       AUTHOR         Joe Klems
     123             :         //       DATE WRITTEN   August 2011
     124             :         //       MODIFIED       na
     125             :         //       RE-ENGINEERED  na
     126             : 
     127             :         // PURPOSE OF THIS SUBROUTINE:
     128             :         // Set up the overall optical geometry for a BSDF window
     129             : 
     130             :         using namespace Vectors;
     131             : 
     132             :         int BaseSurf;           // base surface number (used in finding back surface)
     133             :         int NumStates;          // Local variable for the number of states
     134         781 :         Array1D<Real64> Thetas; // temp array holding theta values
     135         781 :         Array1D_int NPhis;      // temp array holding number of phis for a given theta
     136         781 :         Array1D<Real64> V(3);   // vector array
     137             :         Real64 VLen;            // Length of vector array
     138             :         int NHold;              // No. values in the Temporary array
     139             : 
     140             :         struct TempBasisIdx
     141             :         {
     142             :             // Members
     143             :             int Basis; // Basis no in basis table
     144             :             int State; // State in which basis first occurs
     145             : 
     146             :             // Default Constructor
     147           0 :             TempBasisIdx() : Basis(0), State(0)
     148             :             {
     149           0 :             }
     150             :         };
     151             : 
     152             :         // Object Data
     153         781 :         Array1D<TempBasisIdx> IHold; // Temporary array
     154             : 
     155         771 :         if (state.dataBSDFWindow->TotComplexFenStates <= 0) return; // Nothing to do if no complex fenestration states
     156             :         // Construct Basis List
     157          10 :         state.dataWindowComplexManager->BasisList.allocate(state.dataBSDFWindow->TotComplexFenStates);
     158             : 
     159             :         // Note:  Construction of the basis list contains the assumption of identical incoming and outgoing bases in
     160             :         //            that the complex fenestration state definition contains only one basis description, hence
     161             :         //            assumes square property matrices.  If this assumption were relaxed through change of the
     162             :         //            definition or additional definition of a state type with non-square matrices, then the loop
     163             :         //            below should be modified to enter both of the bases into the basis list.
     164             : 
     165          24 :         for (int IConst = state.dataBSDFWindow->FirstBSDF; IConst <= state.dataBSDFWindow->FirstBSDF + state.dataBSDFWindow->TotComplexFenStates - 1;
     166             :              ++IConst) {
     167          14 :             state.dataWindowComplexManager->MatrixNo = state.dataConstruction->Construct(IConst).BSDFInput.BasisMatIndex;
     168          14 :             if (state.dataWindowComplexManager->NumBasis == 0) {
     169          10 :                 state.dataWindowComplexManager->NumBasis = 1;
     170          10 :                 ConstructBasis(state, IConst, state.dataWindowComplexManager->BasisList(1));
     171             :             } else {
     172          11 :                 for (int IBasis = 1; IBasis <= state.dataWindowComplexManager->NumBasis; ++IBasis) {
     173           7 :                     if (state.dataWindowComplexManager->MatrixNo == state.dataWindowComplexManager->BasisList(IBasis).BasisMatIndex) goto BsLoop_loop;
     174             :                 }
     175           4 :                 ++state.dataWindowComplexManager->NumBasis;
     176           4 :                 ConstructBasis(state, IConst, state.dataWindowComplexManager->BasisList(state.dataWindowComplexManager->NumBasis));
     177             :             }
     178          14 :         BsLoop_loop:;
     179             :         }
     180          10 :         state.dataWindowComplexManager->BasisList.redimension(state.dataWindowComplexManager->NumBasis);
     181             :         //  Proceed to set up geometry for complex fenestration states
     182          10 :         state.dataBSDFWindow->ComplexWind.allocate(state.dataSurface->TotSurfaces); // Set up companion array to SurfaceWindow to hold window
     183             :         //     geometry for each state.  This is an allocatable array of
     184             :         //     geometries for the window states but only the complex
     185             :         //     fenestration surfaces will have the arrays allocated
     186             :         //  Search Thru Surfaces for Complex Fenestration State references
     187             :         //  This will define the first complex fenestration state for that window, others will follow if there are
     188             :         //     control specifications
     189          10 :         state.dataWindowComplexManager->WindowList.allocate(state.dataSurface->TotSurfaces); // Temporary allocation
     190          30 :         state.dataWindowComplexManager->WindowStateList.allocate(state.dataBSDFWindow->TotComplexFenStates,
     191          30 :                                                                  state.dataSurface->TotSurfaces); // Temporary allocation
     192         125 :         for (int ISurf = 1; ISurf <= state.dataSurface->TotSurfaces; ++ISurf) {
     193         115 :             int IConst = state.dataSurface->Surface(ISurf).Construction;
     194         115 :             if (IConst == 0) continue; // This is true for overhangs (Shading:Zone:Detailed)
     195         115 :             if (!(state.dataConstruction->Construct(IConst).TypeIsWindow && (state.dataConstruction->Construct(IConst).WindowTypeBSDF)))
     196          91 :                 continue; // Only BSDF windows
     197             :             // Simon Check: Thermal construction removed
     198             :             // ThConst = Construct(IConst)%BSDFInput%ThermalConstruction
     199          24 :             state.dataSurface->SurfWinWindowModelType(ISurf) = WindowModel::BSDF;
     200          24 :             state.dataHeatBal->AnyBSDF = true;
     201          24 :             ++state.dataWindowComplexManager->NumComplexWind;
     202          24 :             NumStates = 1;
     203          24 :             state.dataWindowComplexManager->WindowList(state.dataWindowComplexManager->NumComplexWind).NumStates =
     204             :                 1; // Having found the construction reference in
     205             :             // the Surface array defines the first state for this window
     206          24 :             state.dataWindowComplexManager->WindowList(state.dataWindowComplexManager->NumComplexWind).SurfNo = ISurf;
     207             :             // WindowList(  NumComplexWind ).Azimuth = DegToRadians * Surface( ISurf ).Azimuth;
     208             :             // WindowList(  NumComplexWind ).Tilt = DegToRadians * Surface( ISurf ).Tilt;
     209          24 :             state.dataWindowComplexManager->WindowStateList(NumStates, state.dataWindowComplexManager->NumComplexWind).InitInc =
     210          24 :                 state.dataWindowComplexManager->Calculate_Geometry;
     211          24 :             state.dataWindowComplexManager->WindowStateList(NumStates, state.dataWindowComplexManager->NumComplexWind).InitTrn =
     212          24 :                 state.dataWindowComplexManager->Calculate_Geometry;
     213          24 :             state.dataWindowComplexManager->WindowStateList(NumStates, state.dataWindowComplexManager->NumComplexWind).CopyIncState = 0;
     214          24 :             state.dataWindowComplexManager->WindowStateList(NumStates, state.dataWindowComplexManager->NumComplexWind).CopyTrnState = 0;
     215          24 :             state.dataWindowComplexManager->WindowStateList(NumStates, state.dataWindowComplexManager->NumComplexWind).Konst = IConst;
     216             :             // Simon Check: ThermalConstruction assigned to current construction
     217             :             // WindowStateList(NumComplexWind, NumStates)%ThermConst = ThConst
     218          72 :             for (int I = 1; I <= state.dataWindowComplexManager->NumBasis; ++I) { // Find basis in Basis List
     219          48 :                 if (state.dataConstruction->Construct(IConst).BSDFInput.BasisMatIndex == state.dataWindowComplexManager->BasisList(I).BasisMatIndex) {
     220          24 :                     state.dataWindowComplexManager->WindowStateList(NumStates, state.dataWindowComplexManager->NumComplexWind).IncBasisIndx =
     221             :                         I; // Note: square property matrices
     222          24 :                     state.dataWindowComplexManager->WindowStateList(NumStates, state.dataWindowComplexManager->NumComplexWind).TrnBasisIndx =
     223             :                         I; //   assumption
     224             :                 }
     225             :             }
     226          24 :             if (state.dataWindowComplexManager->WindowStateList(NumStates, state.dataWindowComplexManager->NumComplexWind).IncBasisIndx <= 0) {
     227           0 :                 ShowFatalError(state, "Complex Window Init: Window Basis not in BasisList.");
     228             :             }
     229             :         }
     230             :         //  Should now have a WindowList with dataWindowComplexManager. NumComplexWind entries containing all the complex fenestrations
     231             :         //    with a first state defined for each.
     232             :         //  *  *  *
     233             :         //  Here a search should be made for control specifications, which will give additional states for
     234             :         //    controlled complex fenestrations.  These should be added to the dataWindowComplexManager.WindowStateList, and
     235             :         //     WindowList( )%NumStates incremented for each window for which states are added.
     236             :         //      Added states should have WindowStateList ( , )%InitInc set to Calculate_Geometry
     237             :         //  *  *  *
     238             : 
     239             :         // At this point, we have a complete WindowList and WindowStateList, with dataWindowComplexManager. NumComplexWind
     240             :         //   defined, and NumStates for each complex window defined
     241             :         // Now sort through the window list to see that geometry will only be done once for each
     242             :         //  window, basis combination
     243             :         // Note:  code below assumes identical incoming and outgoing bases; following code will
     244             :         //   need revision if this assumption relaxed
     245             : 
     246          34 :         for (int IWind = 1; IWind <= state.dataWindowComplexManager->NumComplexWind; ++IWind) { // Search window list for repeated bases
     247          24 :             if (state.dataWindowComplexManager->WindowList(IWind).NumStates > 1) {
     248           0 :                 IHold.allocate(state.dataWindowComplexManager->WindowList(IWind).NumStates);
     249           0 :                 NHold = 1;
     250           0 :                 IHold(1).State = 1;
     251           0 :                 IHold(1).Basis = state.dataWindowComplexManager->WindowStateList(1, IWind).IncBasisIndx;
     252             :                 // If the Mth new basis found is basis B in the basis list, and it
     253             :                 // first occurs in the WindowStateList  in state N, then IHold(M)%Basis=B
     254             :                 // and IHold(M)%State=N
     255           0 :                 for (int K = 1; K <= state.dataWindowComplexManager->NumBasis; ++K) {
     256           0 :                     if (K > NHold) break;
     257           0 :                     int KBasis = IHold(K).Basis;
     258           0 :                     int J = IHold(K).State;
     259           0 :                     state.dataWindowComplexManager->InitBSDFWindowsOnce = true;
     260           0 :                     for (int I = J + 1; I <= state.dataWindowComplexManager->WindowList(IWind).NumStates;
     261             :                          ++I) { // See if subsequent states have the same basis
     262           0 :                         if ((state.dataWindowComplexManager->WindowStateList(I, state.dataWindowComplexManager->NumComplexWind).InitInc ==
     263           0 :                              state.dataWindowComplexManager->Calculate_Geometry) &&
     264           0 :                             (state.dataWindowComplexManager->WindowStateList(I, state.dataWindowComplexManager->NumComplexWind).IncBasisIndx ==
     265             :                              KBasis)) {
     266             :                             // Note:  square property matrices (same inc & trn bases) assumption
     267             :                             // If same incident and outgoing basis assumption removed, following code will need to
     268             :                             //  be extended to treat the two bases separately
     269           0 :                             state.dataWindowComplexManager->WindowStateList(I, state.dataWindowComplexManager->NumComplexWind).InitInc =
     270           0 :                                 state.dataWindowComplexManager->Copy_Geometry;
     271           0 :                             state.dataWindowComplexManager->WindowStateList(I, state.dataWindowComplexManager->NumComplexWind).InitTrn =
     272           0 :                                 state.dataWindowComplexManager->Copy_Geometry;
     273           0 :                             state.dataWindowComplexManager->WindowStateList(I, state.dataWindowComplexManager->NumComplexWind).CopyIncState = J;
     274           0 :                             state.dataWindowComplexManager->WindowStateList(I, state.dataWindowComplexManager->NumComplexWind).CopyTrnState = J;
     275           0 :                         } else if (state.dataWindowComplexManager->InitBSDFWindowsOnce) {
     276           0 :                             state.dataWindowComplexManager->InitBSDFWindowsOnce = false; // First occurrence of a different basis
     277           0 :                             ++NHold;
     278           0 :                             IHold(NHold).State = I;
     279           0 :                             IHold(NHold).Basis = state.dataWindowComplexManager->WindowStateList(I, IWind).IncBasisIndx;
     280           0 :                             state.dataWindowComplexManager->WindowStateList(I, state.dataWindowComplexManager->NumComplexWind).InitTrn =
     281           0 :                                 state.dataWindowComplexManager->Calculate_Geometry;
     282           0 :                             state.dataWindowComplexManager->WindowStateList(I, state.dataWindowComplexManager->NumComplexWind).CopyIncState = 0;
     283           0 :                             state.dataWindowComplexManager->WindowStateList(I, state.dataWindowComplexManager->NumComplexWind).CopyTrnState = 0;
     284             :                         }
     285             :                     }
     286             :                 }
     287           0 :                 IHold.deallocate();
     288             :             }
     289             :         }
     290             : 
     291             :         //  Now go through window list and window state list and calculate or copy the
     292             :         //   geometry information for each window, state
     293          34 :         for (int IWind = 1; IWind <= state.dataWindowComplexManager->NumComplexWind; ++IWind) {
     294          24 :             int ISurf = state.dataWindowComplexManager->WindowList(IWind).SurfNo;
     295          24 :             NumStates = state.dataWindowComplexManager->WindowList(IWind).NumStates;
     296             :             // ALLOCATE(SurfaceWindow( ISurf )%ComplexFen)    !activate the BSDF window description
     297             :             //  for this surface
     298          24 :             state.dataSurface->SurfaceWindow(ISurf).ComplexFen.NumStates = NumStates;
     299          24 :             state.dataSurface->SurfaceWindow(ISurf).ComplexFen.State.allocate(NumStates); // Allocate space for the states
     300          24 :             state.dataBSDFWindow->ComplexWind(ISurf).NumStates = NumStates;
     301          24 :             state.dataBSDFWindow->ComplexWind(ISurf).Geom.allocate(NumStates); // Allocate space for the geometries
     302             :             // Azimuth = WindowList( IWind ).Azimuth;
     303             :             // Tilt = WindowList( IWind ).Tilt;
     304             :             // Get the number of back surfaces for this window
     305          24 :             BaseSurf = state.dataSurface->Surface(ISurf).BaseSurf; // ShadowComb is organized by base surface
     306          24 :             int NBkSurf = state.dataShadowComb->ShadowComb(BaseSurf).NumBackSurf;
     307          24 :             state.dataBSDFWindow->ComplexWind(ISurf).NBkSurf = NBkSurf;
     308             :             // Define the back surface directions
     309          24 :             state.dataBSDFWindow->ComplexWind(ISurf).sWinSurf.allocate(NBkSurf);
     310          24 :             state.dataBSDFWindow->ComplexWind(ISurf).sdotN.allocate(NBkSurf);
     311             :             // Define the unit vectors pointing from the window center to the back surface centers
     312         180 :             for (int KBkSurf = 1; KBkSurf <= NBkSurf; ++KBkSurf) {
     313         156 :                 BaseSurf = state.dataSurface->Surface(ISurf).BaseSurf;                    // ShadowComb is organized by base surface
     314         156 :                 int JSurf = state.dataShadowComb->ShadowComb(BaseSurf).BackSurf(KBkSurf); // these are all proper back surfaces
     315         156 :                 V = state.dataSurface->Surface(JSurf).Centroid - state.dataSurface->Surface(ISurf).Centroid;
     316         156 :                 VLen = magnitude(V);
     317             :                 // Define the unit vector from the window center to the back
     318         156 :                 state.dataBSDFWindow->ComplexWind(ISurf).sWinSurf(KBkSurf) = V / VLen;
     319             :                 // surface center
     320             :                 // Define the back surface cosine(incident angle)
     321         156 :                 state.dataBSDFWindow->ComplexWind(ISurf).sdotN(KBkSurf) = dot(V, state.dataSurface->Surface(JSurf).OutNormVec) / VLen;
     322             :             }
     323          48 :             for (int IState = 1; IState <= NumStates; ++IState) {
     324             :                 // The following assumes identical incoming and outgoing bases.  The logic will need to be
     325             :                 //  redesigned if this assumption is relaxed
     326          24 :                 int IConst = state.dataWindowComplexManager->WindowStateList(IState, IWind).Konst;
     327             :                 // ThConst = WindowStateList ( IWind , IState )%ThermConst
     328          24 :                 state.dataSurface->SurfaceWindow(ISurf).ComplexFen.State(IState).Konst = IConst;
     329             :                 // SurfaceWindow(ISurf)%ComplexFen%State(IState)%ThermConst = ThConst
     330          24 :                 if (state.dataWindowComplexManager->WindowStateList(IState, IWind).InitInc == state.dataWindowComplexManager->Calculate_Geometry) {
     331          72 :                     state.dataBSDFWindow->ComplexWind(ISurf).Geom(IState).Inc = state.dataWindowComplexManager->BasisList(
     332          48 :                         state.dataWindowComplexManager->WindowStateList(IState, IWind).IncBasisIndx); // Put in the basis structure from the BasisList
     333          48 :                     state.dataBSDFWindow->ComplexWind(ISurf).Geom(IState).Trn =
     334          48 :                         state.dataWindowComplexManager->BasisList(state.dataWindowComplexManager->WindowStateList(IState, IWind).TrnBasisIndx);
     335             : 
     336          72 :                     SetupComplexWindowStateGeometry(state,
     337             :                                                     ISurf,
     338             :                                                     IState,
     339             :                                                     IConst,
     340          24 :                                                     state.dataBSDFWindow->ComplexWind(ISurf),
     341          24 :                                                     state.dataBSDFWindow->ComplexWind(ISurf).Geom(IState),
     342          24 :                                                     state.dataSurface->SurfaceWindow(ISurf).ComplexFen.State(IState));
     343             :                     // Note--setting up the state geometry will include constructing outgoing basis/surface
     344             :                     //  maps and those incoming maps that will not depend on shading.
     345             :                 } else {
     346           0 :                     state.dataSurface->SurfaceWindow(ISurf).ComplexFen.State(IState) = state.dataSurface->SurfaceWindow(ISurf).ComplexFen.State(
     347           0 :                         state.dataWindowComplexManager->WindowStateList(IState, IWind).CopyIncState); // Note this overwrites Konst
     348           0 :                     state.dataSurface->SurfaceWindow(ISurf).ComplexFen.State(IState).Konst = IConst;  //  so it has to be put back
     349             :                     // SurfaceWindow (ISurf )%ComplexFen%State(IState)%ThermConst = ThConst  !same for ThermConst
     350           0 :                     state.dataBSDFWindow->ComplexWind(ISurf).Geom(IState) =
     351           0 :                         state.dataBSDFWindow->ComplexWind(ISurf).Geom(state.dataWindowComplexManager->WindowStateList(IState, IWind).CopyIncState);
     352             :                 }
     353             : 
     354             :             } // State loop
     355             :         }     // Complex Window loop
     356             :         //  Allocate all beam-dependent complex fenestration quantities
     357          34 :         for (int IWind = 1; IWind <= state.dataWindowComplexManager->NumComplexWind; ++IWind) {
     358          24 :             int ISurf = state.dataWindowComplexManager->WindowList(IWind).SurfNo;
     359          24 :             NumStates = state.dataWindowComplexManager->WindowList(IWind).NumStates;
     360          48 :             for (int IState = 1; IState <= NumStates; ++IState) {
     361          24 :                 AllocateCFSStateHourlyData(state, ISurf, IState);
     362             :             } // State loop
     363             :         }     // Complex Window loop
     364             :     }
     365             : 
     366          24 :     void AllocateCFSStateHourlyData(EnergyPlusData &state,
     367             :                                     int const iSurf, // Surface number
     368             :                                     int const iState // Complex fenestration state number
     369             :     )
     370             :     {
     371             : 
     372             :         // SUBROUTINE INFORMATION:
     373             :         //       AUTHOR         Simon Vidanovic
     374             :         //       DATE WRITTEN   May 2013
     375             :         //       MODIFIED       na
     376             :         //       RE-ENGINEERED  na
     377             : 
     378             :         // PURPOSE OF THIS SUBROUTINE:
     379             :         // Allocate hourly data arrays for complex fenestration state
     380             : 
     381             :         // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
     382             :         int NLayers; // Number of complex fenestration layers
     383             :         int NBkSurf; // Number of back surfaces
     384             :         int KBkSurf; // Back surfaces counter
     385             : 
     386          24 :         NLayers = state.dataSurface->SurfaceWindow(iSurf).ComplexFen.State(iState).NLayers;
     387          24 :         NBkSurf = state.dataBSDFWindow->ComplexWind(iSurf).NBkSurf;
     388             : 
     389          48 :         state.dataBSDFWindow->ComplexWind(iSurf).Geom(iState).SolBmGndWt.allocate(
     390          48 :             24, state.dataGlobal->NumOfTimeStepInHour, state.dataBSDFWindow->ComplexWind(iSurf).Geom(iState).NGnd);
     391          24 :         state.dataBSDFWindow->ComplexWind(iSurf).Geom(iState).SolBmIndex.allocate(24, state.dataGlobal->NumOfTimeStepInHour);
     392          24 :         state.dataBSDFWindow->ComplexWind(iSurf).Geom(iState).ThetaBm.allocate(24, state.dataGlobal->NumOfTimeStepInHour);
     393          24 :         state.dataBSDFWindow->ComplexWind(iSurf).Geom(iState).PhiBm.allocate(24, state.dataGlobal->NumOfTimeStepInHour);
     394          24 :         state.dataSurface->SurfaceWindow(iSurf).ComplexFen.State(iState).WinDirHemiTrans.allocate(24, state.dataGlobal->NumOfTimeStepInHour);
     395          24 :         state.dataSurface->SurfaceWindow(iSurf).ComplexFen.State(iState).WinDirSpecTrans.allocate(24, state.dataGlobal->NumOfTimeStepInHour);
     396          24 :         state.dataSurface->SurfaceWindow(iSurf).ComplexFen.State(iState).WinBmGndTrans.allocate(24, state.dataGlobal->NumOfTimeStepInHour);
     397          24 :         state.dataSurface->SurfaceWindow(iSurf).ComplexFen.State(iState).WinBmFtAbs.allocate(24, state.dataGlobal->NumOfTimeStepInHour, NLayers);
     398          24 :         state.dataSurface->SurfaceWindow(iSurf).ComplexFen.State(iState).WinBmGndAbs.allocate(24, state.dataGlobal->NumOfTimeStepInHour, NLayers);
     399          48 :         state.dataSurface->SurfaceWindow(iSurf).ComplexFen.State(iState).WinToSurfBmTrans.allocate(
     400          48 :             24, state.dataGlobal->NumOfTimeStepInHour, NBkSurf);
     401          24 :         state.dataSurface->SurfaceWindow(iSurf).ComplexFen.State(iState).BkSurf.allocate(NBkSurf);
     402         180 :         for (KBkSurf = 1; KBkSurf <= NBkSurf; ++KBkSurf) {
     403         312 :             state.dataSurface->SurfaceWindow(iSurf).ComplexFen.State(iState).BkSurf(KBkSurf).WinDHBkRefl.allocate(
     404         312 :                 24, state.dataGlobal->NumOfTimeStepInHour);
     405         312 :             state.dataSurface->SurfaceWindow(iSurf).ComplexFen.State(iState).BkSurf(KBkSurf).WinDirBkAbs.allocate(
     406         312 :                 24, state.dataGlobal->NumOfTimeStepInHour, NLayers);
     407             :         }
     408          24 :     }
     409             : 
     410           0 :     void ExpandComplexState(EnergyPlusData &state,
     411             :                             int const iSurf, // Surface number
     412             :                             int const iConst // Construction number
     413             :     )
     414             :     {
     415             : 
     416             :         // SUBROUTINE INFORMATION:
     417             :         //       AUTHOR         Simon Vidanovic
     418             :         //       DATE WRITTEN   May 2013
     419             :         //       MODIFIED       Simon Vidanovic (July 2013)
     420             :         //       RE-ENGINEERED  na
     421             : 
     422             :         // PURPOSE OF THIS SUBROUTINE:
     423             :         // When complex fenestration is controlled by EMS, program does not know in advance how many states are assigned to
     424             :         // ceratin surface. This information can be obtain only at runtime. Purpose of this routine is to extend number of states
     425             :         // used by complex fenestration in case that is necessary.
     426             : 
     427             :         // Expands states by one
     428           0 :         int NumOfStates = state.dataSurface->SurfaceWindow(iSurf).ComplexFen.NumStates;
     429             : 
     430           0 :         state.dataBSDFWindow->ComplexWind(iSurf).Geom.redimension(NumOfStates + 1);
     431           0 :         state.dataSurface->SurfaceWindow(iSurf).ComplexFen.State.redimension(NumOfStates + 1);
     432             : 
     433             :         // Do daylighting geometry only in case it is initialized. If daylighting is not used then no need to expand state for that
     434           0 :         if (state.dataBSDFWindow->ComplexWind(iSurf).DaylightingInitialized) {
     435           0 :             state.dataBSDFWindow->ComplexWind(iSurf).DaylghtGeom.redimension(NumOfStates + 1);
     436           0 :             state.dataBSDFWindow->ComplexWind(iSurf).DaylightingInitialized = false;
     437             :         } else {
     438           0 :             state.dataBSDFWindow->ComplexWind(iSurf).DaylghtGeom.allocate(NumOfStates + 1);
     439             :         }
     440             : 
     441             :         // Increase number of states and insert new state
     442           0 :         ++NumOfStates;
     443           0 :         state.dataSurface->SurfaceWindow(iSurf).ComplexFen.NumStates = NumOfStates;
     444           0 :         state.dataBSDFWindow->ComplexWind(iSurf).NumStates = NumOfStates;
     445             : 
     446           0 :         state.dataSurface->SurfaceWindow(iSurf).ComplexFen.State(NumOfStates).Konst = iConst;
     447             : 
     448             :         // load basis and setup window state geometry
     449           0 :         ConstructBasis(state, iConst, state.dataBSDFWindow->ComplexWind(iSurf).Geom(NumOfStates).Inc);
     450           0 :         ConstructBasis(state, iConst, state.dataBSDFWindow->ComplexWind(iSurf).Geom(NumOfStates).Trn);
     451             : 
     452           0 :         SetupComplexWindowStateGeometry(state,
     453             :                                         iSurf,
     454             :                                         NumOfStates,
     455             :                                         iConst,
     456           0 :                                         state.dataBSDFWindow->ComplexWind(iSurf),
     457           0 :                                         state.dataBSDFWindow->ComplexWind(iSurf).Geom(NumOfStates),
     458           0 :                                         state.dataSurface->SurfaceWindow(iSurf).ComplexFen.State(NumOfStates));
     459             : 
     460             :         // allocation of memory for hourly data can be performed only after window state geometry has been setup
     461           0 :         AllocateCFSStateHourlyData(state, iSurf, NumOfStates);
     462             : 
     463             :         // calculate static properties for complex fenestration
     464           0 :         CalcWindowStaticProperties(state,
     465             :                                    iSurf,
     466             :                                    NumOfStates,
     467           0 :                                    state.dataBSDFWindow->ComplexWind(iSurf),
     468           0 :                                    state.dataBSDFWindow->ComplexWind(iSurf).Geom(NumOfStates),
     469           0 :                                    state.dataSurface->SurfaceWindow(iSurf).ComplexFen.State(NumOfStates));
     470             : 
     471             :         // calculate hourly data from complex fenestration
     472           0 :         CFSShadeAndBeamInitialization(state, iSurf, NumOfStates);
     473           0 :     }
     474             : 
     475       39540 :     void CheckCFSStates(EnergyPlusData &state, int const iSurf) // Surface number
     476             :     {
     477             : 
     478             :         // SUBROUTINE INFORMATION:
     479             :         //       AUTHOR         Simon Vidanovic
     480             :         //       DATE WRITTEN   May 2013
     481             :         //       MODIFIED       na
     482             :         //       RE-ENGINEERED  na
     483             : 
     484             :         // PURPOSE OF THIS SUBROUTINE:
     485             :         // Check if there are new states available for complex fenestration and performs proper initialization
     486             : 
     487             :         int NumOfStates; // number of states for current surface
     488             :         bool StateFound; // variable to indicate if state has been found
     489             :         int i;           // Local counter
     490             :         int CurrentCFSState;
     491             : 
     492       39540 :         StateFound = false;
     493       39540 :         CurrentCFSState = state.dataSurface->SurfaceWindow(iSurf).ComplexFen.CurrentState;
     494             : 
     495             :         // Check if EMS changed construction number
     496       39540 :         if (state.dataSurface->Surface(iSurf).Construction != state.dataSurface->SurfaceWindow(iSurf).ComplexFen.State(CurrentCFSState).Konst) {
     497             : 
     498             :             // If construction number changed then take new state
     499             :             // First search for existing states. Maybe state is already added in previous timestep
     500           0 :             NumOfStates = state.dataSurface->SurfaceWindow(iSurf).ComplexFen.NumStates;
     501           0 :             for (i = 1; i <= NumOfStates; ++i) {
     502           0 :                 if (state.dataSurface->Surface(iSurf).Construction == state.dataSurface->SurfaceWindow(iSurf).ComplexFen.State(i).Konst) {
     503           0 :                     StateFound = true;
     504           0 :                     CurrentCFSState = i;
     505           0 :                     state.dataSurface->SurfaceWindow(iSurf).ComplexFen.CurrentState = i;
     506             :                 }
     507             :             }
     508             :         } else {
     509       39540 :             StateFound = true;
     510             :         }
     511             : 
     512             :         // If new state is not found in the list of current states, then create new one, initialize and make it active
     513       39540 :         if (!StateFound) {
     514           0 :             ExpandComplexState(state, iSurf, state.dataSurface->Surface(iSurf).Construction);
     515           0 :             CurrentCFSState = state.dataSurface->SurfaceWindow(iSurf).ComplexFen.NumStates;
     516           0 :             state.dataSurface->SurfaceWindow(iSurf).ComplexFen.CurrentState = CurrentCFSState;
     517             :         }
     518       39540 :     }
     519             : 
     520         771 :     void InitComplexWindows(EnergyPlusData &state)
     521             :     {
     522             : 
     523             :         // SUBROUTINE INFORMATION:
     524             :         //       AUTHOR         Linda Lawrie
     525             :         //       DATE WRITTEN   November 2012
     526             :         //       MODIFIED       na
     527             :         //       RE-ENGINEERED  na
     528             : 
     529             :         // PURPOSE OF THIS SUBROUTINE:
     530             :         // Extract simple init for Complex Windows
     531             : 
     532             :         // One-time initialization
     533         771 :         if (state.dataWindowComplexManager->InitComplexWindowsOnce) {
     534         771 :             state.dataWindowComplexManager->InitComplexWindowsOnce = false;
     535         771 :             InitBSDFWindows(state);
     536         771 :             CalcStaticProperties(state);
     537             :         }
     538         771 :     }
     539             : 
     540       10621 :     void UpdateComplexWindows(EnergyPlusData &state)
     541             :     {
     542             : 
     543             :         // SUBROUTINE INFORMATION:
     544             :         //       AUTHOR         Joe Klems
     545             :         //       DATE WRITTEN   August 2011
     546             :         //       MODIFIED       B. Griffith, Nov. 2012 revised for detailed timestep integration mode
     547             :         //       RE-ENGINEERED  na
     548             : 
     549             :         // PURPOSE OF THIS SUBROUTINE:
     550             :         // Performs the shading-dependent initialization of the Complex Fenestration data;
     551             :         // On first call, calls the one-time initializition
     552             : 
     553             :         // LOGICAL,SAVE    ::  Once  =.TRUE.  !Flag for insuring things happen once
     554             :         int NumStates; // Number of states for a given complex fen
     555             :         int ISurf;     // Index for sorting thru Surface array
     556             :         int IState;    // Index identifying the window state for a particular window
     557             :         int IWind;     // Index identifying a window in the WindowList
     558             : 
     559       10621 :         if (state.dataWindowComplexManager->NumComplexWind == 0) return;
     560             : 
     561          24 :         if (state.dataGlobal->KickOffSizing || state.dataGlobal->KickOffSimulation) return;
     562             : 
     563             :         // Shading-dependent initialization; performed once for each shading period
     564             : 
     565             :         // Initialize the geometric quantities
     566             : 
     567          76 :         for (IWind = 1; IWind <= state.dataWindowComplexManager->NumComplexWind; ++IWind) {
     568          52 :             ISurf = state.dataWindowComplexManager->WindowList(IWind).SurfNo;
     569          52 :             NumStates = state.dataBSDFWindow->ComplexWind(ISurf).NumStates;
     570         104 :             for (IState = 1; IState <= NumStates; ++IState) {
     571          52 :                 CFSShadeAndBeamInitialization(state, ISurf, IState);
     572             :             } // State loop
     573             :         }     // window loop
     574             :     }
     575             : 
     576          52 :     void CFSShadeAndBeamInitialization(EnergyPlusData &state,
     577             :                                        int const iSurf, // Window surface number
     578             :                                        int const iState // Window state number
     579             :     )
     580             :     {
     581             : 
     582             :         // SUBROUTINE INFORMATION:
     583             :         //       AUTHOR         Simon Vidanovic
     584             :         //       DATE WRITTEN   May 2013
     585             :         //       MODIFIED       na
     586             :         //       RE-ENGINEERED  na
     587             : 
     588             :         // PURPOSE OF THIS SUBROUTINE:
     589             :         // Calculates shading properties of complex fenestration
     590             :         // Refactoring from Klems code
     591             : 
     592             :         using namespace Vectors;
     593             : 
     594         104 :         Vector SunDir(0.0, 0.0, 1.0); // unit vector pointing toward sun (world CS)
     595         104 :         Vector HitPt(0.0, 0.0, 1.0);  // vector location of ray intersection with a surface
     596             : 
     597          52 :         if (state.dataGlobal->KickOffSizing || state.dataGlobal->KickOffSimulation) return;
     598             : 
     599             :         int IncRay;   // Index of incident ray corresponding to beam direction
     600             :         Real64 Theta; // Theta angle of incident ray corresponding to beam direction
     601             :         Real64 Phi;   // Phi angle of incident ray corresponding to beam direction
     602             :         bool hit;     // hit flag
     603             :         int TotHits;  // hit counter
     604          52 :         auto &complexWindow(state.dataBSDFWindow->ComplexWind(iSurf));
     605          52 :         auto &complexWindowGeom(complexWindow.Geom(iState));
     606          52 :         auto &surfaceWindowState(state.dataSurface->SurfaceWindow(iSurf).ComplexFen.State(iState));
     607             : 
     608          52 :         if (!state.dataSysVars->DetailedSolarTimestepIntegration) {
     609          52 :             std::size_t lHT(0);  // Linear index for ( Hour, TS )
     610          52 :             std::size_t lHTI(0); // Linear index for ( Hour, TS, I )
     611        1300 :             for (int Hour = 1; Hour <= 24; ++Hour) {
     612        6864 :                 for (int TS = 1; TS <= state.dataGlobal->NumOfTimeStepInHour; ++TS, ++lHT) { // [ lHT ] == ( Hour, TS )
     613        5616 :                     SunDir = state.dataBSDFWindow->SUNCOSTS(TS, Hour);
     614        5616 :                     Theta = 0.0;
     615        5616 :                     Phi = 0.0;
     616        5616 :                     if (state.dataBSDFWindow->SUNCOSTS(TS, Hour)(3) > DataEnvironment::SunIsUpValue) {
     617        2833 :                         IncRay = FindInBasis(state, SunDir, RayIdentificationType::Front_Incident, iSurf, iState, complexWindowGeom.Inc, Theta, Phi);
     618        2833 :                         complexWindowGeom.ThetaBm[lHT] = Theta;
     619        2833 :                         complexWindowGeom.PhiBm[lHT] = Phi;
     620             :                     } else {
     621        2783 :                         complexWindowGeom.ThetaBm[lHT] = 0.0;
     622        2783 :                         complexWindowGeom.PhiBm[lHT] = 0.0;
     623        2783 :                         IncRay = 0; // sundown can't have ray incident on window
     624             :                     }
     625        5616 :                     if (IncRay > 0) { // Sun may be incident on the window
     626        1675 :                         complexWindowGeom.SolBmIndex[lHT] = IncRay;
     627             :                     } else { // Window can't be sunlit, set front incidence ray index to zero
     628        3941 :                         complexWindowGeom.SolBmIndex[lHT] = 0;
     629             :                     }
     630      212976 :                     for (int I = 1, nGnd = complexWindowGeom.NGnd; I <= nGnd; ++I, ++lHTI) { // Gnd pt loop
     631      207360 :                         TotHits = 0;
     632      414720 :                         Vector const gndPt(complexWindowGeom.GndPt(I));
     633     3184002 :                         for (int JSurf = 1, eSurf = state.dataSurface->TotSurfaces; JSurf <= eSurf; ++JSurf) {
     634             :                             // the following test will cycle on anything except exterior surfaces and shading surfaces
     635     5977366 :                             if (state.dataSurface->Surface(JSurf).HeatTransSurf &&
     636     2988683 :                                 state.dataSurface->Surface(JSurf).ExtBoundCond != ExternalEnvironment)
     637     1036924 :                                 continue;
     638             :                             // skip surfaces that face away from the ground point
     639     1951759 :                             if (dot(SunDir, state.dataSurface->Surface(JSurf).NewellSurfaceNormalVector) >= 0.0) continue;
     640             :                             // Looking for surfaces between GndPt and sun
     641             :                             PierceSurface(state, JSurf, gndPt, SunDir, HitPt, hit);
     642      520917 :                             if (hit) {
     643             :                                 // Are not going into the details of whether a hit surface is transparent
     644             :                                 // Since this is ultimately simply weighting the transmittance, so great
     645             :                                 // detail is not warranted
     646       12041 :                                 ++TotHits;
     647       12041 :                                 break;
     648             :                             }
     649             :                         }
     650      207360 :                         if (TotHits > 0) {
     651       12041 :                             complexWindowGeom.SolBmGndWt[lHTI] = 0.0; // [ lHTI ] == ( Hour, TS, I )
     652             :                         } else {
     653      195319 :                             complexWindowGeom.SolBmGndWt[lHTI] = 1.0; // [ lHTI ] == ( Hour, TS, I )
     654             :                         }
     655             :                     } // Gnd pt loop
     656             : 
     657             :                     // update window beam properties
     658        5616 :                     CalculateWindowBeamProperties(state, iSurf, iState, complexWindow, complexWindowGeom, surfaceWindowState, Hour, TS);
     659             :                 } // Timestep loop
     660             :             }     // Hour loop
     661             :         } else {  // detailed timestep integration
     662             :             std::size_t const lHT(
     663           0 :                 complexWindowGeom.ThetaBm.index(state.dataGlobal->HourOfDay, state.dataGlobal->TimeStep)); // [ lHT ] == ( HourOfDay, TimeStep )
     664           0 :             SunDir = state.dataBSDFWindow->SUNCOSTS(state.dataGlobal->TimeStep, state.dataGlobal->HourOfDay);
     665           0 :             Theta = 0.0;
     666           0 :             Phi = 0.0;
     667           0 :             if (state.dataBSDFWindow->SUNCOSTS(state.dataGlobal->TimeStep, state.dataGlobal->HourOfDay)(3) > DataEnvironment::SunIsUpValue) {
     668           0 :                 IncRay = FindInBasis(state, SunDir, RayIdentificationType::Front_Incident, iSurf, iState, complexWindowGeom.Inc, Theta, Phi);
     669           0 :                 complexWindowGeom.ThetaBm[lHT] = Theta;
     670           0 :                 complexWindowGeom.PhiBm[lHT] = Phi;
     671             :             } else {
     672           0 :                 complexWindowGeom.ThetaBm[lHT] = 0.0;
     673           0 :                 complexWindowGeom.PhiBm[lHT] = 0.0;
     674           0 :                 IncRay = 0; // sundown can't have ray incident on window
     675             :             }
     676             : 
     677           0 :             if (IncRay > 0) { // Sun may be incident on the window
     678           0 :                 complexWindowGeom.SolBmIndex[lHT] = IncRay;
     679             :             } else { // Window can't be sunlit, set front incidence ray index to zero
     680           0 :                 complexWindowGeom.SolBmIndex[lHT] = 0.0;
     681             :             }
     682           0 :             std::size_t lHTI(complexWindowGeom.SolBmGndWt.index(
     683           0 :                 state.dataGlobal->HourOfDay, state.dataGlobal->TimeStep, 1));        // Linear index for ( HourOfDay, TimeStep, I )
     684           0 :             for (int I = 1, nGnd = complexWindowGeom.NGnd; I <= nGnd; ++I, ++lHTI) { // Gnd pt loop
     685           0 :                 TotHits = 0;
     686           0 :                 Vector const gndPt(complexWindowGeom.GndPt(I));
     687           0 :                 for (int JSurf = 1; JSurf <= state.dataSurface->TotSurfaces; ++JSurf) {
     688             :                     // the following test will cycle on anything except exterior surfaces and shading surfaces
     689           0 :                     if (state.dataSurface->Surface(JSurf).HeatTransSurf && state.dataSurface->Surface(JSurf).ExtBoundCond != ExternalEnvironment)
     690           0 :                         continue;
     691             :                     // skip surfaces that face away from the ground point
     692           0 :                     if (dot(SunDir, state.dataSurface->Surface(JSurf).NewellSurfaceNormalVector) >= 0.0) continue;
     693             :                     // Looking for surfaces between GndPt and sun
     694             :                     PierceSurface(state, JSurf, gndPt, SunDir, HitPt, hit);
     695           0 :                     if (hit) {
     696             :                         // Are not going into the details of whether a hit surface is transparent
     697             :                         // Since this is ultimately simply weighting the transmittance, so great
     698             :                         // detail is not warranted
     699           0 :                         ++TotHits;
     700           0 :                         break;
     701             :                     }
     702             :                 }
     703           0 :                 if (TotHits > 0) {
     704           0 :                     complexWindowGeom.SolBmGndWt[lHTI] = 0.0; // [ lHTI ] == ( HourOfDay, TimeStep, I )
     705             :                 } else {
     706           0 :                     complexWindowGeom.SolBmGndWt[lHTI] = 1.0; // [ lHTI ] == ( HourOfDay, TimeStep, I )
     707             :                 }
     708             :             } // Gnd pt loop
     709             : 
     710             :             // Update window beam properties
     711           0 :             CalculateWindowBeamProperties(
     712           0 :                 state, iSurf, iState, complexWindow, complexWindowGeom, surfaceWindowState, state.dataGlobal->HourOfDay, state.dataGlobal->TimeStep);
     713             :         } // solar calculation mode, average over days or detailed
     714             :     }
     715             : 
     716        5616 :     void CalculateWindowBeamProperties(EnergyPlusData &state,
     717             :                                        int const ISurf,                   // Window surface number
     718             :                                        int const IState,                  // Window state number
     719             :                                        BSDFWindowGeomDescr const &Window, // Window Geometry
     720             :                                        BSDFGeomDescr const &Geom,         // State Geometry
     721             :                                        BSDFStateDescr &State,             // State Description
     722             :                                        int const Hour,                    // Hour number
     723             :                                        int const TS                       // Timestep number
     724             :     )
     725             :     {
     726             : 
     727             :         // SUBROUTINE INFORMATION:
     728             :         //       AUTHOR         Joe Klems
     729             :         //       DATE WRITTEN   August 2011
     730             :         //       MODIFIED       na
     731             :         //       RE-ENGINEERED  na
     732             : 
     733             :         // PURPOSE OF THIS SUBROUTINE:
     734             :         // Calculates those optical properties of all the Complex Fenestrations that
     735             :         //  depend on the beam direction (hence, on hour and time step)
     736             : 
     737             :         // METHODOLOGY EMPLOYED:
     738             :         // Locate the bidirectional property matrices in the BSDFInput structure
     739             :         // and use them to calculate the desired average properties.
     740             : 
     741             :         using namespace Vectors;
     742             : 
     743             :         int IConst; // State construction number
     744             :         int I;      // general purpose index--Back surface
     745             :         int J;      // general purpose index--ray
     746             :         int JRay;   // ray index number
     747             :         Real64 Theta;
     748             :         Real64 Phi;
     749             :         int JSurf;               // gen purpose surface no
     750             :         int BaseSurf;            // base surface no
     751             :         int M;                   // general purpose index--ray
     752             :         int L;                   // general purpose index--layer
     753             :         int KBkSurf;             // general purpose index--back surface
     754             :         Real64 Sum1;             // general purpose sum
     755             :         Real64 Sum2;             // general purpose sum
     756             :         int IBm;                 // index of beam ray in incoming basis
     757             :         int BkIncRay;            // index of sun dir in back incidence basis
     758             :         bool RegWindFnd;         // flag for regular exterior back surf window
     759       11232 :         Array1D_int RegWinIndex; // bk surf nos of reg windows
     760        5616 :         int NRegWin(0);          // no reg windows found as back surfaces
     761        5616 :         int KRegWin(0);          // index of reg window as back surface
     762             :         Real64 Refl;             // temporary reflectance
     763       11232 :         Array1D<Real64> Absorb;  // temporary layer absorptance
     764             : 
     765             :         // Object Data
     766       11232 :         Vector SunDir; // current sun direction
     767             : 
     768        5616 :         IConst = state.dataSurface->SurfaceWindow(ISurf).ComplexFen.State(IState).Konst;
     769             : 
     770             :         //  Begin calculation
     771             :         //  Calculate the Transmittance from a given beam direction to a given zone surface
     772             : 
     773        5616 :         IBm = Geom.SolBmIndex(Hour, TS);
     774        5616 :         if (IBm <= 0.0) { // Beam cannot be incident on window for this Hour, TS
     775        3941 :             State.WinToSurfBmTrans(Hour, TS, {1, Window.NBkSurf}) = 0.0;
     776        3941 :             State.WinDirHemiTrans(Hour, TS) = 0.0;
     777        3941 :             State.WinDirSpecTrans(Hour, TS) = 0.0;
     778        3941 :             State.WinBmFtAbs(Hour, TS, {1, State.NLayers}) = 0.0;
     779             :         } else {
     780       12084 :             for (I = 1; I <= Window.NBkSurf; ++I) { // Back surface loop
     781       10409 :                 Sum1 = 0.0;
     782      159996 :                 for (J = 1; J <= Geom.NSurfInt(I); ++J) { // Ray loop
     783      149587 :                     Sum1 +=
     784      149587 :                         Geom.Trn.Lamda(Geom.SurfInt(J, I)) * state.dataConstruction->Construct(IConst).BSDFInput.SolFrtTrans(IBm, Geom.SurfInt(J, I));
     785             :                 } // Ray loop
     786       10409 :                 State.WinToSurfBmTrans(Hour, TS, I) = Sum1;
     787             :             } // Back surface loop
     788             :             // Calculate the directional-hemispherical transmittance
     789        1675 :             Sum1 = 0.0;
     790      151262 :             for (J = 1; J <= Geom.Trn.NBasis; ++J) {
     791      149587 :                 Sum1 += Geom.Trn.Lamda(J) * state.dataConstruction->Construct(IConst).BSDFInput.SolFrtTrans(IBm, J);
     792             :             }
     793        1675 :             State.WinDirHemiTrans(Hour, TS) = Sum1;
     794             :             // Calculate the directional specular transmittance
     795             :             // Note:  again using assumption that Inc and Trn basis have same structure
     796        1675 :             State.WinDirSpecTrans(Hour, TS) = Geom.Trn.Lamda(IBm) * state.dataConstruction->Construct(IConst).BSDFInput.SolFrtTrans(IBm, IBm);
     797             :             // Calculate the layer front absorptance for beam radiation
     798        6230 :             for (L = 1; L <= State.NLayers; ++L) {
     799        4555 :                 State.WinBmFtAbs(Hour, TS, L) = state.dataConstruction->Construct(IConst).BSDFInput.Layer(L).FrtAbs(IBm, 1);
     800             :             }
     801             :         }
     802             :         // Calculate,  for a given beam direction, the transmittance into the zone
     803             :         // for ground-reflected radiation (transmitted radiation assumed uniformly diffuse)
     804             : 
     805        5616 :         Sum1 = 0.0;
     806        5616 :         Sum2 = 0.0;
     807      212976 :         for (J = 1; J <= Geom.NGnd; ++J) { // Incident ray loop
     808      207360 :             JRay = Geom.GndIndex(J);
     809      207360 :             if (Geom.SolBmGndWt(Hour, TS, J) > 0.0) {
     810      195319 :                 Sum2 += Geom.SolBmGndWt(Hour, TS, J) * Geom.Inc.Lamda(JRay);
     811    23724254 :                 for (M = 1; M <= Geom.Trn.NBasis; ++M) { // Outgoing ray loop
     812    47057870 :                     Sum1 += Geom.SolBmGndWt(Hour, TS, J) * Geom.Inc.Lamda(JRay) * Geom.Trn.Lamda(M) *
     813    23528935 :                             state.dataConstruction->Construct(IConst).BSDFInput.SolFrtTrans(JRay, M);
     814             :                 } // Outgoing ray loop
     815             :             }
     816             :         } // Indcident ray loop
     817        5616 :         if (Sum2 > 0.0) {
     818        5561 :             State.WinBmGndTrans(Hour, TS) = Sum1 / Sum2;
     819             :         } else {
     820          55 :             State.WinBmGndTrans(Hour, TS) = 0.0; // No unshaded ground => no transmittance
     821             :         }
     822             : 
     823             :         // Calculate,  for a given beam direction, the layer front absorptance
     824             :         // for ground-reflected radiation
     825             : 
     826       21216 :         for (L = 1; L <= State.NLayers; ++L) { // layer loop
     827       15600 :             Sum1 = 0.0;
     828       15600 :             Sum2 = 0.0;
     829      599280 :             for (J = 1; J <= Geom.NGnd; ++J) { // Incident ray loop
     830      583680 :                 JRay = Geom.GndIndex(J);
     831      583680 :                 if (Geom.SolBmGndWt(Hour, TS, J) > 0.0) {
     832      548444 :                     Sum2 += Geom.SolBmGndWt(Hour, TS, J) * Geom.Inc.Lamda(JRay);
     833     1096888 :                     Sum1 += Geom.SolBmGndWt(Hour, TS, J) * Geom.Inc.Lamda(JRay) *
     834      548444 :                             state.dataConstruction->Construct(IConst).BSDFInput.Layer(L).FrtAbs(JRay, 1);
     835             :                 }
     836             :             } // Incident ray loop
     837       15600 :             if (Sum2 > 0.0) {
     838       15435 :                 State.WinBmGndAbs(Hour, TS, L) = Sum1 / Sum2;
     839             :             } else {
     840         165 :                 State.WinBmGndAbs(Hour, TS, L) = 0.0; // No unshaded ground => no absorptance
     841             :             }
     842             :         } // layer loop
     843             : 
     844             :         // Check the back surfaces for exterior windows
     845        5616 :         RegWindFnd = false;
     846        5616 :         NRegWin = 0.0;
     847        5616 :         RegWinIndex.allocate(Window.NBkSurf);
     848       41760 :         for (KBkSurf = 1; KBkSurf <= Window.NBkSurf; ++KBkSurf) {
     849       36144 :             BaseSurf = state.dataSurface->Surface(ISurf).BaseSurf; // ShadowComb is organized by base surface
     850       36144 :             JSurf = state.dataShadowComb->ShadowComb(BaseSurf).BackSurf(KBkSurf);
     851       36144 :             if (state.dataSurface->SurfWinWindowModelType(JSurf) == WindowModel::BSDF) continue;
     852       56160 :             if (!(state.dataSurface->Surface(JSurf).Class == SurfaceClass::Window ||
     853       28080 :                   state.dataSurface->Surface(JSurf).Class == SurfaceClass::GlassDoor))
     854       28080 :                 continue;
     855           0 :             if (!(state.dataSurface->Surface(JSurf).HeatTransSurf && state.dataSurface->Surface(JSurf).ExtBoundCond == ExternalEnvironment &&
     856           0 :                   state.dataSurface->Surface(JSurf).ExtSolar))
     857           0 :                 continue;
     858             :             // Back surface is an exterior window or door
     859           0 :             RegWindFnd = true;
     860           0 :             ++NRegWin;
     861           0 :             RegWinIndex(NRegWin) = KBkSurf;
     862             :         }
     863        5616 :         if (RegWindFnd) {
     864           0 :             Absorb.allocate(State.NLayers);
     865           0 :             SunDir = state.dataBSDFWindow->SUNCOSTS(TS, Hour);
     866           0 :             BkIncRay = FindInBasis(state,
     867             :                                    SunDir,
     868             :                                    RayIdentificationType::Back_Incident,
     869             :                                    ISurf,
     870             :                                    IState,
     871           0 :                                    state.dataBSDFWindow->ComplexWind(ISurf).Geom(IState).Trn,
     872             :                                    Theta,
     873             :                                    Phi);
     874           0 :             if (BkIncRay > 0) {
     875             :                 // Here calculate the back incidence properties for the solar ray
     876             :                 // this does not say whether or not the ray can pass through the
     877             :                 // back surface window and hit this one!
     878           0 :                 Sum1 = 0.0;
     879           0 :                 for (J = 1; J <= Geom.Trn.NBasis; ++J) {
     880           0 :                     Sum1 += Geom.Trn.Lamda(J) * state.dataConstruction->Construct(IConst).BSDFInput.SolBkRefl(BkIncRay, J);
     881             :                 }
     882           0 :                 Refl = Sum1;
     883           0 :                 for (L = 1; L <= State.NLayers; ++L) {
     884           0 :                     Absorb(L) = state.dataConstruction->Construct(IConst).BSDFInput.Layer(L).BkAbs(BkIncRay, 1);
     885             :                 }
     886             :             } else {
     887             :                 // solar ray can't be incident on back, so set properties equal to zero
     888           0 :                 Refl = 0.0;
     889           0 :                 for (L = 1; L <= State.NLayers; ++L) {
     890           0 :                     Absorb(L) = 0.0;
     891             :                 }
     892             :             }
     893           0 :             for (KRegWin = 1; KRegWin <= NRegWin; ++KRegWin) {
     894           0 :                 KBkSurf = RegWinIndex(KRegWin);
     895           0 :                 State.BkSurf(KBkSurf).WinDHBkRefl(Hour, TS) = Refl;
     896           0 :                 for (L = 1; L <= State.NLayers; ++L) {
     897           0 :                     State.BkSurf(KBkSurf).WinDirBkAbs(Hour, TS, L) = Absorb(L);
     898             :                 }
     899             :             }
     900             :         }
     901        5616 :         if (allocated(Absorb)) Absorb.deallocate();
     902        5616 :         RegWinIndex.deallocate();
     903        5616 :     }
     904             : 
     905         771 :     void CalcStaticProperties(EnergyPlusData &state)
     906             :     {
     907             : 
     908             :         // SUBROUTINE INFORMATION:
     909             :         //       AUTHOR         Joe Klems
     910             :         //       DATE WRITTEN   <date_written>
     911             :         //       MODIFIED       na
     912             :         //       RE-ENGINEERED  na
     913             : 
     914             :         // PURPOSE OF THIS SUBROUTINE:
     915             :         // Calculates those optical properties of all the Complex Fenestrations that
     916             :         // do not depend on the beam direction (hence, on hour and time step)
     917             : 
     918             :         using namespace Vectors;
     919             : 
     920             :         //       ISurf(0);     // Index for sorting thru Surface array
     921             :         //                     //        static int IConst( 0 ); // Index for accessing Construct array
     922             :         //       IState(0);    // Index identifying the window state for a particular window
     923             :         //       IWind(0);     // Index identifying a window in the WindowList
     924             :         //       NumStates(0); // local copy of no of states
     925             : 
     926         795 :         for (int IWind = 1; IWind <= state.dataWindowComplexManager->NumComplexWind; ++IWind) {
     927          24 :             int ISurf = state.dataWindowComplexManager->WindowList(IWind).SurfNo;
     928          24 :             int NumStates = state.dataWindowComplexManager->WindowList(IWind).NumStates;
     929          48 :             for (int IState = 1; IState <= NumStates; ++IState) {
     930             :                 // IConst = WindowStateList ( IWind , IState )%Konst
     931          24 :                 state.dataSurface->SurfaceWindow(ISurf).ComplexFen.State(IState).Konst =
     932          24 :                     state.dataWindowComplexManager->WindowStateList(IState, IWind).Konst;
     933          72 :                 CalcWindowStaticProperties(state,
     934             :                                            ISurf,
     935             :                                            IState,
     936          24 :                                            state.dataBSDFWindow->ComplexWind(ISurf),
     937          24 :                                            state.dataBSDFWindow->ComplexWind(ISurf).Geom(IState),
     938          24 :                                            state.dataSurface->SurfaceWindow(ISurf).ComplexFen.State(IState));
     939             :             }
     940             :         }
     941         771 :     }
     942             : 
     943          14 :     void CalculateBasisLength(EnergyPlusData &state,
     944             :                               BSDFWindowInputStruct const &Input, // BSDF data input struct for this construction
     945             :                               int const IConst,                   // Construction number of input
     946             :                               int &NBasis                         // Calculated Basis length
     947             :     )
     948             :     {
     949             : 
     950             :         // SUBROUTINE INFORMATION:
     951             :         //       AUTHOR         Joe Klems
     952             :         //       DATE WRITTEN   August 2011
     953             :         //       MODIFIED       na
     954             :         //       RE-ENGINEERED  na
     955             : 
     956             :         // PURPOSE OF THIS SUBROUTINE:
     957             :         // Calculates the basis length for a Window6 Non-Symmetric or Axisymmetric basis
     958             :         // from the input basis matrix
     959             : 
     960          14 :         if (Input.BasisMatNcols == 1) {
     961             :             // Axisymmetric basis, No. rows is no. of thetas = basis length
     962           0 :             NBasis = Input.BasisMatNrows;
     963           0 :             return;
     964             :         }
     965          14 :         NBasis = 1;
     966         106 :         for (int I = 2; I <= Input.BasisMatNrows; ++I) {
     967          92 :             NBasis += std::floor(state.dataConstruction->Construct(IConst).BSDFInput.BasisMat(2, I) + 0.001);
     968             :         }
     969             :     }
     970             : 
     971          14 :     void ConstructBasis(EnergyPlusData &state,
     972             :                         int const IConst, // Index for accessing Construct array
     973             :                         BasisStruct &Basis)
     974             :     {
     975             :         // SUBROUTINE INFORMATION:
     976             :         //       AUTHOR         Joe Klems
     977             :         //       DATE WRITTEN  June 2011
     978             :         //       MODIFIED       na
     979             :         //       RE-ENGINEERED  na
     980             : 
     981             :         // PURPOSE OF THIS SUBROUTINE:
     982             :         // Set up a basis from the matrix information pointed to in Construction by ICons
     983             : 
     984          14 :         int I(0);               // general purpose index
     985          14 :         int J(0);               // general purpose index
     986          14 :         int NThetas(0);         // Current number of theta values
     987          14 :         int NumElem(0);         // Number of elements in current basis
     988          14 :         int ElemNo(0);          // Current basis element number
     989             :         int MaxNPhis;           // Max no of NPhis for any theta
     990          14 :         Real64 Theta(0.0);      // Current theta value
     991          14 :         Real64 Phi(0.0);        // Current phi value
     992          14 :         Real64 DTheta(0.0);     // Increment for theta value (Window6 type input)
     993          14 :         Real64 DPhi(0.0);       // Increment for phi value (Window6 type input)
     994          14 :         Real64 HalfDTheta(0.0); // Half-width of all theta bins except first and last (W6 input)
     995          14 :         Real64 Lamda(0.0);      // Current 'Lamda' value (element weight)
     996          14 :         Real64 SolAng(0.0);     // Current element solid angle
     997          14 :         Real64 NextTheta(0.0);  // Next theta in the W6 basis after current
     998          14 :         Real64 LastTheta(0.0);  // Previous theta in the W6 basis before current
     999          14 :         Real64 LowerTheta(0.0); // Lower theta boundary of the element
    1000          14 :         Real64 UpperTheta(0.0); // Upper theta boundary of the element
    1001          28 :         Array1D<Real64> Thetas; // temp array holding theta values
    1002          28 :         Array1D_int NPhis;      // temp array holding number of phis for a given theta
    1003             : 
    1004          14 :         NThetas = state.dataConstruction->Construct(IConst).BSDFInput.BasisMatNrows; // Note here assuming row by row input
    1005          14 :         Basis.NThetas = NThetas;
    1006          14 :         Basis.BasisMatIndex = state.dataConstruction->Construct(IConst).BSDFInput.BasisMatIndex;
    1007          14 :         Basis.NBasis = state.dataConstruction->Construct(IConst).BSDFInput.NBasis;
    1008          14 :         Basis.Grid.allocate(Basis.NBasis);
    1009          14 :         Thetas.allocate(NThetas + 1); // Temp array
    1010             :         // By convention the Thetas array contains a final point at Pi/2 which is not a basis element
    1011          14 :         NPhis.allocate(NThetas + 1); // Temp array
    1012          14 :         Basis.Thetas.allocate(NThetas + 1);
    1013          14 :         Basis.NPhis.allocate(NThetas + 1);
    1014             : 
    1015          14 :         Basis.Lamda.allocate(state.dataConstruction->Construct(IConst).BSDFInput.NBasis);
    1016          14 :         Basis.SolAng.allocate(state.dataConstruction->Construct(IConst).BSDFInput.NBasis);
    1017          14 :         if (state.dataConstruction->Construct(IConst).BSDFInput.BasisType == DataBSDFWindow::Basis::WINDOW) {
    1018             :             //   WINDOW6 Basis
    1019          14 :             Basis.BasisType = DataBSDFWindow::Basis::WINDOW;
    1020          14 :             if (state.dataConstruction->Construct(IConst).BSDFInput.BasisSymmetryType == DataBSDFWindow::BasisSymmetry::None) {
    1021             :                 // No basis symmetry
    1022          14 :                 Basis.BasisSymmetryType = DataBSDFWindow::BasisSymmetry::None;
    1023          14 :                 Thetas(1) = 0.0;                                     // By convention, the first basis point is at the center (theta=0,phi=0)
    1024          14 :                 Thetas(NThetas + 1) = 0.5 * DataGlobalConstants::Pi; // and there is an N+1st point (not a basis element) at Pi/2
    1025          14 :                 NPhis(1) = 1;
    1026          14 :                 NumElem = 1;
    1027         106 :                 for (I = 2; I <= NThetas; ++I) {
    1028          92 :                     Thetas(I) = state.dataConstruction->Construct(IConst).BSDFInput.BasisMat(1, I) * DataGlobalConstants::DegToRadians;
    1029          92 :                     NPhis(I) = std::floor(state.dataConstruction->Construct(IConst).BSDFInput.BasisMat(2, I) + 0.001);
    1030          92 :                     if (NPhis(I) <= 0) ShowFatalError(state, "WindowComplexManager: incorrect input, no. phis must be positive.");
    1031          92 :                     NumElem += NPhis(I);
    1032             :                 }
    1033          14 :                 MaxNPhis = maxval(NPhis({1, NThetas}));
    1034          14 :                 Basis.Phis.allocate(NThetas + 1, MaxNPhis + 1); // N+1st Phi point (not basis element) at 2Pi
    1035          14 :                 Basis.BasisIndex.allocate(MaxNPhis, NThetas + 1);
    1036          14 :                 Basis.Phis = 0.0;                                                            // Initialize so undefined elements will contain zero
    1037          14 :                 Basis.BasisIndex = 0;                                                        // Initialize so undefined elements will contain zero
    1038          14 :                 if (NumElem != state.dataConstruction->Construct(IConst).BSDFInput.NBasis) { // Constructed Basis must match property matrices
    1039           0 :                     ShowFatalError(state, "WindowComplexManager: Constructed basis length does not match property matrices.");
    1040             :                 }
    1041          14 :                 Basis.Thetas = Thetas;
    1042          14 :                 Basis.NPhis = NPhis;
    1043          14 :                 ElemNo = 0;
    1044         120 :                 for (I = 1; I <= NThetas; ++I) {
    1045         106 :                     Theta = Thetas(I);
    1046         106 :                     if (I == 1) { // First theta value must always be zero
    1047          14 :                         HalfDTheta = 0.5 * Thetas(I + 1);
    1048          14 :                         LastTheta = 0.0;
    1049          14 :                         NextTheta = Thetas(I + 1);
    1050          14 :                         LowerTheta = 0.0;
    1051          14 :                         UpperTheta = HalfDTheta;
    1052          92 :                     } else if (I > 1 && I < NThetas) {
    1053          78 :                         LastTheta = Thetas(I - 1);
    1054          78 :                         NextTheta = Thetas(I + 1);
    1055          78 :                         LowerTheta = UpperTheta;
    1056          78 :                         HalfDTheta = Theta - LowerTheta;
    1057          78 :                         UpperTheta = Theta + HalfDTheta;
    1058          14 :                     } else if (I == NThetas) {
    1059          14 :                         LastTheta = Thetas(I - 1);
    1060          14 :                         NextTheta = 0.5 * DataGlobalConstants::Pi;
    1061          14 :                         LowerTheta = UpperTheta; // It is assumed that Thetas(N) is the mean between the previous
    1062             :                         // UpperTheta and pi/2.
    1063          14 :                         UpperTheta = 0.5 * DataGlobalConstants::Pi;
    1064             :                     }
    1065         106 :                     DPhi = 2.0 * DataGlobalConstants::Pi / NPhis(I);
    1066         106 :                     if (I == 1) {
    1067          14 :                         Lamda = DataGlobalConstants::Pi * pow_2(std::sin(UpperTheta));
    1068          14 :                         SolAng = 2.0 * DataGlobalConstants::Pi * (1.0 - std::cos(UpperTheta));
    1069             :                     } else {
    1070          92 :                         Lamda = 0.5 * DPhi * (pow_2(std::sin(UpperTheta)) - pow_2(std::sin(LowerTheta))); // For W6 basis, lamda is funct of Theta and
    1071             :                         // NPhis, not individual Phi
    1072          92 :                         SolAng = DPhi * (std::cos(LowerTheta) - std::cos(UpperTheta));
    1073             :                     }
    1074         106 :                     DTheta = UpperTheta - LowerTheta;
    1075         106 :                     Basis.Phis(I, NPhis(I) + 1) = 2.0 * DataGlobalConstants::Pi; // Non-basis-element Phi point for table searching in Phi
    1076        1616 :                     for (J = 1; J <= NPhis(I); ++J) {
    1077        1510 :                         ++ElemNo;
    1078        1510 :                         Basis.BasisIndex(J, I) = ElemNo;
    1079        1510 :                         Phi = (J - 1) * DPhi;
    1080        1510 :                         Basis.Phis(I, J) = Phi; // Note: this ordering of I & J are necessary to allow Phis(Theta) to
    1081             :                         //  be searched as a one-dimensional table
    1082        1510 :                         FillBasisElement(state,
    1083             :                                          Theta,
    1084             :                                          Phi,
    1085             :                                          ElemNo,
    1086             :                                          Basis.Grid(ElemNo),
    1087             :                                          LowerTheta,
    1088             :                                          UpperTheta,
    1089             :                                          DPhi,
    1090             :                                          DataBSDFWindow::Basis::WINDOW); // This gets all the simple grid characteristics
    1091        1510 :                         Basis.Lamda(ElemNo) = Lamda;
    1092        1510 :                         Basis.SolAng(ElemNo) = SolAng;
    1093             :                     }
    1094             :                 }
    1095             :             } else { // BST
    1096             :                 //  Axisymmetric basis symmetry (Note this only useful specular systems, where it allows shorter data input)
    1097           0 :                 Basis.BasisSymmetryType = DataBSDFWindow::BasisSymmetry::Axisymmetric;
    1098           0 :                 Thetas(1) = 0.0;                                     // By convention, the first basis point is at the center (theta=0,phi=0)
    1099           0 :                 Thetas(NThetas + 1) = 0.5 * DataGlobalConstants::Pi; // and there is an N+1st point (not a basis element) at Pi/2
    1100           0 :                 NPhis = 1;                                           // As insurance, define one phi for each theta
    1101           0 :                 NumElem = 1;
    1102           0 :                 for (I = 2; I <= NThetas; ++I) {
    1103           0 :                     Thetas(I) = state.dataConstruction->Construct(IConst).BSDFInput.BasisMat(1, I) * DataGlobalConstants::DegToRadians;
    1104           0 :                     ++NumElem;
    1105             :                 }
    1106           0 :                 Basis.Phis.allocate(1, NThetas);
    1107           0 :                 Basis.BasisIndex.allocate(1, NThetas);
    1108           0 :                 Basis.Phis = 0.0;                                                            // Initialize so undefined elements will contain zero
    1109           0 :                 Basis.BasisIndex = 0;                                                        // Initialize so undefined elements will contain zero
    1110           0 :                 if (NumElem != state.dataConstruction->Construct(IConst).BSDFInput.NBasis) { // Constructed Basis must match property matrices
    1111           0 :                     ShowFatalError(state, "WindowComplexManager: Constructed basis length does not match property matrices.");
    1112             :                 }
    1113           0 :                 Basis.Thetas = Thetas;
    1114           0 :                 Basis.NPhis = NPhis;
    1115           0 :                 ElemNo = 0;
    1116           0 :                 DPhi = 2.0 * DataGlobalConstants::Pi;
    1117           0 :                 for (I = 1; I <= NThetas; ++I) {
    1118           0 :                     Theta = Thetas(I);
    1119           0 :                     if (I == 1) { // First theta value must always be zero
    1120           0 :                         HalfDTheta = 0.5 * Thetas(I + 1);
    1121           0 :                         LastTheta = 0.0;
    1122           0 :                         NextTheta = Thetas(I + 1);
    1123           0 :                         LowerTheta = 0.0;
    1124           0 :                         UpperTheta = HalfDTheta;
    1125           0 :                     } else if (I > 1 && I < NThetas) {
    1126           0 :                         LastTheta = Thetas(I - 1);
    1127           0 :                         NextTheta = Thetas(I + 1);
    1128           0 :                         LowerTheta = UpperTheta;
    1129           0 :                         HalfDTheta = Theta - LowerTheta;
    1130           0 :                         UpperTheta = Theta + HalfDTheta;
    1131           0 :                     } else if (I == NThetas) {
    1132           0 :                         LastTheta = Thetas(I - 1);
    1133           0 :                         NextTheta = 0.5 * DataGlobalConstants::Pi;
    1134           0 :                         LowerTheta = UpperTheta; // It is assumed that Thetas(N) is the mean between the previous
    1135             :                         // UpperTheta and pi/2.
    1136           0 :                         UpperTheta = 0.5 * DataGlobalConstants::Pi;
    1137             :                     }
    1138           0 :                     if (I == 1) {
    1139           0 :                         Lamda = DataGlobalConstants::Pi * pow_2(std::sin(UpperTheta));
    1140           0 :                         SolAng = 2.0 * DataGlobalConstants::Pi * (1.0 - std::cos(UpperTheta));
    1141             :                     } else {
    1142           0 :                         Lamda = 0.5 * DPhi * (pow_2(std::sin(UpperTheta)) - pow_2(std::sin(LowerTheta))); // For W6 basis, lamda is funct of Theta and
    1143             :                         // NPhis, not individual Phi
    1144           0 :                         SolAng = DPhi * (std::cos(LowerTheta) - std::cos(UpperTheta));
    1145             :                     }
    1146           0 :                     DTheta = UpperTheta - LowerTheta;
    1147           0 :                     ++ElemNo;
    1148           0 :                     Basis.BasisIndex(1, I) = ElemNo;
    1149           0 :                     Phi = 0.0;
    1150           0 :                     Basis.Phis(I, 1) = Phi; // Note: this ordering of I & J are necessary to allow Phis(Theta) to
    1151             :                     //  be searched as a one-dimensional table
    1152           0 :                     FillBasisElement(state,
    1153             :                                      Theta,
    1154             :                                      Phi,
    1155             :                                      ElemNo,
    1156             :                                      Basis.Grid(ElemNo),
    1157             :                                      LowerTheta,
    1158             :                                      UpperTheta,
    1159             :                                      DPhi,
    1160             :                                      DataBSDFWindow::Basis::WINDOW); // This gets all the simple grid characteristics
    1161           0 :                     Basis.Lamda(ElemNo) = Lamda;
    1162           0 :                     Basis.SolAng(ElemNo) = SolAng;
    1163             :                 }
    1164             :             }    // BST
    1165             :         } else { // BTW
    1166           0 :             ShowFatalError(state, "WindowComplexManager: Non-Window6 basis type not yet implemented.");
    1167             :         } // BTW
    1168          14 :         Thetas.deallocate();
    1169          14 :         NPhis.deallocate();
    1170          14 :     }
    1171             : 
    1172        1510 :     void FillBasisElement(EnergyPlusData &state,
    1173             :                           Real64 const Theta, // Central polar angle of element
    1174             :                           Real64 const Phi,   // Central azimuthal angle of element
    1175             :                           int const Elem,     // Index number of element in basis
    1176             :                           BasisElemDescr &BasisElem,
    1177             :                           Real64 const LowerTheta,              // Lower edge of element (polar angle)
    1178             :                           Real64 const UpperTheta,              // Upper edge of element (polar angle)
    1179             :                           Real64 const DPhi,                    // Width of element (azimuthal angle)
    1180             :                           DataBSDFWindow::Basis const InputType // Basis type
    1181             :     )
    1182             :     {
    1183             : 
    1184             :         // SUBROUTINE INFORMATION:
    1185             :         //       AUTHOR         Joe Klems
    1186             :         //       DATE WRITTEN   August 2010
    1187             :         //       MODIFIED       na
    1188             :         //       RE-ENGINEERED  na
    1189             : 
    1190             :         // PURPOSE OF THIS SUBROUTINE:
    1191             :         // fill in values for all the components of a basis element
    1192             : 
    1193        1510 :         if (InputType == DataBSDFWindow::Basis::WINDOW) {
    1194             :             // WINDOW6 Type BASIS
    1195        1510 :             if (Elem == 1) {
    1196             :                 // first element, theta=0, is special case
    1197          14 :                 BasisElem.Theta = Theta;
    1198          14 :                 BasisElem.Phi = 0.0;
    1199          14 :                 BasisElem.dPhi = 2.0 * DataGlobalConstants::Pi;
    1200          14 :                 BasisElem.UpprTheta = UpperTheta;
    1201          14 :                 BasisElem.dTheta = BasisElem.UpprTheta - Theta;
    1202          14 :                 BasisElem.LwrTheta = Theta;
    1203          14 :                 BasisElem.LwrPhi = 0.0;
    1204          14 :                 BasisElem.UpprPhi = 2.0 * DataGlobalConstants::Pi;
    1205             :             } else {
    1206        1496 :                 BasisElem.Theta = Theta;
    1207        1496 :                 BasisElem.Phi = Phi;
    1208        1496 :                 BasisElem.dPhi = DPhi;
    1209        1496 :                 BasisElem.LwrPhi = Phi - DPhi / 2.0;
    1210        1496 :                 BasisElem.UpprPhi = Phi + DPhi / 2.0;
    1211        1496 :                 BasisElem.LwrTheta = LowerTheta;
    1212        1496 :                 BasisElem.UpprTheta = UpperTheta;
    1213        1496 :                 BasisElem.dTheta = BasisElem.UpprTheta - BasisElem.LwrTheta;
    1214             :             }
    1215             :         } else {
    1216             :             // Non-WINDOW6 Type Basis
    1217             :             // Currently not implemented
    1218           0 :             ShowFatalError(state, "WindowComplexManager: Custom basis type not yet implemented.");
    1219             :         }
    1220        1510 :     }
    1221             : 
    1222          24 :     void SetupComplexWindowStateGeometry(EnergyPlusData &state,
    1223             :                                          int const ISurf,                       // Surface number of the complex fenestration
    1224             :                                          int const IState,                      // State number of the complex fenestration state
    1225             :                                          int const IConst,                      // Pointer to construction for this state
    1226             :                                          BSDFWindowGeomDescr &Window,           // Window Geometry
    1227             :                                          BSDFGeomDescr &Geom,                   // State Geometry
    1228             :                                          [[maybe_unused]] BSDFStateDescr &State // State Description
    1229             :     )
    1230             :     {
    1231             : 
    1232             :         // SUBROUTINE INFORMATION:
    1233             :         //       AUTHOR         J. Klems
    1234             :         //       DATE WRITTEN   June 2011
    1235             :         //       MODIFIED       na
    1236             :         //       RE-ENGINEERED  na
    1237             : 
    1238             :         // PURPOSE OF THIS SUBROUTINE:
    1239             :         // Define all the geometric quantites for a complex fenestration state
    1240             : 
    1241             :         using namespace Vectors;
    1242             : 
    1243             :         // Locals
    1244             :         // SUBROUTINE ARGUMENT DEFINITIONS:
    1245             :         // INTEGER, INTENT(IN)      ::  IWind            !Complex fenestration number (in window list)
    1246             : 
    1247             :         Real64 Azimuth; // Complex fenestration azimuth
    1248             :         Real64 Tilt;    // Complex fenestration tilt
    1249             :         int ElemNo;     // Grid index variable
    1250             :         bool hit;       // Surface intersection flag
    1251             :         int I;          // Temp Indices
    1252             :         int J;
    1253             :         int IRay;                    // Ray index variable
    1254             :         int IZone;                   // Zone containing the complex window
    1255             :         int JSurf;                   // Secondary Surface index
    1256             :         int BaseSurf;                // base surface index
    1257             :         int KBkSurf;                 // Back surface index
    1258             :         int MaxHits;                 // Max no of hits found
    1259             :         int MaxInt;                  // Max no of intersections found
    1260             :         int NSky;                    // No of sky rays
    1261             :         int NGnd;                    // No of gnd rays
    1262             :         int NReflSurf;               // No of rays striking ext surfaces
    1263             :         int NBkSurf;                 // No of back surfaces
    1264             :         int TotHits;                 // Current number of surface intersections
    1265             :         Real64 Theta;                // Basis theta angle
    1266             :         Real64 Phi;                  // Basis phi angle
    1267             :         Real64 HitDsq;               // Squared distance to current hit pt
    1268             :         Real64 LeastHitDsq;          // Squared distance to closest hit pt
    1269          48 :         Array1D<Real64> V(3);        // vector array
    1270          48 :         Array1D_int TmpRfSfInd;      // Temporary RefSurfIndex
    1271          48 :         Array1D_int TmpRfRyNH;       // Temporary RefRayNHits
    1272          48 :         Array2D_int TmpHSurfNo;      // Temporary HitSurfNo
    1273          48 :         Array2D<Real64> TmpHSurfDSq; // Temporary HitSurfDSq
    1274          48 :         Array1D_int TmpSkyInd;       // Temporary sky index list
    1275          48 :         Array1D_int TmpGndInd;       // Temporary gnd index list
    1276          48 :         Array2D_int TmpSurfInt;      // Temporary index of ray intersecing back surf
    1277          48 :         Array2D<Real64> TmpSjdotN;   // Temporary dot prod of ray angle w bk surf norm
    1278          48 :         Array1D_int ITemp1D;         // Temporary INT 1D array
    1279          48 :         Array2D<Real64> Temp2D;      // Temporary real 2D array
    1280             :         Real64 TransRSurf;           // Norminal transmittance of shading surface
    1281             :         Real64 WtSum;                // Sum for normalizing various weights
    1282             :         Real64 DotProd;              // Temporary variable for manipulating dot product .dot.
    1283             : 
    1284          24 :         struct BackHitList
    1285             :         {
    1286             :             // Members
    1287             :             int KBkSurf;   // Back surface index of the hit surface
    1288             :             int HitSurf;   // Surface number of the hit surface
    1289             :             Vector HitPt;  // coords of hit pt (world syst)
    1290             :             Real64 HitDsq; // Squared distance to the current hit pt
    1291             : 
    1292             :             // Default Constructor
    1293          24 :             BackHitList() : KBkSurf(0), HitSurf(0), HitDsq(0.0)
    1294             :             {
    1295          24 :             }
    1296             :         };
    1297             : 
    1298             :         // Object Data
    1299          48 :         Vector HitPt;             // coords of hit pt (world syst)
    1300          48 :         Vector X;                 // position vector
    1301          48 :         Vector VecNorm;           // outer normal vector
    1302          48 :         Array1D<Vector> TmpGndPt; // Temporary ground intersection list
    1303          48 :         Array2D<Vector> TempV2D;  // Temporary vector 2D array
    1304          48 :         Array2D<Vector> TmpHitPt; // Temporary HitPt
    1305          48 :         BackHitList BSHit;        // Temp list of back surface hit quantities for a ray
    1306             : 
    1307             :         // This routine primarily fills in the BSDFGeomDescr type for a given window and state
    1308             :         // Note that on call the incoming and outgoing basis structure types have already been filled in
    1309             :         //  Define the central ray directions (in world coordinate system)
    1310             : 
    1311          24 :         state.dataSurface->SurfaceWindow(ISurf).ComplexFen.State(IState).NLayers = state.dataConstruction->Construct(IConst).BSDFInput.NumLayers;
    1312          24 :         Azimuth = DataGlobalConstants::DegToRadians * state.dataSurface->Surface(ISurf).Azimuth;
    1313          24 :         Tilt = DataGlobalConstants::DegToRadians * state.dataSurface->Surface(ISurf).Tilt;
    1314             : 
    1315             :         // For incoming grid
    1316             : 
    1317          24 :         Geom.sInc.allocate(Geom.Inc.NBasis);
    1318          24 :         Geom.sInc = Vector(0.0, 0.0, 0.0);
    1319          24 :         Geom.pInc.allocate(Geom.Inc.NBasis);
    1320          24 :         Geom.CosInc.allocate(Geom.Inc.NBasis);
    1321          24 :         Geom.DAInc.allocate(Geom.Inc.NBasis);
    1322          24 :         Geom.pInc = BSDFDaylghtPosition(0.0, 0.0);
    1323        1944 :         for (ElemNo = 1; ElemNo <= Geom.Inc.NBasis; ++ElemNo) {
    1324        1920 :             Theta = Geom.Inc.Grid(ElemNo).Theta;
    1325        1920 :             Phi = Geom.Inc.Grid(ElemNo).Phi;
    1326             :             // The following puts in the vectors depending on
    1327             :             // window orientation
    1328        1920 :             Geom.sInc(ElemNo) = WorldVectFromW6(state, Theta, Phi, RayIdentificationType::Front_Incident, Tilt, Azimuth);
    1329        1920 :             Geom.pInc(ElemNo) = DaylghtAltAndAzimuth(Geom.sInc(ElemNo));
    1330             : 
    1331        1920 :             Geom.CosInc(ElemNo) = std::cos(Geom.Inc.Grid(ElemNo).Theta);
    1332             :             // Geom%DAInc(ElemNo) = COS(Geom%pInc(ElemNo)%Altitude) * Geom%Inc%Grid(ElemNo)%dTheta * Geom%Inc%Grid(ElemNo)%dPhi
    1333             :             // Geom%DAInc(ElemNo) = Geom%Inc%Grid(ElemNo)%dTheta * Geom%Inc%Grid(ElemNo)%dPhi
    1334        1920 :             Geom.DAInc(ElemNo) = std::cos(Geom.Inc.Grid(ElemNo).Theta) * Geom.Inc.Grid(ElemNo).dTheta * Geom.Inc.Grid(ElemNo).dPhi;
    1335             :         }
    1336             :         //  For outgoing grid
    1337          24 :         Geom.sTrn.allocate(Geom.Trn.NBasis);
    1338          24 :         Geom.sTrn = Vector(0.0, 0.0, 0.0);
    1339          24 :         Geom.pTrn.allocate(Geom.Trn.NBasis);
    1340          24 :         Geom.pTrn = BSDFDaylghtPosition(0.0, 0.0);
    1341        1944 :         for (ElemNo = 1; ElemNo <= Geom.Trn.NBasis; ++ElemNo) {
    1342        1920 :             Theta = Geom.Trn.Grid(ElemNo).Theta;
    1343        1920 :             Phi = Geom.Trn.Grid(ElemNo).Phi;
    1344             :             // The following puts in the vectors depending on
    1345             :             // window orientation
    1346        1920 :             Geom.sTrn(ElemNo) = WorldVectFromW6(state, Theta, Phi, RayIdentificationType::Front_Transmitted, Tilt, Azimuth);
    1347        1920 :             Geom.pTrn(ElemNo) = DaylghtAltAndAzimuth(Geom.sTrn(ElemNo));
    1348             :         }
    1349             :         //  Incident Basis:
    1350             :         //  Construct sky and ground ray index maps, and list of rays intersecting exterior surfaces
    1351             :         // Sky, and ground ray index maps, and rays that are potentially beam radiation reflected from exterior surfaces
    1352          24 :         TmpRfSfInd.allocate(Geom.Inc.NBasis);
    1353          24 :         TmpRfRyNH.allocate(Geom.Inc.NBasis);
    1354          24 :         TmpHSurfNo.allocate(state.dataSurface->TotSurfaces, Geom.Inc.NBasis);
    1355          24 :         TmpHSurfDSq.allocate(state.dataSurface->TotSurfaces, Geom.Inc.NBasis);
    1356          24 :         TmpHitPt.allocate(state.dataSurface->TotSurfaces, Geom.Inc.NBasis);
    1357          24 :         TmpSkyInd.allocate(Geom.Inc.NBasis);
    1358          24 :         TmpGndInd.allocate(Geom.Inc.NBasis);
    1359          24 :         TmpGndPt.allocate(Geom.Inc.NBasis);
    1360          24 :         NSky = 0;
    1361          24 :         NGnd = 0;
    1362          24 :         NReflSurf = 0;
    1363          24 :         TmpRfRyNH = 0;
    1364          24 :         Geom.NSkyUnobs = 0;
    1365          24 :         Geom.NGndUnobs = 0;
    1366             :         //  Note--this loop could be repeated for different positions in the window plane (as for detailed reflection
    1367             :         //  calculations, varying the origin in the call to PierceSurface.  Essentially, have set NsubV =1.
    1368        1944 :         for (IRay = 1; IRay <= Geom.Inc.NBasis; ++IRay) {
    1369        1920 :             if (Geom.sInc(IRay).z < 0.0) {
    1370             :                 // A ground ray
    1371         816 :                 ++Geom.NGndUnobs;
    1372             :             } else {
    1373             :                 // A sky ray
    1374        1104 :                 ++Geom.NSkyUnobs;
    1375             :             }
    1376             :             // Exterior reveal shadowing/reflection treatment should be inserted here
    1377        1920 :             TotHits = 0;
    1378       37070 :             for (JSurf = 1; JSurf <= state.dataSurface->TotSurfaces; ++JSurf) {
    1379             :                 // the following test will cycle on anything except exterior surfaces and shading surfaces
    1380       35150 :                 if (state.dataSurface->Surface(JSurf).HeatTransSurf && state.dataSurface->Surface(JSurf).ExtBoundCond != ExternalEnvironment)
    1381       12652 :                     continue;
    1382             :                 //  skip the base surface containing the window and any other subsurfaces of that surface
    1383       43076 :                 if (JSurf == state.dataSurface->Surface(ISurf).BaseSurf ||
    1384       20578 :                     state.dataSurface->Surface(JSurf).BaseSurf == state.dataSurface->Surface(ISurf).BaseSurf)
    1385        3840 :                     continue;
    1386             :                 //  skip surfaces that face away from the window
    1387       18658 :                 DotProd = dot(Geom.sInc(IRay), state.dataSurface->Surface(JSurf).NewellSurfaceNormalVector);
    1388       18658 :                 if (DotProd >= 0.0) continue;
    1389        9796 :                 PierceSurface(state, JSurf, state.dataSurface->Surface(ISurf).Centroid, Geom.sInc(IRay), HitPt, hit);
    1390        9796 :                 if (!hit) continue; // Miss: Try next surface
    1391           0 :                 if (TotHits == 0) {
    1392             :                     //  First hit for this ray
    1393           0 :                     TotHits = 1;
    1394           0 :                     ++NReflSurf;
    1395           0 :                     TmpRfSfInd(NReflSurf) = IRay;
    1396           0 :                     TmpRfRyNH(NReflSurf) = 1;
    1397           0 :                     TmpHSurfNo(1, NReflSurf) = JSurf;
    1398           0 :                     TmpHitPt(1, NReflSurf) = HitPt;
    1399           0 :                     V = HitPt - state.dataSurface->Surface(ISurf).Centroid; // vector array from window ctr to hit pt
    1400           0 :                     LeastHitDsq = magnitude_squared(V);                     // dist^2 window ctr to hit pt
    1401           0 :                     TmpHSurfDSq(1, NReflSurf) = LeastHitDsq;
    1402           0 :                     if (!state.dataSurface->Surface(JSurf).HeatTransSurf && state.dataSurface->Surface(JSurf).SchedShadowSurfIndex != 0) {
    1403           0 :                         TransRSurf = 1.0; // If a shadowing surface may have a scheduled transmittance,
    1404             :                         //   treat it here as completely transparent
    1405             :                     } else {
    1406           0 :                         TransRSurf = 0.0;
    1407             :                     }
    1408             :                 } else {
    1409           0 :                     V = HitPt - state.dataSurface->Surface(ISurf).Centroid;
    1410           0 :                     HitDsq = magnitude_squared(V);
    1411           0 :                     if (HitDsq >= LeastHitDsq) {
    1412           0 :                         if (TransRSurf > 0.0) { // forget the new hit if the closer hit is opaque
    1413           0 :                             J = TotHits + 1;
    1414           0 :                             if (TotHits > 1) {
    1415           0 :                                 for (I = 2; I <= TotHits; ++I) {
    1416           0 :                                     if (HitDsq < TmpHSurfDSq(I, NReflSurf)) {
    1417           0 :                                         J = I;
    1418           0 :                                         break;
    1419             :                                     }
    1420             :                                 }
    1421           0 :                                 if (!state.dataSurface->Surface(JSurf).HeatTransSurf && state.dataSurface->Surface(JSurf).SchedShadowSurfIndex == 0) {
    1422             :                                     //  The new hit is opaque, so we can drop all the hits further away
    1423           0 :                                     TmpHSurfNo(J, NReflSurf) = JSurf;
    1424           0 :                                     TmpHitPt(J, NReflSurf) = HitPt;
    1425           0 :                                     TmpHSurfDSq(J, NReflSurf) = HitDsq;
    1426           0 :                                     TotHits = J;
    1427             :                                 } else {
    1428             :                                     //  The new hit is scheduled (presumed transparent), so keep the more distant hits
    1429             :                                     //     Note that all the hists in the list will be transparent except the last,
    1430             :                                     //       which may be either transparent or opaque
    1431           0 :                                     if (TotHits >= J) {
    1432           0 :                                         for (I = TotHits; I >= J; --I) {
    1433           0 :                                             TmpHSurfNo(I + 1, NReflSurf) = TmpHSurfNo(I, NReflSurf);
    1434           0 :                                             TmpHitPt(I + 1, NReflSurf) = TmpHitPt(I, NReflSurf);
    1435           0 :                                             TmpHSurfDSq(I + 1, NReflSurf) = TmpHSurfDSq(I, NReflSurf);
    1436             :                                         }
    1437           0 :                                         TmpHSurfNo(J, NReflSurf) = JSurf;
    1438           0 :                                         TmpHitPt(J, NReflSurf) = HitPt;
    1439           0 :                                         TmpHSurfDSq(J, NReflSurf) = HitDsq;
    1440           0 :                                         ++TotHits;
    1441             :                                     }
    1442             :                                 }
    1443             :                             }
    1444             :                         }
    1445             :                     } else {
    1446             :                         //  A new closest hit.  If it is opaque, drop the current hit list,
    1447             :                         //    otherwise add it at the front
    1448           0 :                         LeastHitDsq = HitDsq;
    1449           0 :                         if (!state.dataSurface->Surface(JSurf).HeatTransSurf && state.dataSurface->Surface(JSurf).SchedShadowSurfIndex != 0) {
    1450           0 :                             TransRSurf = 1.0; // New closest hit is transparent, keep the existing hit list
    1451           0 :                             for (I = TotHits; I >= 1; --I) {
    1452           0 :                                 TmpHSurfNo(I + 1, NReflSurf) = TmpHSurfNo(I, NReflSurf);
    1453           0 :                                 TmpHitPt(I + 1, NReflSurf) = TmpHitPt(I, NReflSurf);
    1454           0 :                                 TmpHSurfDSq(I + 1, NReflSurf) = TmpHSurfDSq(I, NReflSurf);
    1455           0 :                                 ++TotHits;
    1456             :                             }
    1457             :                         } else {
    1458           0 :                             TransRSurf = 0.0; // New closest hit is opaque, drop the existing hit list
    1459           0 :                             TotHits = 1;
    1460             :                         }
    1461           0 :                         TmpHSurfNo(1, NReflSurf) = JSurf; // In either case the new hit is put in position 1
    1462           0 :                         TmpHitPt(1, NReflSurf) = HitPt;
    1463           0 :                         TmpHSurfDSq(1, NReflSurf) = LeastHitDsq;
    1464             :                     }
    1465             :                 }
    1466             :             } // End of loop over surfaces
    1467        1920 :             if (TotHits <= 0) {
    1468             :                 // This ray reached the sky or ground unobstructed
    1469        1920 :                 if (Geom.sInc(IRay).z < 0.0) {
    1470             :                     // A ground ray
    1471         816 :                     ++NGnd;
    1472         816 :                     TmpGndInd(NGnd) = IRay;
    1473        1632 :                     TmpGndPt(NGnd).x = state.dataSurface->Surface(ISurf).Centroid.x -
    1474         816 :                                        (Geom.sInc(IRay).x / Geom.sInc(IRay).z) * state.dataSurface->Surface(ISurf).Centroid.z;
    1475        1632 :                     TmpGndPt(NGnd).y = state.dataSurface->Surface(ISurf).Centroid.y -
    1476         816 :                                        (Geom.sInc(IRay).y / Geom.sInc(IRay).z) * state.dataSurface->Surface(ISurf).Centroid.z;
    1477         816 :                     TmpGndPt(NGnd).z = 0.0;
    1478             :                 } else {
    1479             :                     // A sky ray
    1480        1104 :                     ++NSky;
    1481        1104 :                     TmpSkyInd(NSky) = IRay;
    1482             :                 }
    1483             :             } else {
    1484             :                 // Save the number of hits for this ray
    1485           0 :                 TmpRfRyNH(NReflSurf) = TotHits;
    1486             :             }
    1487             :         } // End of loop over basis rays
    1488             :         // Store results of indexing the incident basis for this window
    1489          24 :         Geom.NSky = NSky;
    1490          24 :         Geom.NGnd = NGnd;
    1491          24 :         Geom.NReflSurf = NReflSurf;
    1492          24 :         Geom.SkyIndex.allocate(NSky);
    1493          24 :         Geom.SkyIndex = TmpSkyInd({1, NSky});
    1494          24 :         TmpSkyInd.deallocate();
    1495          24 :         Geom.GndIndex.allocate(NGnd);
    1496          24 :         Geom.GndPt.allocate(NGnd);
    1497          24 :         Geom.GndIndex = TmpGndInd({1, NGnd});
    1498          24 :         Geom.GndPt = TmpGndPt({1, NGnd});
    1499          24 :         TmpGndInd.deallocate();
    1500          24 :         TmpGndPt.deallocate();
    1501          24 :         MaxHits = maxval(TmpRfRyNH);
    1502          24 :         Geom.RefSurfIndex.allocate(NReflSurf);
    1503          24 :         Geom.RefRayNHits.allocate(NReflSurf);
    1504          24 :         Geom.HitSurfNo.allocate(MaxHits, NReflSurf);
    1505          24 :         Geom.HitSurfDSq.allocate(MaxHits, NReflSurf);
    1506          24 :         Geom.HitPt.allocate(MaxHits, NReflSurf);
    1507          24 :         Geom.RefSurfIndex = TmpRfSfInd({1, NReflSurf});
    1508          24 :         Geom.RefRayNHits = TmpRfRyNH({1, NReflSurf});
    1509          24 :         Geom.HitSurfNo = 0;
    1510          24 :         Geom.HitSurfDSq = 0.0;
    1511          24 :         Geom.HitPt = Vector(0.0, 0.0, 0.0);
    1512          24 :         for (I = 1; I <= NReflSurf; ++I) {
    1513           0 :             TotHits = TmpRfRyNH(I);
    1514           0 :             Geom.HitSurfNo({1, TotHits}, I) = TmpHSurfNo({1, TotHits}, I);
    1515           0 :             Geom.HitSurfDSq({1, TotHits}, I) = TmpHSurfDSq({1, TotHits}, I);
    1516           0 :             Geom.HitPt({1, TotHits}, I) = TmpHitPt({1, TotHits}, I);
    1517             :         }
    1518          24 :         TmpRfRyNH.deallocate();
    1519          24 :         TmpRfSfInd.deallocate();
    1520          24 :         TmpHSurfNo.deallocate();
    1521          24 :         TmpHSurfDSq.deallocate();
    1522          24 :         TmpHitPt.deallocate();
    1523             :         // In above scheme sky and ground rays are those that intesect no exterior surfaces.
    1524             :         //  The list of hit points is compiled for later (future?) calculation
    1525             :         //  of reflections from these surfaces.  The hit list for each ray includes all
    1526             :         //   surfaces with schedulable transmittance intersected by the ray,
    1527             :         //   in order of increasing distance, up to the first opaque surface.
    1528             :         //  Rays that intesect one or more schedulable transmittance but no opaque
    1529             :         //  surfaces (therefore may reach the sky or ground) are left out of the sky/ground
    1530             :         //  calcuation.  A correction for these rays could/should be made after the
    1531             :         //  shading calculation.
    1532             :         // Now calculate weights for averaging the transmittance matrix
    1533             :         // Sky Weights
    1534          24 :         Geom.SolSkyWt.allocate(NSky);
    1535        1128 :         for (I = 1; I <= NSky; ++I) {
    1536        1104 :             J = Geom.SkyIndex(I);
    1537        1104 :             Geom.SolSkyWt(I) = SkyWeight(Geom.sInc(J));
    1538             :         }
    1539          24 :         WtSum = sum(Geom.SolSkyWt({1, NSky}));
    1540          24 :         if (WtSum > DataGlobalConstants::rTinyValue) {
    1541          24 :             Geom.SolSkyWt({1, NSky}) /= WtSum;
    1542             :         } else {
    1543           0 :             Geom.SolSkyWt({1, NSky}) = 0.0;
    1544             :         }
    1545             :         // SkyGround Weights
    1546          24 :         Geom.SolSkyGndWt.allocate(NGnd);
    1547         840 :         for (I = 1; I <= NGnd; ++I) {
    1548         816 :             Geom.SolSkyGndWt(I) = SkyGndWeight(Geom.GndPt(I));
    1549             :         }
    1550          24 :         WtSum = sum(Geom.SolSkyGndWt({1, NGnd}));
    1551          24 :         if (WtSum > DataGlobalConstants::rTinyValue) {
    1552          24 :             Geom.SolSkyGndWt({1, NGnd}) /= WtSum;
    1553             :         } else {
    1554           0 :             Geom.SolSkyGndWt({1, NGnd}) = 0.0;
    1555             :         }
    1556             :         //  Weights for beam reflected from ground are calculated after shading
    1557             :         //  interval is determined
    1558             :         // Transmitted Basis:
    1559             :         //  Construct back surface intersection maps
    1560          24 :         IZone = state.dataSurface->Surface(ISurf).Zone;
    1561          24 :         NBkSurf = Window.NBkSurf;
    1562          24 :         Geom.NSurfInt.allocate(NBkSurf);
    1563          24 :         Geom.NSurfInt = 0; // Initialize the number of intersections to zero
    1564          24 :         TmpSurfInt.allocate(Geom.Trn.NBasis, NBkSurf);
    1565          24 :         TmpSjdotN.allocate(Geom.Trn.NBasis, NBkSurf);
    1566             :         // Find the intersections of the basis rays with the back surfaces
    1567        1944 :         for (IRay = 1; IRay <= Geom.Trn.NBasis; ++IRay) { // ray loop
    1568        1920 :             TotHits = 0;
    1569             :             //  Insert treatment of intersection & reflection from interior reveals here
    1570       14244 :             for (KBkSurf = 1; KBkSurf <= NBkSurf; ++KBkSurf) {                        // back surf loop
    1571       12324 :                 BaseSurf = state.dataSurface->Surface(ISurf).BaseSurf;                // ShadowComb is organized by base surface
    1572       12324 :                 JSurf = state.dataShadowComb->ShadowComb(BaseSurf).BackSurf(KBkSurf); // these are all proper back surfaces
    1573       12324 :                 PierceSurface(state, JSurf, state.dataSurface->Surface(ISurf).Centroid, Geom.sTrn(IRay), HitPt, hit);
    1574       12324 :                 if (!hit) continue; // Miss: Try next surface
    1575        2192 :                 if (TotHits == 0) {
    1576             :                     //  First hit for this ray
    1577        1920 :                     TotHits = 1;
    1578        1920 :                     BSHit.KBkSurf = KBkSurf;
    1579        1920 :                     BSHit.HitSurf = JSurf;
    1580        1920 :                     BSHit.HitPt = HitPt;
    1581        1920 :                     V = HitPt - state.dataSurface->Surface(ISurf).Centroid;
    1582        1920 :                     BSHit.HitDsq = magnitude_squared(V);
    1583         272 :                 } else if (BSHit.HitSurf == state.dataSurface->Surface(JSurf).BaseSurf) {
    1584             :                     //  another hit, check whether this is a subsurface of a previously hit base surface
    1585             :                     //  (which would be listed first in the Surface array)
    1586             :                     //  if so, replace the previous hit with this one
    1587         272 :                     ++TotHits;
    1588         272 :                     BSHit.KBkSurf = KBkSurf;
    1589         272 :                     BSHit.HitSurf = JSurf;
    1590         272 :                     BSHit.HitPt = HitPt;
    1591         272 :                     V = HitPt - state.dataSurface->Surface(ISurf).Centroid;
    1592         272 :                     BSHit.HitDsq = magnitude_squared(V);
    1593             :                 } else {
    1594           0 :                     ++TotHits;
    1595             :                     // is the new hit closer than the previous one (i.e., zone not strictly convex)?
    1596             :                     // if so, take the closer hit
    1597           0 :                     V = HitPt - state.dataSurface->Surface(ISurf).Centroid;
    1598           0 :                     HitDsq = magnitude_squared(V);
    1599           0 :                     if (HitDsq < BSHit.HitDsq) {
    1600           0 :                         BSHit.KBkSurf = KBkSurf;
    1601           0 :                         BSHit.HitSurf = JSurf;
    1602           0 :                         BSHit.HitPt = HitPt;
    1603           0 :                         BSHit.HitDsq = HitDsq;
    1604             :                     }
    1605             :                 }
    1606             :             }                   // back surf loop
    1607        1920 :             if (TotHits == 0) { // this should not happen--means a ray has gotten lost
    1608             :                 //    CALL ShowWarningError(state, 'BSDF--Zone surfaces do not completely enclose zone--transmitted ray lost')
    1609             :             } else {
    1610        1920 :                 KBkSurf = BSHit.KBkSurf;
    1611        1920 :                 JSurf = BSHit.HitSurf;
    1612        1920 :                 ++Geom.NSurfInt(KBkSurf);
    1613        1920 :                 TmpSurfInt(Geom.NSurfInt(KBkSurf), KBkSurf) = IRay;
    1614        1920 :                 VecNorm = state.dataSurface->Surface(JSurf).OutNormVec;
    1615        1920 :                 TmpSjdotN(Geom.NSurfInt(KBkSurf), KBkSurf) = dot(Geom.sTrn(IRay), VecNorm);
    1616             :             }
    1617             :         } // ray loop
    1618             :         //  All rays traced, now put away the results in the temporary arrays
    1619          24 :         MaxInt = maxval(Geom.NSurfInt);
    1620          24 :         Geom.SurfInt.allocate(MaxInt, Window.NBkSurf);
    1621          24 :         Geom.SjdotN.allocate(MaxInt, Window.NBkSurf);
    1622          24 :         Geom.SurfInt = 0;
    1623         180 :         for (I = 1; I <= Window.NBkSurf; ++I) {
    1624         156 :             Geom.SurfInt({1, Geom.NSurfInt(I)}, I) = TmpSurfInt({1, Geom.NSurfInt(I)}, I);
    1625         156 :             Geom.SjdotN({1, Geom.NSurfInt(I)}, I) = TmpSjdotN({1, Geom.NSurfInt(I)}, I);
    1626             :         }
    1627             : 
    1628          24 :         TmpSurfInt.deallocate();
    1629          24 :         TmpSjdotN.deallocate();
    1630          24 :     }
    1631             : 
    1632          24 :     void CalcWindowStaticProperties(EnergyPlusData &state,
    1633             :                                     int const ISurf,             // Surface number of the complex fenestration
    1634             :                                     int const IState,            // State number of the complex fenestration state
    1635             :                                     BSDFWindowGeomDescr &Window, // Window Geometry
    1636             :                                     BSDFGeomDescr &Geom,         // State Geometry
    1637             :                                     BSDFStateDescr &State        // State Description
    1638             :     )
    1639             :     {
    1640             : 
    1641             :         // SUBROUTINE INFORMATION:
    1642             :         //       AUTHOR         Joe Klems
    1643             :         //       DATE WRITTEN   <date_written>
    1644             :         //       MODIFIED       na
    1645             :         //       RE-ENGINEERED  na
    1646             : 
    1647             :         // PURPOSE OF THIS SUBROUTINE:
    1648             :         // Calculates those optical properties of all the Complex Fenestrations that
    1649             :         // do not depend on the beam direction (hence, on hour and time step)
    1650             : 
    1651             :         using namespace Vectors;
    1652             : 
    1653             :         int IConst;   // Pointer to construction for this fenestration
    1654          24 :         int I(0);     // general purpose index
    1655          24 :         int J(0);     // general purpose index
    1656          24 :         int JJ(0);    // general purpose index--ray
    1657          24 :         int L(0);     // general purpose index--layer
    1658          24 :         int M(0);     // general purpose index--ray
    1659             :         int KBkSurf;  // back surface index
    1660             :         int JSurf;    // surface number (used for back surface)
    1661             :         int BaseSurf; // base surface number (used for finding back surface)
    1662             :         Real64 Sum1;  // general purpose temporary sum
    1663             :         Real64 Sum2;  // general purpose temporary sum
    1664             :         Real64 Sum3;  // general purpose temporary sum
    1665             :         Real64 Hold;  // temp variable
    1666             : 
    1667          24 :         IConst = state.dataSurface->SurfaceWindow(ISurf).ComplexFen.State(IState).Konst;
    1668             : 
    1669             :         // Calculate the hemispherical-hemispherical transmittance
    1670             : 
    1671          24 :         Sum1 = 0.0;
    1672          24 :         Sum2 = 0.0;
    1673        1944 :         for (J = 1; J <= Geom.Inc.NBasis; ++J) { // Incident ray loop
    1674        1920 :             Sum2 += Geom.Inc.Lamda(J);
    1675      216360 :             for (M = 1; M <= Geom.Trn.NBasis; ++M) { // Outgoing ray loop
    1676      214440 :                 Sum1 += Geom.Inc.Lamda(J) * Geom.Trn.Lamda(M) * state.dataConstruction->Construct(IConst).BSDFInput.SolFrtTrans(M, J);
    1677             :             } // Outgoing ray loop
    1678             :         }     // Incident ray loop
    1679          24 :         if (Sum2 > 0) {
    1680          24 :             State.WinDiffTrans = Sum1 / Sum2;
    1681             :         } else {
    1682           0 :             State.WinDiffTrans = 0.0;
    1683           0 :             ShowWarningError(state, "BSDF--Inc basis has zero projected solid angle");
    1684             :         }
    1685             : 
    1686             :         // Calculate the hemispherical-hemispherical transmittance for visible spetrum
    1687             : 
    1688          24 :         Sum1 = 0.0;
    1689          24 :         Sum2 = 0.0;
    1690        1944 :         for (J = 1; J <= Geom.Inc.NBasis; ++J) { // Incident ray loop
    1691        1920 :             Sum2 += Geom.Inc.Lamda(J);
    1692      216360 :             for (M = 1; M <= Geom.Trn.NBasis; ++M) { // Outgoing ray loop
    1693      214440 :                 Sum1 += Geom.Inc.Lamda(J) * Geom.Trn.Lamda(M) * state.dataConstruction->Construct(IConst).BSDFInput.VisFrtTrans(M, J);
    1694             :             } // Outgoing ray loop
    1695             :         }     // Incident ray loop
    1696          24 :         if (Sum2 > 0.0) {
    1697          24 :             State.WinDiffVisTrans = Sum1 / Sum2;
    1698             :         } else {
    1699           0 :             State.WinDiffVisTrans = 0.0;
    1700           0 :             ShowWarningError(state, "BSDF--Inc basis has zero projected solid angle");
    1701             :         }
    1702             : 
    1703             :         // Set the nominal diffuse transmittance so the surface isn't mistaken as opaque
    1704             :         // Simon: Commented this out. We are not using TransDiff and it is already set to 0.1 in input routines.
    1705             :         // Construct( IConst ).TransDiff = SurfaceWindow( ISurf ).ComplexFen.State( IState ).WinDiffTrans;
    1706             :         // Calculate Window Sky Transmittance (transmitted radiation assumed diffuse)
    1707             :         // and Sky Absorptance (by layer)
    1708          24 :         Sum1 = 0.0;
    1709          24 :         Sum2 = 0.0;
    1710          24 :         Sum3 = 0.0;
    1711        1128 :         for (JJ = 1; JJ <= Geom.NSky; ++JJ) {
    1712      122184 :             for (M = 1; M <= Geom.Trn.NBasis; ++M) {
    1713      121080 :                 J = Geom.SkyIndex(JJ);
    1714      121080 :                 Sum1 +=
    1715      121080 :                     Geom.SolSkyWt(JJ) * state.dataConstruction->Construct(IConst).BSDFInput.SolFrtTrans(M, J) * Geom.Inc.Lamda(J) * Geom.Trn.Lamda(M);
    1716             :             }
    1717             :         }
    1718        1128 :         for (JJ = 1; JJ <= Geom.NSky; ++JJ) {
    1719        1104 :             J = Geom.SkyIndex(JJ);
    1720        1104 :             Sum2 += Geom.SolSkyWt(JJ) * Geom.Inc.Lamda(J);
    1721             :         }
    1722             : 
    1723          24 :         if (Sum2 != 0.0) {
    1724          24 :             State.WinSkyTrans = Sum1 / Sum2;
    1725             :         } else {
    1726           0 :             State.WinSkyTrans = 0.0;
    1727             :         }
    1728             : 
    1729          24 :         State.WinSkyFtAbs.allocate(State.NLayers);
    1730             :         // Also allocate the beam quantities for this state
    1731          92 :         for (L = 1; L <= State.NLayers; ++L) {
    1732          68 :             Sum3 = 0.0;
    1733        3224 :             for (JJ = 1; JJ <= Geom.NSky; ++JJ) {
    1734        3156 :                 J = Geom.SkyIndex(JJ);
    1735        3156 :                 Sum3 += Geom.SolSkyWt(JJ) * Geom.Inc.Lamda(J) * state.dataConstruction->Construct(IConst).BSDFInput.Layer(L).FrtAbs(J, 1);
    1736             :             }
    1737             : 
    1738          68 :             if (Sum2 != 0.0) {
    1739          68 :                 State.WinSkyFtAbs(L) = Sum3 / Sum2;
    1740             :             } else {
    1741           0 :                 State.WinSkyFtAbs(L) = 0.0;
    1742             :             }
    1743             :         }
    1744             : 
    1745             :         // Calculate Window Sky/Ground Transmittance
    1746             :         //(applies to ground-reflected sky radiation, transmitted radiation assumed diffuse)
    1747             :         // This is the same calculation as the sky transmittance, except that the set of incident
    1748             :         // rays and the ray weights are different
    1749             :         // Also calculate Window Sky/Ground Absorptance (by layer)
    1750          24 :         Sum1 = 0.0;
    1751          24 :         Sum2 = 0.0;
    1752          24 :         Sum3 = 0.0;
    1753             : 
    1754         840 :         for (JJ = 1; JJ <= Geom.NGnd; ++JJ) {
    1755       94176 :             for (M = 1; M <= Geom.Trn.NBasis; ++M) {
    1756       93360 :                 J = Geom.GndIndex(JJ);
    1757      186720 :                 Sum1 += Geom.SolSkyGndWt(JJ) * state.dataConstruction->Construct(IConst).BSDFInput.SolFrtTrans(M, J) * Geom.Inc.Lamda(J) *
    1758       93360 :                         Geom.Trn.Lamda(M);
    1759             :             }
    1760             :         }
    1761             : 
    1762         840 :         for (JJ = 1; JJ <= Geom.NGnd; ++JJ) {
    1763         816 :             J = Geom.GndIndex(JJ);
    1764         816 :             Sum2 += Geom.SolSkyGndWt(JJ) * Geom.Inc.Lamda(J);
    1765             :         }
    1766             : 
    1767          24 :         if (Sum2 != 0.0) {
    1768          24 :             State.WinSkyGndTrans = Sum1 / Sum2;
    1769             :         } else {
    1770           0 :             State.WinSkyGndTrans = 0.0;
    1771             :         }
    1772             : 
    1773          24 :         State.WinSkyGndAbs.allocate(State.NLayers);
    1774          92 :         for (L = 1; L <= State.NLayers; ++L) {
    1775          68 :             Sum3 = 0.0;
    1776        2404 :             for (JJ = 1; JJ <= Geom.NGnd; ++JJ) {
    1777        2336 :                 J = Geom.GndIndex(JJ);
    1778        2336 :                 Sum3 += Geom.SolSkyGndWt(JJ) * Geom.Inc.Lamda(J) * state.dataConstruction->Construct(IConst).BSDFInput.Layer(L).FrtAbs(J, 1);
    1779             :             }
    1780             : 
    1781          68 :             if (Sum2 != 0.0) {
    1782          68 :                 State.WinSkyGndAbs(L) = Sum3 / Sum2;
    1783             :             } else {
    1784           0 :                 State.WinSkyGndAbs(L) = 0.0;
    1785             :             }
    1786             :         }
    1787             : 
    1788             :         // Calculate Window Back Hemispherical Reflectance and Layer Back Hemispherical Absorptance
    1789          24 :         Sum1 = 0.0;
    1790          24 :         Sum2 = 0.0;
    1791          24 :         Sum3 = 0.0;
    1792             :         // Note this again assumes the equivalence Inc basis = transmission basis for back incidence and
    1793             :         // Trn basis = incident basis for back incidence
    1794        1944 :         for (J = 1; J <= Geom.Trn.NBasis; ++J) {
    1795      216360 :             for (M = 1; M <= Geom.Inc.NBasis; ++M) {
    1796      214440 :                 Sum1 += state.dataConstruction->Construct(IConst).BSDFInput.SolBkRefl(M, J) * Geom.Trn.Lamda(J) * Geom.Inc.Lamda(M);
    1797             :             }
    1798             :         }
    1799        1944 :         for (J = 1; J <= Geom.Trn.NBasis; ++J) {
    1800        1920 :             Sum2 += Geom.Trn.Lamda(J);
    1801             :         }
    1802             : 
    1803          24 :         if (Sum2 != 0.0) {
    1804          24 :             State.WinBkHemRefl = Sum1 / Sum2;
    1805             :         } else {
    1806           0 :             State.WinBkHemRefl = 0.0;
    1807             :         }
    1808             : 
    1809          24 :         state.dataConstruction->Construct(IConst).ReflectSolDiffBack = State.WinBkHemRefl;
    1810             : 
    1811          24 :         State.WinBkHemAbs.allocate(State.NLayers);
    1812          92 :         for (L = 1; L <= State.NLayers; ++L) {
    1813        5560 :             for (J = 1; J <= Geom.Trn.NBasis; ++J) {
    1814        5492 :                 Sum3 += Geom.Trn.Lamda(J) * state.dataConstruction->Construct(IConst).BSDFInput.Layer(L).BkAbs(J, 1);
    1815             :             }
    1816             : 
    1817          68 :             if (Sum2 != 0.0) {
    1818          68 :                 State.WinBkHemAbs(L) = Sum3 / Sum2;
    1819             :             } else {
    1820           0 :                 State.WinBkHemAbs(L) = 0.0;
    1821             :             }
    1822             : 
    1823             :             // Put this into the construction for use in non-detailed optical calculations
    1824          68 :             state.dataConstruction->Construct(IConst).AbsDiffBack(L) = State.WinBkHemAbs(L);
    1825             :         }
    1826             : 
    1827             :         // Calculate Window Layer Front Hemispherical Absorptance
    1828          24 :         Sum1 = 0.0;
    1829          24 :         Sum2 = 0.0;
    1830        1944 :         for (J = 1; J <= Geom.Inc.NBasis; ++J) {
    1831        1920 :             Sum2 += Geom.Inc.Lamda(J);
    1832             :         }
    1833          24 :         State.WinFtHemAbs.allocate(State.NLayers);
    1834          92 :         for (L = 1; L <= State.NLayers; ++L) {
    1835          68 :             Sum1 = 0.0;
    1836        5560 :             for (J = 1; J <= Geom.Inc.NBasis; ++J) {
    1837        5492 :                 Sum1 += Geom.Inc.Lamda(J) * state.dataConstruction->Construct(IConst).BSDFInput.Layer(L).FrtAbs(J, 1);
    1838             :             }
    1839             : 
    1840          68 :             if (Sum2 != 0.0) {
    1841          68 :                 State.WinFtHemAbs(L) = Sum1 / Sum2;
    1842             :             } else {
    1843           0 :                 State.WinFtHemAbs(L) = 0.0;
    1844             :             }
    1845             : 
    1846             :             // Put this into the construction for use in non-detailed optical calculations
    1847          68 :             state.dataConstruction->Construct(IConst).AbsDiff(L) = State.WinFtHemAbs(L);
    1848             :         }
    1849             : 
    1850             :         // Calculate Window Back Hemispherical Visible Reflectance
    1851          24 :         Sum1 = 0.0;
    1852          24 :         Sum2 = 0.0;
    1853             :         // Note this again assumes the equivalence Inc basis = transmission basis for back incidence and
    1854             :         // Trn basis = incident basis for back incidence
    1855        1944 :         for (J = 1; J <= Geom.Trn.NBasis; ++J) {
    1856      216360 :             for (M = 1; M <= Geom.Inc.NBasis; ++M) {
    1857      214440 :                 Sum1 += state.dataConstruction->Construct(IConst).BSDFInput.VisBkRefl(M, J) * Geom.Trn.Lamda(J) * Geom.Inc.Lamda(M);
    1858             :             }
    1859             :         }
    1860        1944 :         for (J = 1; J <= Geom.Trn.NBasis; ++J) {
    1861        1920 :             Sum2 += Geom.Trn.Lamda(J);
    1862             :         }
    1863             : 
    1864          24 :         if (Sum2 != 0.0) {
    1865          24 :             State.WinBkHemVisRefl = Sum1 / Sum2;
    1866             :         } else {
    1867           0 :             State.WinBkHemVisRefl = 0.0;
    1868             :         }
    1869             : 
    1870          24 :         state.dataConstruction->Construct(IConst).ReflectVisDiffBack = State.WinBkHemVisRefl;
    1871             : 
    1872             :         //     *     *     *     *
    1873             :         // Note potential problem if one relaxes the assumption that Inc and Trn basis have same structure:
    1874             :         //  The following calculations are made for the set of ray numbers defined in the Trn basis that
    1875             :         //   were determined to connect the center of the window to a particular back surface.
    1876             :         //   Here it is assumed that one can reverse these rays and get an equivalent set in the Trn
    1877             :         //   basis for back-incidence quantities: back transmittance and back layer absorptance
    1878             :         //   This assumption may fail if the Inc and Trn bases are allowed to have different structure.
    1879             :         //   Note also that in this case one would need to rethink the relationship of the basis
    1880             :         //   definitions to back-incidence quantities:  possibly this would
    1881             :         //   also require that the basis for back incident quantities be
    1882             :         //   different from the Trn basis, and similarly the basis for backward outgoing rays
    1883             :         //   be different from the Inc basis.
    1884             : 
    1885             :         //     *     *     *     *
    1886             :         //  Note that we are assuming that for back incidence the layer numberings are the same
    1887             :         //  as for front incidence, i.e., from outside to inside when incidence is from inside
    1888             :         //     *     *     *     *
    1889             :         // For back surfaces that are complex fenestrations, calculate the directional-hemispherical back
    1890             :         //  reflectance and the directional back absorptance by layer for this fenestration receiving
    1891             :         //  radiation via the back surface
    1892             :         //  Make this calculation only for cases where the back surface is a Complex Fenestration
    1893             :         // First allocate the back surface section of the state properties
    1894          24 :         if (!allocated(State.BkSurf)) State.BkSurf.allocate(Window.NBkSurf);
    1895         180 :         for (KBkSurf = 1; KBkSurf <= Window.NBkSurf; ++KBkSurf) {  // back surface loop
    1896         156 :             BaseSurf = state.dataSurface->Surface(ISurf).BaseSurf; // ShadowComb is organized by base surface
    1897         156 :             JSurf = state.dataShadowComb->ShadowComb(BaseSurf).BackSurf(KBkSurf);
    1898         156 :             if (state.dataSurface->SurfWinWindowModelType(JSurf) != WindowModel::BSDF) continue;
    1899             : 
    1900             :             //  Directional-hemispherical back reflectance
    1901          36 :             Sum1 = 0.0;
    1902          36 :             Sum2 = 0.0;
    1903         308 :             for (J = 1; J <= Geom.NSurfInt(KBkSurf); ++J) { // Inc Ray loop
    1904         272 :                 Sum2 += Geom.Trn.Lamda(Geom.SurfInt(J, KBkSurf));
    1905       33888 :                 for (M = 1; M <= Geom.Inc.NBasis; ++M) { // Outgoing Ray loop
    1906       67232 :                     Sum1 += Geom.Trn.Lamda(Geom.SurfInt(J, KBkSurf)) * Geom.Inc.Lamda(M) *
    1907       33616 :                             state.dataConstruction->Construct(IConst).BSDFInput.SolBkRefl(Geom.SurfInt(J, KBkSurf), M);
    1908             :                 } // Outgoing Ray loop
    1909             :             }     // Inc Ray loop
    1910          36 :             if (Sum2 > 0.0) {
    1911          36 :                 Hold = Sum1 / Sum2;
    1912         900 :                 for (I = 1; I <= 24; ++I) {
    1913        4896 :                     for (J = 1; J <= state.dataGlobal->NumOfTimeStepInHour; ++J) {
    1914        4032 :                         State.BkSurf(KBkSurf).WinDHBkRefl(I, J) = Hold;
    1915             :                     }
    1916             :                 }
    1917             :             } else {
    1918           0 :                 for (I = 1; I <= 24; ++I) {
    1919           0 :                     for (J = 1; J <= state.dataGlobal->NumOfTimeStepInHour; ++J) {
    1920           0 :                         State.BkSurf(KBkSurf).WinDHBkRefl(I, J) = 0.0;
    1921             :                     }
    1922             :                 }
    1923             :             }
    1924             : 
    1925             :             //  Directional layer  back absorption
    1926         147 :             for (L = 1; L <= State.NLayers; ++L) { // layer loop
    1927         111 :                 Sum1 = 0.0;
    1928         111 :                 Sum2 = 0.0;
    1929         962 :                 for (J = 1; J <= Geom.NSurfInt(KBkSurf); ++J) { // Inc Ray loop
    1930         851 :                     Sum2 += Geom.Trn.Lamda(Geom.SurfInt(J, KBkSurf));
    1931        1702 :                     Sum1 += Geom.Trn.Lamda(Geom.SurfInt(J, KBkSurf)) *
    1932         851 :                             state.dataConstruction->Construct(IConst).BSDFInput.Layer(L).BkAbs(Geom.SurfInt(J, KBkSurf), 1);
    1933             :                 } // Inc Ray loop
    1934         111 :                 if (Sum2 > 0.0) {
    1935         111 :                     Hold = Sum1 / Sum2;
    1936        2775 :                     for (I = 1; I <= 24; ++I) {
    1937       15192 :                         for (J = 1; J <= state.dataGlobal->NumOfTimeStepInHour; ++J) {
    1938       12528 :                             State.BkSurf(KBkSurf).WinDirBkAbs(I, J, L) = Hold;
    1939             :                         }
    1940             :                     }
    1941             :                 } else {
    1942           0 :                     for (I = 1; I <= 24; ++I) {
    1943           0 :                         for (J = 1; J <= state.dataGlobal->NumOfTimeStepInHour; ++J) {
    1944           0 :                             State.BkSurf(KBkSurf).WinDirBkAbs(I, J, L) = 0.0;
    1945             :                         }
    1946             :                     }
    1947             :                 }
    1948             : 
    1949             :             } // layer loop
    1950             :         }     // back surface loop
    1951             : 
    1952             :         // ********************************************************************************
    1953             :         // Allocation and calculation of integrated values for front of window surface
    1954             :         // ********************************************************************************
    1955             : 
    1956             :         // Sum of front absorptances for each incident direction (integration of absorptances)
    1957          24 :         if (!allocated(State.IntegratedFtAbs)) State.IntegratedFtAbs.allocate(Geom.Inc.NBasis);
    1958        1944 :         for (J = 1; J <= Geom.Inc.NBasis; ++J) {
    1959        1920 :             Sum1 = 0.0;
    1960        7412 :             for (L = 1; L <= State.NLayers; ++L) { // layer loop
    1961        5492 :                 Sum1 += state.dataConstruction->Construct(IConst).BSDFInput.Layer(L).FrtAbs(J, 1);
    1962             :             }
    1963        1920 :             State.IntegratedFtAbs(J) = Sum1;
    1964             :         }
    1965             : 
    1966             :         // Integrating front transmittance
    1967          24 :         if (!allocated(State.IntegratedFtTrans)) State.IntegratedFtTrans.allocate(Geom.Inc.NBasis);
    1968        1944 :         for (J = 1; J <= Geom.Inc.NBasis; ++J) { // Incident ray loop
    1969        1920 :             Sum1 = 0.0;
    1970      216360 :             for (M = 1; M <= Geom.Trn.NBasis; ++M) { // Outgoing ray loop
    1971      214440 :                 Sum1 += Geom.Trn.Lamda(J) * state.dataConstruction->Construct(IConst).BSDFInput.SolFrtTrans(J, M);
    1972             :             } // Outgoing ray loop
    1973        1920 :             State.IntegratedFtTrans(J) = Sum1;
    1974             :         } // Incident ray loop
    1975             : 
    1976          24 :         if (!allocated(State.IntegratedFtRefl)) State.IntegratedFtRefl.allocate(Geom.Inc.NBasis);
    1977             :         // Integrating front reflectance
    1978        1944 :         for (J = 1; J <= Geom.Inc.NBasis; ++J) { // Incoming ray loop
    1979        1920 :             State.IntegratedFtRefl(J) = 1 - State.IntegratedFtTrans(J) - State.IntegratedFtAbs(J);
    1980             :         } // Incoming ray loop
    1981             : 
    1982             :         // ********************************************************************************
    1983             :         // Allocation and calculation of integrated values for back of window surface
    1984             :         // ********************************************************************************
    1985             : 
    1986             :         // Sum of back absorptances for each incident direction (integration of absorptances)
    1987          24 :         if (!allocated(State.IntegratedBkAbs)) State.IntegratedBkAbs.allocate(Geom.Trn.NBasis);
    1988        1944 :         for (J = 1; J <= Geom.Trn.NBasis; ++J) {
    1989        1920 :             Sum1 = 0.0;
    1990        7412 :             for (L = 1; L <= State.NLayers; ++L) { // layer loop
    1991        5492 :                 Sum1 += state.dataConstruction->Construct(IConst).BSDFInput.Layer(L).BkAbs(J, 1);
    1992             :             }
    1993        1920 :             State.IntegratedBkAbs(J) = Sum1;
    1994             :         }
    1995             : 
    1996             :         // Integrating back reflectance
    1997          24 :         if (!allocated(State.IntegratedBkRefl)) State.IntegratedBkRefl.allocate(Geom.Trn.NBasis);
    1998        1944 :         for (J = 1; J <= Geom.Trn.NBasis; ++J) { // Outgoing ray loop
    1999        1920 :             Sum1 = 0.0;
    2000      216360 :             for (M = 1; M <= Geom.Inc.NBasis; ++M) { // Incident ray loop
    2001      214440 :                 Sum1 += Geom.Inc.Lamda(J) * state.dataConstruction->Construct(IConst).BSDFInput.SolBkRefl(J, M);
    2002             :             } // Incident ray loop
    2003        1920 :             State.IntegratedBkRefl(J) = Sum1;
    2004             :         } // Outgoing ray loop
    2005             : 
    2006          24 :         if (!allocated(State.IntegratedBkTrans)) State.IntegratedBkTrans.allocate(Geom.Trn.NBasis);
    2007             :         // Integrating back transmittance
    2008        1944 :         for (J = 1; J <= Geom.Trn.NBasis; ++J) { // Outgoing ray loop
    2009        1920 :             State.IntegratedBkTrans(J) = 1 - State.IntegratedBkRefl(J) - State.IntegratedBkAbs(J);
    2010             :         } // Outgoing ray loop
    2011          24 :     }
    2012             : 
    2013        1104 :     Real64 SkyWeight([[maybe_unused]] Vector const &DirVec) // Direction of the element to be weighted
    2014             :     {
    2015             : 
    2016             :         // FUNCTION INFORMATION:
    2017             :         //       AUTHOR         Joe Klems
    2018             :         //       DATE WRITTEN   June 2011
    2019             :         //       MODIFIED       na
    2020             :         //       RE-ENGINEERED  na
    2021             : 
    2022             :         // PURPOSE OF THIS FUNCTION:
    2023             :         // Search a one-dimensional array for a given value, returning the index of the element equal to the value, if
    2024             :         //   found, or zero
    2025             : 
    2026             :         using namespace Vectors;
    2027             : 
    2028             :         // Return value
    2029             :         Real64 Wt; // Weight
    2030             : 
    2031        1104 :         Wt = 1.0;
    2032             : 
    2033             :         // To do:  figure out how to weight sky elements to reproduce the current E+ assumptions
    2034             :         //  Possibly one will need to calculated average DH transmittance for isotropic sky and
    2035             :         //  horizon separately and then later average according to sky conditions.  Then a completely
    2036             :         //  different scheme for daylight.  For now: rays that reach sky equally weighted in calculating
    2037             :         //  transmittance, rays passing through surfaces with scheduled transmittance are neglected.
    2038             : 
    2039        1104 :         return Wt;
    2040             :     }
    2041             : 
    2042         816 :     Real64 SkyGndWeight([[maybe_unused]] Vector const &PosVec) // x,y,z(=0) of ground intersection pt
    2043             :     {
    2044             : 
    2045             :         // FUNCTION INFORMATION:
    2046             :         //       AUTHOR         Joe Klems
    2047             :         //       DATE WRITTEN   June 2011
    2048             :         //       MODIFIED       na
    2049             :         //       RE-ENGINEERED  na
    2050             : 
    2051             :         // PURPOSE OF THIS FUNCTION:
    2052             :         // Search a one-dimensional array for a given value, returning the index of the element equal to the value, if
    2053             :         //   found, or zero
    2054             : 
    2055             :         using namespace Vectors;
    2056             : 
    2057             :         // Return value
    2058             :         Real64 Wt; // Weight
    2059             : 
    2060         816 :         Wt = 1.0;
    2061             : 
    2062             :         //  At present, equally weights all ground rays for calculation of the complex window transmittance for
    2063             :         //  sky radiation reflected from ground.  This does not take into account shading of the ground.
    2064             :         //  The correct procedure would be to generate a set of rays to the sky and see which do not intersect
    2065             :         //  surfaces, as is done in the reflection manager.  However, this would increase computational load.
    2066             :         //  Given that equal weighting, by averaging the transmittance only over rays that come from the ground,
    2067             :         //  already produces a more accurate ground transmittance than the existing method, it is at least questionable
    2068             :         //  whether the more detailed procedure would produce enough improvement in accuracy to make up for
    2069             :         //  the additional calculation time.  Therefore a more detailed treatment is deferred until there is some
    2070             :         //  experience with the new method to determine whether further detail is warranted.
    2071             : 
    2072         816 :         return Wt;
    2073             :     }
    2074             : 
    2075        4074 :     BSDFDaylghtPosition DaylghtAltAndAzimuth(Vector const &UnitVect) // vector which needs to be converted
    2076             :     {
    2077             : 
    2078             :         // SUBROUTINE INFORMATION:
    2079             :         //       AUTHOR         Simon Vidanovic
    2080             :         //       DATE WRITTEN   April 2013
    2081             :         //       MODIFIED       na
    2082             :         //       RE-ENGINEERED  na
    2083             : 
    2084             :         // PURPOSE OF THIS FUNCTION:
    2085             :         // Transform unit vector (given in world coordinates) into altitude and azimuth.  Azimuth is measured from positive x-axe.
    2086             :         // Altitude range is from -pi/2 to pi/2. Vector laying in horizontal plane will have altitude equal to zero and vector
    2087             :         // pointing upward will have altitude equal to pi/2. Range for azimuth is calculated from -pi to +pi.
    2088             : 
    2089             :         using namespace DataBSDFWindow;
    2090             :         using namespace Vectors;
    2091             :         // Return value
    2092        4074 :         BSDFDaylghtPosition DayPos; // altitude and azimuth in world coordinates
    2093             : 
    2094        4074 :         if (UnitVect.x != 0.0) {
    2095        3620 :             if (UnitVect.x >= 0.0) {
    2096        1828 :                 DayPos.Azimuth = std::atan(UnitVect.y / UnitVect.x);
    2097             :             } else {
    2098        1792 :                 if (UnitVect.y >= 0.0) {
    2099         922 :                     DayPos.Azimuth = DataGlobalConstants::Pi + std::atan(UnitVect.y / UnitVect.x);
    2100             :                 } else {
    2101         870 :                     DayPos.Azimuth = -DataGlobalConstants::Pi + std::atan(UnitVect.y / UnitVect.x);
    2102             :                 }
    2103             :             }
    2104             :         } else {
    2105         454 :             if (UnitVect.y >= 0.0) {
    2106         182 :                 DayPos.Azimuth = DataGlobalConstants::PiOvr2;
    2107             :             } else {
    2108         272 :                 DayPos.Azimuth = -DataGlobalConstants::PiOvr2;
    2109             :             }
    2110             :         }
    2111             : 
    2112        4074 :         DayPos.Altitude = std::asin(UnitVect.z);
    2113             : 
    2114        4074 :         return DayPos;
    2115             :     }
    2116             : 
    2117        3840 :     Vector WorldVectFromW6([[maybe_unused]] EnergyPlusData &state,
    2118             :                            Real64 const Theta,                  // Polar angle in W6 Coords
    2119             :                            Real64 const Phi,                    // Azimuthal angle in W6 Coords
    2120             :                            const RayIdentificationType RadType, // Type of radiation: Front_Incident, etc.
    2121             :                            Real64 const Gamma,                  // Surface tilt angle, radians, world coordinate system
    2122             :                            Real64 const Alpha                   // Surface azimuth, radians, world coordinate system
    2123             :     )
    2124             :     {
    2125             : 
    2126             :         // SUBROUTINE INFORMATION:
    2127             :         //       AUTHOR         Joe Klems
    2128             :         //       DATE WRITTEN   Aug 2010
    2129             :         //       MODIFIED       na
    2130             :         //       RE-ENGINEERED  na
    2131             : 
    2132             :         // PURPOSE OF THIS FUNCTION:
    2133             :         // Transform angular coordinates in the WINDOW6 coordinate system for
    2134             :         // a given surface into a unit vector in the world coordinate system,
    2135             :         // pointing to the radiation source (for incident radiation) or in
    2136             :         // the direction of propagation (for outgoing radiation)
    2137             : 
    2138             :         using namespace Vectors;
    2139             : 
    2140             :         // Return value
    2141        3840 :         Vector UnitVect; // unit vector direction in world CS
    2142             : 
    2143             :         // Error tolerance is used to make small numbers equal to zero.  Due to precision of pi constant used in E+, performing
    2144             :         // trigonometric operations on those constant will not cause absolutely accurate results
    2145        3840 :         Real64 constexpr ErrorTolerance(1.e-10);
    2146             : 
    2147        3840 :         UnitVect = Vector(0.0, 0.0, 0.0);
    2148             : 
    2149        3840 :         Real64 const sin_Phi = std::sin(Phi);
    2150        3840 :         Real64 const cos_Phi = std::cos(Phi);
    2151             : 
    2152        3840 :         Real64 const sin_Gamma = std::sin(Gamma);
    2153        3840 :         Real64 const cos_Gamma = std::cos(Gamma);
    2154             : 
    2155        3840 :         Real64 const sin_Alpha = std::sin(Alpha);
    2156        3840 :         Real64 const cos_Alpha = std::cos(Alpha);
    2157             : 
    2158        3840 :         Real64 const sin_Theta = std::sin(Theta);
    2159        3840 :         Real64 const cos_Theta = std::cos(Theta);
    2160             : 
    2161        3840 :         switch (RadType) {
    2162        1920 :         case RayIdentificationType::Front_Incident: { // W6 vector will point in direction of propagation, must reverse to get world vector
    2163             :             //  after the W6 vector has been rotated into the world CS
    2164        1920 :             UnitVect.x = sin_Theta * sin_Phi * cos_Gamma * sin_Alpha - sin_Theta * cos_Phi * cos_Alpha + cos_Theta * sin_Gamma * sin_Alpha;
    2165        1920 :             UnitVect.y = sin_Theta * cos_Phi * sin_Alpha + sin_Theta * sin_Phi * cos_Gamma * cos_Alpha + cos_Theta * sin_Gamma * cos_Alpha;
    2166        1920 :             UnitVect.z = -(sin_Theta * sin_Phi * sin_Gamma - cos_Theta * cos_Gamma);
    2167        1920 :             break;
    2168             :         }
    2169        1920 :         case RayIdentificationType::Front_Transmitted: {
    2170        1920 :             UnitVect.x = sin_Theta * cos_Phi * cos_Alpha - sin_Theta * sin_Phi * cos_Gamma * sin_Alpha - cos_Theta * sin_Gamma * sin_Alpha;
    2171        1920 :             UnitVect.y = -(sin_Theta * cos_Phi * sin_Alpha + sin_Theta * sin_Phi * cos_Gamma * cos_Alpha + cos_Theta * sin_Gamma * cos_Alpha);
    2172        1920 :             UnitVect.z = sin_Theta * sin_Phi * sin_Gamma - cos_Theta * cos_Gamma;
    2173        1920 :             break;
    2174             :         }
    2175           0 :         case RayIdentificationType::Front_Reflected: {
    2176           0 :             UnitVect.x = sin_Theta * cos_Phi * cos_Alpha - sin_Theta * sin_Phi * cos_Gamma * sin_Alpha + cos_Theta * sin_Gamma * sin_Alpha;
    2177           0 :             UnitVect.y = cos_Theta * sin_Gamma * cos_Alpha - sin_Theta * cos_Phi * sin_Alpha - sin_Theta * sin_Phi * cos_Gamma * cos_Alpha;
    2178           0 :             UnitVect.z = sin_Theta * sin_Phi * sin_Gamma + cos_Theta * cos_Gamma;
    2179           0 :             break;
    2180             :         }
    2181           0 :         case RayIdentificationType::Back_Incident: {
    2182           0 :             UnitVect.x = sin_Theta * sin_Phi * cos_Gamma * sin_Alpha - sin_Theta * cos_Phi * cos_Alpha - cos_Theta * sin_Gamma * sin_Alpha;
    2183           0 :             UnitVect.y = sin_Theta * cos_Phi * sin_Alpha + sin_Theta * sin_Phi * cos_Gamma * cos_Alpha - cos_Theta * sin_Gamma * cos_Alpha;
    2184           0 :             UnitVect.z = -cos_Theta * cos_Gamma - sin_Theta * sin_Phi * sin_Gamma;
    2185           0 :             break;
    2186             :         }
    2187           0 :         case RayIdentificationType::Back_Transmitted: { // This is same as front reflected
    2188           0 :             UnitVect.x = sin_Theta * cos_Phi * cos_Alpha - sin_Theta * sin_Phi * cos_Gamma * sin_Alpha + cos_Theta * sin_Gamma * sin_Alpha;
    2189           0 :             UnitVect.y = cos_Theta * sin_Gamma * cos_Alpha - sin_Theta * cos_Phi * sin_Alpha - sin_Theta * sin_Phi * cos_Gamma * cos_Alpha;
    2190           0 :             UnitVect.z = sin_Theta * sin_Phi * sin_Gamma + cos_Theta * cos_Gamma;
    2191           0 :             break;
    2192             :         }
    2193           0 :         case RayIdentificationType::Back_Reflected: { // This is same as front transmitted
    2194           0 :             UnitVect.x = sin_Theta * cos_Phi * cos_Alpha - sin_Theta * sin_Phi * cos_Gamma * cos_Alpha - cos_Theta * sin_Gamma * sin_Alpha;
    2195           0 :             UnitVect.y = -(sin_Theta * cos_Phi * sin_Alpha + sin_Theta * sin_Phi * cos_Gamma * cos_Alpha + cos_Theta * sin_Gamma * cos_Alpha);
    2196           0 :             UnitVect.z = sin_Theta * sin_Phi * sin_Gamma - cos_Theta * cos_Gamma;
    2197           0 :             break;
    2198             :         }
    2199           0 :         default:
    2200           0 :             break;
    2201             :         }
    2202             : 
    2203             :         // Remove small numbers from evaluation (due to limited decimal points for pi)
    2204        3840 :         if (std::abs(UnitVect.x) <= ErrorTolerance) UnitVect.x = 0.0;
    2205        3840 :         if (std::abs(UnitVect.y) <= ErrorTolerance) UnitVect.y = 0.0;
    2206        3840 :         if (std::abs(UnitVect.z) <= ErrorTolerance) UnitVect.z = 0.0;
    2207             : 
    2208        3840 :         return UnitVect;
    2209             :     }
    2210             : 
    2211        2833 :     int FindInBasis(EnergyPlusData &state,
    2212             :                     Vector const &RayToFind,             // Ray vector direction in world CS
    2213             :                     const RayIdentificationType RadType, // Type of radiation: Front_Incident, etc.
    2214             :                     int const ISurf,                     // Window Surface number
    2215             :                     [[maybe_unused]] int const IState,   // Complex Fenestration state number
    2216             :                     BasisStruct const &Basis,            // Complex Fenestration basis root
    2217             :                     Real64 &Theta,                       // Theta value for ray
    2218             :                     Real64 &Phi                          // Phi value for ray
    2219             :     )
    2220             :     {
    2221             : 
    2222             :         // SUBROUTINE INFORMATION:
    2223             :         //       AUTHOR         Joe Klems
    2224             :         //       DATE WRITTEN August 2011
    2225             :         //       MODIFIED       na
    2226             :         //       RE-ENGINEERED  na
    2227             : 
    2228             :         using namespace Vectors;
    2229             : 
    2230             :         // Return value
    2231             :         int RayIndex; // Index of ray in basis, zero if ray not in hemisphere
    2232             : 
    2233             :         // INTEGER, INTENT(IN)      ::  IWind  !window index in window list
    2234             : 
    2235             :         int ITheta;   // Table index of Theta
    2236             :         int IPhi;     // Table index of Phi, given ITheta
    2237             :         int IThDn;    // Theta lower table index
    2238             :         int IThUp;    // Theta upper table index
    2239             :         int IPhDn;    // Phi lower table index
    2240             :         int IPhUp;    // Phi upper table index
    2241             :         Real64 Gamma; // Gamma (tilt) angle of window
    2242             :         Real64 Alpha; // Alpha (azimuth) angle of window
    2243             :         Real64 DotProd;
    2244             : 
    2245        2833 :         Theta = 0.0;
    2246        2833 :         Phi = 0.0;
    2247             : 
    2248             :         // Check if surface and vector are pointing in different directions
    2249        2833 :         DotProd = dot(RayToFind, state.dataSurface->Surface(ISurf).NewellSurfaceNormalVector);
    2250        2833 :         if (DotProd <= 0.0) {
    2251        1158 :             RayIndex = 0;
    2252        1158 :             return RayIndex;
    2253             :         }
    2254             : 
    2255             :         // get window tilt and azimuth
    2256        1675 :         Gamma = DataGlobalConstants::DegToRadians * state.dataSurface->Surface(ISurf).Tilt;
    2257        1675 :         Alpha = DataGlobalConstants::DegToRadians * state.dataSurface->Surface(ISurf).Azimuth;
    2258             :         // get the corresponding local Theta, Phi for ray
    2259        1675 :         W6CoordsFromWorldVect(state, RayToFind, RadType, Gamma, Alpha, Theta, Phi);
    2260             : 
    2261        1675 :         if (Theta >= 0.5 * DataGlobalConstants::Pi) { // Ray was in not in correct hemisphere
    2262           0 :             RayIndex = 0;
    2263           0 :             return RayIndex;
    2264             :         }
    2265        1675 :         if (Basis.BasisSymmetryType == DataBSDFWindow::BasisSymmetry::None) {
    2266             :             // Search the basis thetas
    2267        1675 :             if (Theta <= 0.0) {
    2268             :                 // Special case, Theta = 0.; this is always the first basis element
    2269           0 :                 RayIndex = 1;
    2270           0 :                 return RayIndex;
    2271             :             }
    2272             :             // So here Theta > 0
    2273             :             // Note the table searches always go to the limit point, which is not itself a basis element
    2274        1675 :             IThUp = SearchAscTable(Theta, Basis.NThetas + 1, Basis.Thetas);
    2275        1675 :             IThDn = IThUp - 1;
    2276             :             // Determine which of the theta basis points is closer to the Theta value
    2277        1675 :             if (Theta <= Basis.Grid(Basis.BasisIndex(1, IThDn)).UpprTheta) {
    2278             :                 // Note this will take care of both the special cases IThUp=2 and IThUp=NThetas +1
    2279         872 :                 ITheta = IThDn;
    2280             :             } else {
    2281         803 :                 ITheta = IThUp;
    2282             :             }
    2283             :             // Now determine the Phi index
    2284        1675 :             if (Basis.NPhis(ITheta) == 1) {
    2285             :                 // Note that for W6 basis this can only happen for the first basis element
    2286             :                 // If later bases are introduced this logic may have to be redesigned
    2287           0 :                 RayIndex = Basis.BasisIndex(1, ITheta);
    2288           0 :                 return RayIndex;
    2289             :             }
    2290        1675 :             IPhUp = SearchAscTable(Phi, Basis.NPhis(ITheta) + 1, Basis.Phis(ITheta, _));
    2291        1675 :             IPhDn = IPhUp - 1;
    2292        1675 :             if (Phi <= Basis.Grid(Basis.BasisIndex(IPhDn, ITheta)).UpprPhi) {
    2293         862 :                 IPhi = IPhDn;
    2294             :             } else {
    2295         813 :                 if (IPhUp == Basis.NPhis(ITheta) + 1) {
    2296             :                     // Phi is above upper limit for highest Phi basis element, meaning it is closer to 2Pi,
    2297             :                     // i.e., the first element
    2298         114 :                     IPhi = 1;
    2299             :                 } else {
    2300         699 :                     IPhi = IPhUp;
    2301             :                 }
    2302             :             }
    2303        1675 :             RayIndex = Basis.BasisIndex(IPhi, ITheta);
    2304        1675 :             return RayIndex;
    2305           0 :         } else if (Basis.BasisSymmetryType == DataBSDFWindow::BasisSymmetry::Axisymmetric) {
    2306             :             // Search the basis thetas
    2307           0 :             if (Theta <= 0.0) {
    2308             :                 // Special case, Theta = 0.; this is always the first basis element
    2309           0 :                 RayIndex = 1;
    2310           0 :                 return RayIndex;
    2311             :             }
    2312             :             // So here Theta > 0
    2313             :             // Note the table searches always go to the limit point, which is not itself a basis element
    2314           0 :             IThUp = SearchAscTable(Theta, Basis.NThetas + 1, Basis.Thetas);
    2315           0 :             IThDn = IThUp - 1;
    2316             :             // Determine which of the theta basis points is closer to the Theta value
    2317           0 :             if (Theta <= Basis.Grid(Basis.BasisIndex(1, IThDn)).UpprTheta) {
    2318             :                 // Note this will take care of both the special cases IThUp=2 and IThUp=NThetas +1
    2319           0 :                 ITheta = IThDn;
    2320             :             } else {
    2321           0 :                 ITheta = IThUp;
    2322             :             }
    2323           0 :             RayIndex = Basis.BasisIndex(1, ITheta);
    2324           0 :             return RayIndex;
    2325             :         }
    2326             :         // No other type is implemented
    2327           0 :         RayIndex = 0;
    2328             : 
    2329           0 :         return RayIndex;
    2330             :     }
    2331             : 
    2332        3327 :     void W6CoordsFromWorldVect([[maybe_unused]] EnergyPlusData &state,
    2333             :                                Vector const &RayVect,               // Ray vector direction in world CS
    2334             :                                const RayIdentificationType RadType, // Type of radiation: Front_Incident, etc.
    2335             :                                Real64 const Gamma,                  // Surface tilt angle, world coordinate system
    2336             :                                Real64 const Alpha,                  // Surface azimuth, world coordinate system
    2337             :                                Real64 &Theta,                       // Polar angle in W6 Coords
    2338             :                                Real64 &Phi                          // Azimuthal angle in W6 Coords
    2339             :     )
    2340             :     {
    2341             : 
    2342             :         // SUBROUTINE INFORMATION:
    2343             :         //       AUTHOR         Joe Klems
    2344             :         //       DATE WRITTEN   August 2011
    2345             :         //       MODIFIED       na
    2346             :         //       RE-ENGINEERED  na
    2347             : 
    2348             :         // PURPOSE OF THIS FUNCTION:
    2349             :         // Invert the transformation from W6 to world coordinates to
    2350             :         // calculate the theta, phi corresponding to a given ray direction
    2351             :         // in the world coordinate system, for a window with a
    2352             :         // given rotation and tilt (Gamma and Alpha)
    2353             :         //  (needed for locating the sun direction in the local coordinate system)
    2354             : 
    2355             :         using namespace Vectors;
    2356             : 
    2357        3327 :         Real64 Cost(0.0); // Temp for cos theta
    2358             :         Real64 Sint;      // Temp for sin theta
    2359             :         Real64 Psi;       // Temp for phi before rotation adjustment
    2360             :         Real64 RdotX;     // Temp variable for manipulating .dot. produt
    2361             :         Real64 RdotY;     // Temp variable for manipulating .dot. produt
    2362             :         Real64 RdotZ;     // Temp variable for manipulating .dot. produt
    2363             : 
    2364             :         // Object Data
    2365        6654 :         Vector W6x; // W6 x coordinate unit vector
    2366        6654 :         Vector W6y; // W6 y coordinate unit vector
    2367        6654 :         Vector W6z; // W6 z coordinate unit vector
    2368             : 
    2369             :         // define the local W6 coordinate vectors
    2370        3327 :         W6x.x = std::cos(Alpha);
    2371        3327 :         W6x.y = -std::sin(Alpha);
    2372        3327 :         W6x.z = 0.0;
    2373        3327 :         W6y.x = -std::cos(Gamma) * std::sin(Alpha);
    2374        3327 :         W6y.y = -std::cos(Gamma) * std::cos(Alpha);
    2375        3327 :         W6y.z = std::sin(Gamma);
    2376        3327 :         W6z.x = -std::sin(Gamma) * std::sin(Alpha);
    2377        3327 :         W6z.y = -std::sin(Gamma) * std::cos(Alpha);
    2378        3327 :         W6z.z = -std::cos(Gamma);
    2379             : 
    2380        3327 :         switch (RadType) {
    2381        3327 :         case RayIdentificationType::Front_Incident: {
    2382        3327 :             RdotZ = dot(W6z, RayVect);
    2383        3327 :             Cost = -RdotZ;
    2384        3327 :             Sint = std::sqrt(1.0 - pow_2(Cost));
    2385        3327 :             Theta = std::acos(Cost);
    2386        3327 :             RdotY = dot(W6y, RayVect);
    2387        3327 :             RdotX = dot(W6x, RayVect);
    2388        3327 :             Psi = std::atan2(-RdotY / Sint, -RdotX / Sint);
    2389        3327 :             if (Psi < 0.0) {
    2390        3327 :                 Phi = 2.0 * DataGlobalConstants::Pi + Psi;
    2391             :             } else {
    2392           0 :                 Phi = Psi;
    2393             :             }
    2394        3327 :             break;
    2395             :         }
    2396           0 :         case RayIdentificationType::Front_Transmitted: {
    2397           0 :             Cost = dot(W6z, RayVect);
    2398           0 :             Sint = std::sqrt(1.0 - pow_2(Cost));
    2399           0 :             Theta = std::acos(Cost);
    2400           0 :             RdotY = dot(W6y, RayVect);
    2401           0 :             RdotX = dot(W6x, RayVect);
    2402           0 :             Psi = std::atan2(RdotY / Sint, RdotX / Sint);
    2403           0 :             if (Psi < 0.0) {
    2404           0 :                 Phi = 2.0 * DataGlobalConstants::Pi + Psi;
    2405             :             } else {
    2406           0 :                 Phi = Psi;
    2407             :             }
    2408           0 :             break;
    2409             :         }
    2410           0 :         case RayIdentificationType::Front_Reflected: {
    2411           0 :             RdotZ = dot(W6z, RayVect);
    2412           0 :             Cost = -RdotZ;
    2413           0 :             Sint = std::sqrt(1.0 - pow_2(Cost));
    2414           0 :             Theta = std::acos(Cost);
    2415           0 :             RdotY = dot(W6y, RayVect);
    2416           0 :             RdotX = dot(W6x, RayVect);
    2417           0 :             Psi = std::atan2(RdotY / Sint, RdotX / Sint);
    2418           0 :             if (Psi < 0.0) {
    2419           0 :                 Phi = 2.0 * DataGlobalConstants::Pi + Psi;
    2420             :             } else {
    2421           0 :                 Phi = Psi;
    2422             :             }
    2423           0 :             break;
    2424             :         }
    2425           0 :         case RayIdentificationType::Back_Incident: {
    2426           0 :             Cost = dot(W6z, RayVect);
    2427           0 :             Sint = std::sqrt(1.0 - pow_2(Cost));
    2428           0 :             Theta = std::acos(Cost);
    2429           0 :             RdotY = dot(W6y, RayVect);
    2430           0 :             RdotX = dot(W6x, RayVect);
    2431           0 :             Psi = std::atan2(-RdotY / Sint, -RdotX / Sint);
    2432           0 :             if (Psi < 0.0) {
    2433           0 :                 Phi = 2 * DataGlobalConstants::Pi + Psi;
    2434             :             } else {
    2435           0 :                 Phi = Psi;
    2436             :             }
    2437           0 :             break;
    2438             :         }
    2439           0 :         case RayIdentificationType::Back_Transmitted: { // This is same as front reflected
    2440           0 :             RdotZ = dot(W6z, RayVect);
    2441           0 :             Cost = -RdotZ;
    2442           0 :             Sint = std::sqrt(1.0 - pow_2(Cost));
    2443           0 :             Theta = std::acos(Cost);
    2444           0 :             RdotY = dot(W6y, RayVect);
    2445           0 :             RdotX = dot(W6x, RayVect);
    2446           0 :             Psi = std::atan2(RdotY / Sint, RdotX / Sint);
    2447           0 :             if (Psi < 0.0) {
    2448           0 :                 Phi = 2.0 * DataGlobalConstants::Pi + Psi;
    2449             :             } else {
    2450           0 :                 Phi = Psi;
    2451             :             }
    2452           0 :             break;
    2453             :         }
    2454           0 :         case RayIdentificationType::Back_Reflected: { // This is same as front transmitted
    2455           0 :             Cost = dot(W6z, RayVect);
    2456           0 :             Sint = std::sqrt(1.0 - pow_2(Cost));
    2457           0 :             Theta = std::acos(Cost);
    2458           0 :             RdotY = dot(W6y, RayVect);
    2459           0 :             RdotX = dot(W6x, RayVect);
    2460           0 :             Psi = std::atan2(RdotY / Sint, RdotX / Sint);
    2461           0 :             if (Psi < 0.0) {
    2462           0 :                 Phi = 2.0 * DataGlobalConstants::Pi + Psi;
    2463             :             } else {
    2464           0 :                 Phi = Psi;
    2465             :             }
    2466           0 :             break;
    2467             :         }
    2468           0 :         default:
    2469           0 :             assert(false);
    2470             :             break;
    2471             :         }
    2472        3327 :         if (std::abs(Cost) < DataGlobalConstants::rTinyValue) Cost = 0.0;
    2473        3327 :         if (Cost < 0.0) Theta = DataGlobalConstants::Pi - Theta; // This signals ray out of hemisphere
    2474        3327 :     }
    2475             : 
    2476       37990 :     void CalcComplexWindowThermal(EnergyPlusData &state,
    2477             :                                   int const SurfNum,          // Surface number
    2478             :                                   int &ConstrNum,             // Construction number
    2479             :                                   Real64 const HextConvCoeff, // Outside air film conductance coefficient
    2480             :                                   Real64 &SurfInsideTemp,     // Inside window surface temperature
    2481             :                                   Real64 &SurfOutsideTemp,    // Outside surface temperature (C)
    2482             :                                   Real64 &SurfOutsideEmiss,
    2483             :                                   DataBSDFWindow::Condition const CalcCondition // Calucation condition (summer, winter or no condition)
    2484             :     )
    2485             :     {
    2486             : 
    2487             :         // SUBROUTINE INFORMATION:
    2488             :         //       AUTHOR         B. Griffith
    2489             :         //       DATE WRITTEN   October 2009
    2490             :         //       MODIFIED       Simon Vidanovic
    2491             :         //       RE-ENGINEERED  September 2011
    2492             : 
    2493             :         // PURPOSE OF THIS SUBROUTINE:
    2494             :         // wrapper between E+ and TARCOG
    2495             : 
    2496             :         // METHODOLOGY EMPLOYED:
    2497             :         // draft out an attempt for proof-of-concept, to reuse native TARCOG implementation
    2498             :         // based off of 1-26-2009 version of WinCOG/TARCOG solution from Carli, Inc.
    2499             : 
    2500             :         using namespace DataBSDFWindow;
    2501             :         using Psychrometrics::PsyCpAirFnW;
    2502             :         using Psychrometrics::PsyTdpFnWPb;
    2503             :         using ScheduleManager::GetCurrentScheduleValue;
    2504             :         using TARCOGGassesParams::maxgas;
    2505             :         using TARCOGMain::TARCOG90;
    2506             :         using TARCOGParams::maxlay;
    2507             :         using TARCOGParams::maxlay1;
    2508             : 
    2509             :         // Locals
    2510             :         // SUBROUTINE ARGUMENT DEFINITIONS:
    2511             :         // (temperature of innermost face) [C]
    2512             :         // INTEGER, INTENT(IN)        :: CurrentThermalModelNumber
    2513             : 
    2514             :         // SUBROUTINE PARAMETER DEFINITIONS:
    2515             :         // INTEGER,  PARAMETER :: maxlay = 100 ! maximum number of layers (including laminates)
    2516             :         // INTEGER,  PARAMETER :: maxgas = 10  ! maximum number of individual gasses
    2517             :         // INTEGER, PARAMETER :: maxlay1  = maxlay+1     ! maximum number of 'gaps', including in and out (maxlay+1)
    2518             :         // REAL(r64), PARAMETER :: StefanBoltzmannConst = 5.6697d-8   ! Stefan-Boltzmann constant in W/(m2*K4)
    2519             : 
    2520             :         // TARCOG Inputs:
    2521       37990 :         int nlayer(0);     // Number of glazing layers
    2522       37990 :         int iwd(0);        // Wind direction:  0 - windward, 1 - leeward
    2523       37990 :         Real64 tout(0.0);  // Outdoor temperature [K]
    2524       37990 :         Real64 tind(0.0);  // Indoor temperature [K]
    2525       37990 :         Real64 trmin(0.0); // Indoor mean radiant temperature [K]
    2526       37990 :         Real64 wso(0.0);   // Outdoor wind speed [m/s]
    2527       37990 :         Real64 wsi(0.0);   // Inside forced air speed [m/s]
    2528       37990 :         Real64 dir(0.0);   // Direct solar radiation [W/m^2]
    2529       37990 :         int isky(0);       // Flag for sky temperature (Tsky) and sky emittance (esky)
    2530             :         //                      0 - both tsky and esky are specified
    2531             :         //                      1 - tsky specified, esky = 1
    2532             :         //                      2 - Swinbank model for effective sky emittance
    2533       37990 :         Real64 tsky(0.0);             // Night sky temperature [K]
    2534       37990 :         Real64 esky(0.0);             // Effective night sky emittance
    2535       37990 :         Real64 fclr(0.0);             // Fraction of sky that is clear
    2536             :         Real64 VacuumPressure;        // maximal pressure for gas to be considered as vacuum [Pa]
    2537             :         Real64 VacuumMaxGapThickness; // maximal gap thickness for which vacuum calculation will work without issuing
    2538             :         // warning message
    2539             : 
    2540       37990 :         auto &gap = state.dataWindowComplexManager->gap;
    2541       37990 :         auto &thick = state.dataWindowComplexManager->thick;
    2542       37990 :         auto &scon = state.dataWindowComplexManager->scon;
    2543       37990 :         auto &tir = state.dataWindowComplexManager->tir;
    2544       37990 :         auto &emis = state.dataWindowComplexManager->emis;
    2545       37990 :         auto &SupportPlr = state.dataWindowComplexManager->SupportPlr;
    2546       37990 :         auto &PillarSpacing = state.dataWindowComplexManager->PillarSpacing;
    2547       37990 :         auto &PillarRadius = state.dataWindowComplexManager->PillarRadius;
    2548       37990 :         auto &asol = state.dataWindowComplexManager->asol;
    2549       37990 :         auto &presure = state.dataWindowComplexManager->presure;
    2550       37990 :         auto &GapDefMax = state.dataWindowComplexManager->GapDefMax;
    2551       37990 :         auto &YoungsMod = state.dataWindowComplexManager->YoungsMod;
    2552       37990 :         auto &PoissonsRat = state.dataWindowComplexManager->PoissonsRat;
    2553       37990 :         auto &LayerDef = state.dataWindowComplexManager->LayerDef;
    2554       37990 :         auto &iprop = state.dataWindowComplexManager->iprop;
    2555       37990 :         auto &frct = state.dataWindowComplexManager->frct;
    2556       37990 :         auto &gcon = state.dataWindowComplexManager->gcon;
    2557       37990 :         auto &gvis = state.dataWindowComplexManager->gvis;
    2558       37990 :         auto &gcp = state.dataWindowComplexManager->gcp;
    2559       37990 :         auto &wght = state.dataWindowComplexManager->wght;
    2560       37990 :         auto &gama = state.dataWindowComplexManager->gama;
    2561       37990 :         auto &nmix = state.dataWindowComplexManager->nmix;
    2562       37990 :         auto &ibc = state.dataWindowComplexManager->ibc;
    2563       37990 :         auto &Atop = state.dataWindowComplexManager->Atop;
    2564       37990 :         auto &Abot = state.dataWindowComplexManager->Abot;
    2565       37990 :         auto &Al = state.dataWindowComplexManager->Al;
    2566       37990 :         auto &Ar = state.dataWindowComplexManager->Ar;
    2567       37990 :         auto &Ah = state.dataWindowComplexManager->Ah;
    2568       37990 :         auto &SlatThick = state.dataWindowComplexManager->SlatThick;
    2569       37990 :         auto &SlatWidth = state.dataWindowComplexManager->SlatWidth;
    2570       37990 :         auto &SlatAngle = state.dataWindowComplexManager->SlatAngle;
    2571       37990 :         auto &SlatCond = state.dataWindowComplexManager->SlatCond;
    2572       37990 :         auto &SlatSpacing = state.dataWindowComplexManager->SlatSpacing;
    2573       37990 :         auto &SlatCurve = state.dataWindowComplexManager->SlatCurve;
    2574       37990 :         auto &vvent = state.dataWindowComplexManager->vvent;
    2575       37990 :         auto &tvent = state.dataWindowComplexManager->tvent;
    2576       37990 :         auto &LayerType = state.dataWindowComplexManager->LayerType;
    2577       37990 :         auto &nslice = state.dataWindowComplexManager->nslice;
    2578       37990 :         auto &LaminateA = state.dataWindowComplexManager->LaminateA;
    2579       37990 :         auto &LaminateB = state.dataWindowComplexManager->LaminateB;
    2580       37990 :         auto &sumsol = state.dataWindowComplexManager->sumsol;
    2581       37990 :         auto &theta = state.dataWindowComplexManager->theta;
    2582       37990 :         auto &q = state.dataWindowComplexManager->q;
    2583       37990 :         auto &qv = state.dataWindowComplexManager->qv;
    2584       37990 :         auto &hcgap = state.dataWindowComplexManager->hcgap;
    2585       37990 :         auto &hrgap = state.dataWindowComplexManager->hrgap;
    2586       37990 :         auto &hg = state.dataWindowComplexManager->hg;
    2587       37990 :         auto &hr = state.dataWindowComplexManager->hr;
    2588       37990 :         auto &hs = state.dataWindowComplexManager->hs;
    2589       37990 :         auto &Ra = state.dataWindowComplexManager->Ra;
    2590       37990 :         auto &Nu = state.dataWindowComplexManager->Nu;
    2591       37990 :         auto &Keff = state.dataWindowComplexManager->Keff;
    2592       37990 :         auto &ShadeGapKeffConv = state.dataWindowComplexManager->ShadeGapKeffConv;
    2593             : 
    2594       37990 :         Real64 totsol(0.0);  // Total solar transmittance of the IGU
    2595       37990 :         Real64 tilt(0.0);    // Window tilt [degrees]
    2596       37990 :         Real64 height(0.0);  // IGU cavity height [m]
    2597       37990 :         Real64 heightt(0.0); // Total window height [m]
    2598       37990 :         Real64 width(0.0);   // Window width [m]
    2599             : 
    2600             :         // Deflection
    2601             :         // Tarcog requires deflection as input parameters.  Deflection is NOT used in EnergyPlus simulations
    2602             :         TARCOGParams::DeflectionCalculation CalcDeflection; // Deflection calculation flag:
    2603             :         //    0 - no deflection calculations
    2604             :         //    1 - perform deflection calculation (input is Pressure/Temp)
    2605             :         //    2 - perform deflection calculation (input is measured deflection)
    2606             :         Real64 Pa;        // Atmospheric (outside/inside) pressure (used onlu if CalcDeflection = 1)
    2607             :         Real64 Pini;      // Initial presssure at time of fabrication (used only if CalcDeflection = 1)
    2608             :         Real64 Tini;      // Initial temperature at time of fabrication (used only if CalcDeflection = 1)
    2609       37990 :         Real64 hin(0.0);  // Indoor combined film coefficient (if non-zero) [W/m^2.K]
    2610       37990 :         Real64 hout(0.0); // Outdoor combined film coefficient (if non-zero) [W/m^2.K]
    2611       37990 :         TARCOGGassesParams::Stdrd standard(TARCOGGassesParams::Stdrd::ISO15099); // Calculation standard switch:
    2612             :         //                 1 - ISO 15099,
    2613             :         //                 2 - EN673 / ISO 10292 Declared,
    2614             :         //                 3 - EN673 / ISO 10292 Design.
    2615       37990 :         TARCOGParams::TARCOGThermalModel ThermalMod = TARCOGParams::TARCOGThermalModel::ISO15099; // Thermal model:
    2616             :         //                 0 - ISO15099
    2617             :         //                 1 - Scaled Cavity Width (SCW)
    2618             :         //                 2 - Convective Scalar Model (CSM)
    2619       75980 :         std::string Debug_dir;          // Target directory for debug files (pointer to a character array)
    2620       75980 :         std::string Debug_file("Test"); // Template file name used to create debug output files
    2621       37990 :         std::int32_t Window_ID(-1);     // ID of the window (long integer value, passed by W6)
    2622       37990 :         std::int32_t IGU_ID(-1);        // ID of the IGU (long integer value, passed by W6)
    2623       37990 :         Real64 SDScalar(0.0);           // SD convection factor (value between 0 and 1)
    2624             :         //                 0.0 - No SD layer
    2625             :         //                 1.0 - Closed SD
    2626             :         //               Notes:   * vvent, tvent, Atop, Abot, Al, Ar and Ah are considered for SD layers only.
    2627             :         //                       ** SlatThick, SlatWidth, SlatAngle, SlatCond, SlatSpacing, SlatCurve
    2628             :         //                          are used for Venetian blind layers only.
    2629             :         //                      *** For vvent & tvent: vvent(1) - exterior, vvent(nlayer+1) - interior.
    2630             :         //                     **** Forced ventilation calculation is not active at this time.
    2631             :         // TARCOG Output:
    2632             : 
    2633       37990 :         Real64 ufactor(0.0);           // Center of glass U-value [W/m^2.K]
    2634       37990 :         Real64 sc(0.0);                // Shading Coefficient
    2635       37990 :         Real64 hflux(0.0);             // Net heat flux between room and window [W/m^2]
    2636       37990 :         Real64 hcin(0.0);              // Indoor convective surface heat transfer coefficient  [W/m^2.K]
    2637       37990 :         Real64 hcout(0.0);             // Outdoor convective surface heat transfer coefficient [W/m^2.K]
    2638       37990 :         Real64 hrin(0.0);              // Indoor radiative surface heat transfer coefficient [W/m^2.K]
    2639       37990 :         Real64 hrout(0.0);             // Outdoor radiative surface heat transfer coefficient [W/m^2.K]
    2640       37990 :         Real64 shgc(0.0);              // Solar heat gain coefficient - per ISO 15099
    2641       37990 :         Real64 shgct(0.0);             // Solar heat gain coefficient - per old procedure
    2642       37990 :         Real64 tamb(0.0);              // Outdoor environmental temperature [K]
    2643       37990 :         Real64 troom(0.0);             // Indoor environmental temperature [K]
    2644       37990 :         Real64 he(0.0);                // External heat transfer coefficient [W/m^2.K] - EN673 and ISO 10292 procedure
    2645       37990 :         Real64 hi(0.0);                // Internal heat transfer coefficient [W/m^2.K] - EN673 and ISO 10292 procedure
    2646       37990 :         int nperr(0);                  // Error code
    2647       37990 :         Real64 ShadeEmisRatioOut(0.0); // Ratio of modified to glass emissivity at the outermost glazing surface
    2648       37990 :         Real64 ShadeEmisRatioIn(0.0);  // Ratio of modified to glass emissivity at the innermost glazing surface
    2649       37990 :         Real64 ShadeHcRatioOut(0.0);   // Ratio of modified to unshaded Hc at the outermost glazing surface
    2650       37990 :         Real64 ShadeHcRatioIn(0.0);    // Ratio of modified to unshaded Hc at the innermost glazing surface
    2651       37990 :         Real64 HcUnshadedOut(0.0);     // Hc value at outdoor surface of an unshaded subsystem [W/m^2.K]
    2652       37990 :         Real64 HcUnshadedIn(0.0);      // Hc value at indoor surface of an unshaded subsystem [W/m^2.K]
    2653             : 
    2654             :         int ZoneNum; // Zone number corresponding to SurfNum
    2655             : 
    2656             :         int i;
    2657             : 
    2658             :         int TotLay; // Total number of layers in a construction
    2659             :         //   (sum of solid layers and gap layers)
    2660             :         int Lay;                  // Layer number
    2661             :         int LayPtr;               // Material number for a layer
    2662             :         int IGlass;               // glass layer number (1,2,3,...)
    2663             :         int IGap;                 // Gap layer number (1,2,...)
    2664             :         int TotGlassLay;          // Total number of glass layers in a construction
    2665             :         int k;                    // Layer counter
    2666             :         int SurfNumAdj;           // An interzone surface's number in the adjacent zone
    2667             :         int ZoneNumAdj;           // An interzone surface's adjacent zone number
    2668             :         WinShadingType ShadeFlag; // Flag indicating whether shade or blind is on, and shade/blind position
    2669             :         int IMix;
    2670             : 
    2671             :         Real64 IncidentSolar;       // Solar incident on outside of window (W)
    2672             :         Real64 ConvHeatFlowNatural; // Convective heat flow from gap between glass and interior shade or blind (W)
    2673             :         Real64 ShadeArea;           // shade/blind area (m2)
    2674             :         Real64 sconsh;              // shade/blind conductance (W/m2-K)
    2675             :         Real64 CondHeatGainShade;   // Conduction through shade/blind, outside to inside (W)
    2676             : 
    2677             :         Real64 ShGlReflFacIR; // Factor for long-wave inter-reflection between shade/blind and adjacent glass
    2678             :                               //        Real64 RhoGlIR1; // Long-wave reflectance of glass surface facing shade/blind; 1=exterior shade/blind,
    2679             :         Real64 RhoGlIR2;
    2680             :         //  2=interior shade/blind
    2681             :         Real64 RhoShIR1; // Long-wave reflectance of shade/blind surface facing glass; 1=interior shade/blind,
    2682             :         Real64 RhoShIR2;
    2683             :         //  2=exterior shade/blind
    2684             :         Real64 EpsShIR1; // Long-wave emissivity of shade/blind surface facing glass; 1=interior shade/blind,
    2685             :         Real64 EpsShIR2;
    2686             :         //  2=exterior shade/blind
    2687             :         Real64 TauShIR;            // Long-wave transmittance of isolated shade/blind
    2688             :         Real64 NetIRHeatGainShade; // Net IR heat gain to zone from interior shade/blind (W)
    2689             :         Real64 NetIRHeatGainGlass; // Net IR heat gain to zone from shade/blind side of glass when interior
    2690             :         //  shade/blind is present. Zero if shade/blind has zero IR transmittance (W)
    2691             :         Real64 ConvHeatGainFrZoneSideOfShade; // Convective heat gain to zone from side of interior shade facing zone (W)
    2692             :         Real64 ConvHeatGainFrZoneSideOfGlass; // Convective heat gain to zone from side of glass facing zone when
    2693             :         //  no interior shade/blind is present (W)
    2694             :         Real64 CondHeatGainGlass;     // Conduction through inner glass layer, outside to inside (W)
    2695             :         Real64 TotAirflowGap;         // Total volumetric airflow through window gap (m3/s)
    2696             :         Real64 TAirflowGapOutlet;     // Temperature of air leaving airflow gap between glass panes (K)
    2697             :         Real64 TAirflowGapOutletC;    // Temperature of air leaving airflow gap between glass panes (C)
    2698             :         Real64 ConvHeatFlowForced;    // Convective heat flow from forced airflow gap (W)
    2699             :         Real64 InletAirHumRat;        // Humidity ratio of air from window gap entering fan
    2700             :         Real64 ZoneTemp;              // Zone air temperature (C)
    2701             :         Real64 CpAirOutlet;           // Heat capacity of air from window gap (J/kg-K)
    2702             :         Real64 CpAirZone;             // Heat capacity of zone air (J/kg-K)
    2703             :         Real64 ConvHeatGainToZoneAir; // Convective heat gain to zone air from window gap airflow (W)
    2704             :                                       //        int ConstrNumSh; // Construction number with shading device
    2705       37990 :         int CalcSHGC(0);              // SHGC calculations are not necessary for E+ run
    2706       37990 :         int NumOfIterations(0);
    2707             : 
    2708             :         int GasType; // locally used coefficient to point at correct gas type
    2709             :         int ICoeff;
    2710             : 
    2711       75980 :         std::string tarcogErrorMessage; // store error text from tarcog
    2712             : 
    2713             :         // Simon: locally used variables
    2714             :         int ngllayer;
    2715             :         int nglface;
    2716             :         int nglfacep;
    2717             :         int TempInt;
    2718             :         int PillarPtr;
    2719             :         int DeflectionPtr;
    2720             :         int GasPointer;
    2721             :         int ThermalModelNum;
    2722             :         Real64 rmir; // IR radiance of window's interior surround (W/m2)
    2723             :         Real64 outir;
    2724             :         Real64 Ebout;
    2725             :         Real64 dominantGapWidth; // store value for dominant gap width.  Used for airflow calculations
    2726             :         Real64 edgeGlCorrFac;
    2727             : 
    2728             :         Real64 SrdSurfTempAbs; // Absolute temperature of a surrounding surface
    2729             :         Real64 SrdSurfViewFac; // View factor of a surrounding surface
    2730             :         Real64 OutSrdIR;
    2731             : 
    2732             :         // fill local vars
    2733             : 
    2734       37990 :         CalcDeflection = TARCOGParams::DeflectionCalculation::NONE;
    2735       37990 :         CalcSHGC = 0;
    2736             : 
    2737       37990 :         if (CalcCondition == DataBSDFWindow::Condition::Invalid) {
    2738       37968 :             ConstrNum = state.dataSurface->Surface(SurfNum).Construction;
    2739       37968 :             SurfNumAdj = state.dataSurface->Surface(SurfNum).ExtBoundCond;
    2740       37968 :             ShadeFlag = state.dataSurface->SurfWinShadingFlag(SurfNum);
    2741             :         }
    2742             : 
    2743       37990 :         TotGlassLay = state.dataConstruction->Construct(ConstrNum).TotGlassLayers;
    2744       37990 :         ngllayer = state.dataConstruction->Construct(ConstrNum).TotGlassLayers;
    2745       37990 :         nglface = 2 * ngllayer;
    2746       37990 :         nglfacep = nglface;
    2747       37990 :         hrin = 0.0;
    2748       37990 :         hcin = 0.0;
    2749       37990 :         hrout = 0.0;
    2750       37990 :         hcout = 0.0;
    2751             : 
    2752       37990 :         Pa = state.dataEnvrn->OutBaroPress;
    2753             : 
    2754       37990 :         ThermalModelNum = state.dataConstruction->Construct(ConstrNum).BSDFInput.ThermalModel;
    2755       37990 :         standard = state.dataHeatBal->WindowThermalModel(ThermalModelNum).CalculationStandard;
    2756       37990 :         ThermalMod = state.dataHeatBal->WindowThermalModel(ThermalModelNum).ThermalModel;
    2757       37990 :         CalcDeflection = state.dataHeatBal->WindowThermalModel(ThermalModelNum).DeflectionModel;
    2758       37990 :         SDScalar = state.dataHeatBal->WindowThermalModel(ThermalModelNum).SDScalar;
    2759       37990 :         VacuumPressure = state.dataHeatBal->WindowThermalModel(ThermalModelNum).VacuumPressureLimit;
    2760       37990 :         Tini = state.dataHeatBal->WindowThermalModel(ThermalModelNum).InitialTemperature - DataGlobalConstants::KelvinConv;
    2761       37990 :         Pini = state.dataHeatBal->WindowThermalModel(ThermalModelNum).InitialPressure;
    2762             : 
    2763       37990 :         nlayer = state.dataConstruction->Construct(ConstrNum).TotSolidLayers;
    2764       37990 :         isky = 3; // IR radiation is provided from external source
    2765       37990 :         iwd = 0;  // assume windward for now.  TODO compare surface normal with wind direction
    2766             : 
    2767       37990 :         if (CalcCondition == DataBSDFWindow::Condition::Invalid) {
    2768       37968 :             ZoneNum = state.dataSurface->Surface(SurfNum).Zone;
    2769       37968 :             Real64 RefAirTemp = state.dataSurface->Surface(SurfNum).getInsideAirTemperature(state, SurfNum);
    2770       37968 :             tind = RefAirTemp + DataGlobalConstants::KelvinConv; // Inside air temperature
    2771             : 
    2772             :             // now get "outside" air temperature
    2773       37968 :             if (SurfNumAdj > 0) { // Interzone window
    2774             : 
    2775           0 :                 ZoneNumAdj = state.dataSurface->Surface(SurfNumAdj).Zone;
    2776           0 :                 RefAirTemp = state.dataSurface->Surface(SurfNumAdj).getInsideAirTemperature(state, SurfNumAdj);
    2777           0 :                 tout = RefAirTemp + DataGlobalConstants::KelvinConv; // outside air temperature
    2778             : 
    2779           0 :                 tsky = state.dataHeatBal->ZoneMRT(ZoneNumAdj) +
    2780             :                        DataGlobalConstants::KelvinConv; // TODO this misses IR from sources such as high temp radiant and baseboards
    2781             : 
    2782             :                 //  ! Add long-wave radiation from adjacent zone absorbed by glass layer closest to the adjacent zone.
    2783             :                 //  AbsRadGlassFace(1) = AbsRadGlassFace(1) + SurfQRadThermInAbs(SurfNumAdj)
    2784             :                 //  ! The IR radiance of this window's "exterior" surround is the IR radiance
    2785             :                 //  ! from surfaces and high-temp radiant sources in the adjacent zone
    2786           0 :                 outir = state.dataSurface->SurfWinIRfromParentZone(SurfNumAdj) + state.dataHeatBalSurf->SurfQdotRadHVACInPerArea(SurfNumAdj);
    2787             : 
    2788             :             } else { // Exterior window (ExtBoundCond = 0)
    2789             :                 // Calculate LWR from surrounding surfaces if defined for an exterior window
    2790       37968 :                 OutSrdIR = 0;
    2791       37968 :                 if (state.dataGlobal->AnyLocalEnvironmentsInModel) {
    2792           0 :                     if (state.dataSurface->Surface(SurfNum).SurfHasSurroundingSurfProperty) {
    2793           0 :                         int SrdSurfsNum = state.dataSurface->Surface(SurfNum).SurfSurroundingSurfacesNum;
    2794           0 :                         auto &SrdSurfsProperty = state.dataSurface->SurroundingSurfsProperty(SrdSurfsNum);
    2795           0 :                         for (int SrdSurfNum = 1; SrdSurfNum <= SrdSurfsProperty.TotSurroundingSurface; SrdSurfNum++) {
    2796           0 :                             SrdSurfViewFac = SrdSurfsProperty.SurroundingSurfs(SrdSurfNum).ViewFactor;
    2797           0 :                             SrdSurfTempAbs = GetCurrentScheduleValue(state, SrdSurfsProperty.SurroundingSurfs(SrdSurfNum).TempSchNum) +
    2798             :                                              DataGlobalConstants::KelvinConv;
    2799           0 :                             OutSrdIR += DataGlobalConstants::StefanBoltzmann * SrdSurfViewFac * (pow_4(SrdSurfTempAbs));
    2800             :                         }
    2801             :                     }
    2802             :                 }
    2803       37968 :                 if (state.dataSurface->Surface(SurfNum).ExtWind) { // Window is exposed to wind (and possibly rain)
    2804       37968 :                     if (state.dataEnvrn->IsRain) {                 // Raining: since wind exposed, outside window surface gets wet
    2805           0 :                         tout = state.dataSurface->SurfOutWetBulbTemp(SurfNum) + DataGlobalConstants::KelvinConv;
    2806             :                     } else { // Dry
    2807       37968 :                         tout = state.dataSurface->SurfOutDryBulbTemp(SurfNum) + DataGlobalConstants::KelvinConv;
    2808             :                     }
    2809             :                 } else { // Window not exposed to wind
    2810           0 :                     tout = state.dataSurface->SurfOutDryBulbTemp(SurfNum) + DataGlobalConstants::KelvinConv;
    2811             :                 }
    2812             :                 // tsky = SkyTemp + TKelvin
    2813       37968 :                 tsky = state.dataEnvrn->SkyTempKelvin;
    2814       37968 :                 Ebout = state.dataWindowComplexManager->sigma * pow_4(tout);
    2815      113904 :                 outir = state.dataSurface->Surface(SurfNum).ViewFactorSkyIR *
    2816       75936 :                             (state.dataSurface->SurfAirSkyRadSplit(SurfNum) * state.dataWindowComplexManager->sigma * pow_4(tsky) +
    2817       75936 :                              (1.0 - state.dataSurface->SurfAirSkyRadSplit(SurfNum)) * Ebout) +
    2818       37968 :                         state.dataSurface->Surface(SurfNum).ViewFactorGroundIR * Ebout + OutSrdIR;
    2819             :             }
    2820             : 
    2821       37968 :             hin = state.dataHeatBalSurf->SurfHConvInt(SurfNum); // Room-side surface convective film conductance
    2822       37968 :             ibc(2) = 0; // convective coefficient on indoor side will be recalculated (like in Winkelmann routines)
    2823             : 
    2824             :             // hcout=HextConvCoeff  ! Exterior convection coefficient is passed in from outer routine
    2825       37968 :             hout = HextConvCoeff; // Exterior convection coefficient is passed in from outer routine
    2826       37968 :             ibc(1) = 2;           // prescribed convective film coeff on outdoor side
    2827       37968 :             tilt = state.dataSurface->Surface(SurfNum).Tilt;
    2828       37968 :             height = state.dataSurface->Surface(SurfNum).Height;
    2829       37968 :             heightt = height; // for now put same window and glazing pocket hights
    2830       37968 :             width = state.dataSurface->Surface(SurfNum).Width;
    2831             : 
    2832             :             // indoor mean radiant temperature.
    2833             :             // IR incident on window from zone surfaces and high-temp radiant sources
    2834       37968 :             rmir = state.dataSurface->SurfWinIRfromParentZone(SurfNum) + state.dataHeatBalSurf->SurfQdotRadHVACInPerArea(SurfNum);
    2835       37968 :             trmin = root_4(rmir / DataGlobalConstants::StefanBoltzmann); // TODO check model equation.
    2836             : 
    2837             :             // outdoor wind speed
    2838       37968 :             if (!state.dataSurface->Surface(SurfNum).ExtWind) {
    2839           0 :                 wso = 0.0; // No wind exposure
    2840             :                            // ELSE IF (Surface(SurfNum)%Class == SurfaceClass::Window .AND. SurfaceWindow(SurfNum)%ShadingFlag ==
    2841             :                            // WinShadingType::ExtShade) THEN
    2842             :                 //  wso =  0.0  ! Assume zero wind speed at outside glass surface of window with exterior shade
    2843             :             } else {
    2844       37968 :                 wso = state.dataSurface->SurfOutWindSpeed(SurfNum);
    2845             :             }
    2846             : 
    2847             :             // indoor wind speed
    2848       37968 :             wsi = 0.0; // assumuption (TODO, what to use for inside air velocity?)
    2849             : 
    2850       37968 :             fclr = 1.0 - state.dataEnvrn->CloudFraction;
    2851             :         }
    2852             : 
    2853       37990 :         TotLay = state.dataConstruction->Construct(ConstrNum).TotLayers;
    2854       37990 :         IGap = 0;
    2855             : 
    2856             :         //****************************************************************************************************
    2857             :         // Inside and outside gas coefficients
    2858             :         //****************************************************************************************************
    2859       37990 :         iprop(1, 1) = 1;  // air on outdoor side
    2860       37990 :         frct(1, 1) = 1.0; // pure air on outdoor side
    2861       37990 :         nmix(1) = 1;      // pure air on outdoor side
    2862             : 
    2863       37990 :         iprop(1, nlayer + 1) = 1;  // air on indoor side
    2864       37990 :         frct(1, nlayer + 1) = 1.0; // pure air on indoor side
    2865       37990 :         nmix(nlayer + 1) = 1;      // pure air on indoor side
    2866             : 
    2867             :         // Simon: feed gas coefficients with air.  This is necessary for tarcog because it is used on indoor and outdoor sides
    2868       37990 :         GasType = static_cast<int>(GasCoeffs::Air);
    2869       37990 :         wght(iprop(1, 1)) = GasWght[GasType - 1];
    2870       37990 :         gama(iprop(1, 1)) = GasSpecificHeatRatio[GasType - 1];
    2871      151960 :         for (ICoeff = 1; ICoeff <= 3; ++ICoeff) {
    2872      113970 :             gcon(ICoeff, iprop(1, 1)) = GasCoeffsCon[ICoeff - 1][GasType - 1];
    2873      113970 :             gvis(ICoeff, iprop(1, 1)) = GasCoeffsVis[ICoeff - 1][GasType - 1];
    2874      113970 :             gcp(ICoeff, iprop(1, 1)) = GasCoeffsCp[ICoeff - 1][GasType - 1];
    2875             :         }
    2876             : 
    2877             :         // Fill window layer properties needed for window layer heat balance calculation
    2878       37990 :         IGlass = 0;
    2879       37990 :         IGap = 0;
    2880      213144 :         for (Lay = 1; Lay <= TotLay; ++Lay) {
    2881      175154 :             LayPtr = state.dataConstruction->Construct(ConstrNum).LayerPoint(Lay);
    2882             : 
    2883      273654 :             if ((state.dataMaterial->Material(LayPtr).Group == DataHeatBalance::MaterialGroup::WindowGlass) ||
    2884       98500 :                 (state.dataMaterial->Material(LayPtr).Group == DataHeatBalance::MaterialGroup::WindowSimpleGlazing)) {
    2885       76654 :                 ++IGlass;
    2886       76654 :                 LayerType(IGlass) = TARCOGParams::TARCOGLayerType::SPECULAR; // this marks specular layer type
    2887       76654 :                 thick(IGlass) = state.dataMaterial->Material(LayPtr).Thickness;
    2888       76654 :                 scon(IGlass) = state.dataMaterial->Material(LayPtr).Conductivity;
    2889       76654 :                 emis(2 * IGlass - 1) = state.dataMaterial->Material(LayPtr).AbsorpThermalFront;
    2890       76654 :                 emis(2 * IGlass) = state.dataMaterial->Material(LayPtr).AbsorpThermalBack;
    2891       76654 :                 tir(2 * IGlass - 1) = state.dataMaterial->Material(LayPtr).TransThermal;
    2892       76654 :                 tir(2 * IGlass) = state.dataMaterial->Material(LayPtr).TransThermal;
    2893       76654 :                 YoungsMod(IGlass) = state.dataMaterial->Material(LayPtr).YoungModulus;
    2894       76654 :                 PoissonsRat(IGlass) = state.dataMaterial->Material(LayPtr).PoissonsRatio;
    2895       98500 :             } else if (state.dataMaterial->Material(LayPtr).Group == DataHeatBalance::MaterialGroup::ComplexWindowShade) {
    2896       29918 :                 ++IGlass;
    2897       29918 :                 TempInt = state.dataMaterial->Material(LayPtr).ComplexShadePtr;
    2898       29918 :                 LayerType(IGlass) = state.dataHeatBal->ComplexShade(TempInt).LayerType;
    2899             : 
    2900       29918 :                 thick(IGlass) = state.dataHeatBal->ComplexShade(TempInt).Thickness;
    2901       29918 :                 scon(IGlass) = state.dataHeatBal->ComplexShade(TempInt).Conductivity;
    2902       29918 :                 emis(2 * IGlass - 1) = state.dataHeatBal->ComplexShade(TempInt).FrontEmissivity;
    2903       29918 :                 emis(2 * IGlass) = state.dataHeatBal->ComplexShade(TempInt).BackEmissivity;
    2904       29918 :                 tir(2 * IGlass - 1) = state.dataHeatBal->ComplexShade(TempInt).IRTransmittance;
    2905       29918 :                 tir(2 * IGlass) = state.dataHeatBal->ComplexShade(TempInt).IRTransmittance;
    2906             : 
    2907             :                 // This needs to be converted into correct areas. That can be done only after loading complete window data
    2908       29918 :                 Atop(IGlass) = state.dataHeatBal->ComplexShade(TempInt).TopOpeningMultiplier;
    2909       29918 :                 Abot(IGlass) = state.dataHeatBal->ComplexShade(TempInt).BottomOpeningMultiplier;
    2910       29918 :                 Al(IGlass) = state.dataHeatBal->ComplexShade(TempInt).LeftOpeningMultiplier;
    2911       29918 :                 Ar(IGlass) = state.dataHeatBal->ComplexShade(TempInt).RightOpeningMultiplier;
    2912       29918 :                 Ah(IGlass) = state.dataHeatBal->ComplexShade(TempInt).FrontOpeningMultiplier;
    2913             : 
    2914       29918 :                 SlatThick(IGlass) = state.dataHeatBal->ComplexShade(TempInt).SlatThickness;
    2915       29918 :                 SlatWidth(IGlass) = state.dataHeatBal->ComplexShade(TempInt).SlatWidth;
    2916       29918 :                 SlatAngle(IGlass) = state.dataHeatBal->ComplexShade(TempInt).SlatAngle;
    2917       29918 :                 SlatCond(IGlass) = state.dataHeatBal->ComplexShade(TempInt).SlatConductivity;
    2918       29918 :                 SlatSpacing(IGlass) = state.dataHeatBal->ComplexShade(TempInt).SlatSpacing;
    2919       29918 :                 SlatCurve(IGlass) = state.dataHeatBal->ComplexShade(TempInt).SlatCurve;
    2920       68582 :             } else if (state.dataMaterial->Material(LayPtr).Group == DataHeatBalance::MaterialGroup::ComplexWindowGap) {
    2921       68582 :                 ++IGap;
    2922       68582 :                 gap(IGap) = state.dataMaterial->Material(LayPtr).Thickness;
    2923       68582 :                 presure(IGap) = state.dataMaterial->Material(LayPtr).Pressure;
    2924             : 
    2925       68582 :                 DeflectionPtr = state.dataMaterial->Material(LayPtr).DeflectionStatePtr;
    2926       68582 :                 if (DeflectionPtr != 0) {
    2927        6054 :                     GapDefMax(IGap) = state.dataHeatBal->DeflectionState(DeflectionPtr).DeflectedThickness;
    2928             :                 } else {
    2929       62528 :                     GapDefMax(IGap) = gap(IGap);
    2930             :                 }
    2931             : 
    2932       68582 :                 PillarPtr = state.dataMaterial->Material(LayPtr).SupportPillarPtr;
    2933             : 
    2934       68582 :                 if (PillarPtr != 0) {
    2935        2018 :                     SupportPlr(IGap) = 1;
    2936        2018 :                     PillarSpacing(IGap) = state.dataHeatBal->SupportPillar(PillarPtr).Spacing;
    2937        2018 :                     PillarRadius(IGap) = state.dataHeatBal->SupportPillar(PillarPtr).Radius;
    2938             :                 }
    2939             : 
    2940       68582 :                 GasPointer = state.dataMaterial->Material(LayPtr).GasPointer;
    2941             : 
    2942       68582 :                 nmix(IGap + 1) = state.dataMaterial->Material(GasPointer).NumberOfGasesInMixture;
    2943      149272 :                 for (IMix = 1; IMix <= nmix(IGap + 1); ++IMix) {
    2944       80690 :                     frct(IMix, IGap + 1) = state.dataMaterial->Material(GasPointer).GasFract(IMix);
    2945             : 
    2946             :                     // Now has to build-up gas coefficients arrays. All used gasses should be stored into these arrays and
    2947             :                     // to be correctly referenced by gap arrays
    2948             : 
    2949             :                     // First check if gas coefficients are already part of array.  Duplicates are not necessary
    2950       80690 :                     bool feedData(false);
    2951       80690 :                     CheckGasCoefs(state.dataMaterial->Material(GasPointer).GasWght(IMix), iprop(IMix, IGap + 1), wght, feedData);
    2952       80690 :                     if (feedData) {
    2953           2 :                         wght(iprop(IMix, IGap + 1)) = state.dataMaterial->Material(GasPointer).GasWght(IMix);
    2954           2 :                         gama(iprop(IMix, IGap + 1)) = state.dataMaterial->Material(GasPointer).GasSpecHeatRatio(IMix);
    2955           8 :                         for (i = 1; i <= 3; ++i) {
    2956           6 :                             gcon(i, iprop(IMix, IGap + 1)) = state.dataMaterial->Material(GasPointer).GasCon(i, IMix);
    2957           6 :                             gvis(i, iprop(IMix, IGap + 1)) = state.dataMaterial->Material(GasPointer).GasVis(i, IMix);
    2958           6 :                             gcp(i, iprop(IMix, IGap + 1)) = state.dataMaterial->Material(GasPointer).GasCp(i, IMix);
    2959             :                         }
    2960             :                     } // IF feedData THEN
    2961             :                 }
    2962             :             } else {
    2963           0 :                 ShowContinueError(state, "Illegal layer type in Construction:ComplexFenestrationState.");
    2964           0 :                 ShowContinueError(state, "Allowed object are:");
    2965           0 :                 ShowContinueError(state, "   - WindowMaterial:Glazing");
    2966           0 :                 ShowContinueError(state, "   - WindowMaterial:ComplexShade");
    2967           0 :                 ShowContinueError(state, "   - WindowMaterial:Gap");
    2968           0 :                 ShowFatalError(state, "halting because of error in layer definition for Construction:ComplexFenestrationState");
    2969             :             }
    2970             : 
    2971             :         } // End of loop over glass, gap and blind/shade layers in a window construction
    2972             : 
    2973       37990 :         if (CalcCondition == DataBSDFWindow::Condition::Invalid) {
    2974             :             // now calculate correct areas for multipliers
    2975      144480 :             for (Lay = 1; Lay <= nlayer; ++Lay) {
    2976      106512 :                 if (LayerType(Lay) != TARCOGParams::TARCOGLayerType::SPECULAR) { // Layer is shading
    2977             :                     // before changing multipliers, need to determine which one is dominant gap width
    2978       29904 :                     if (Lay == 1) { // Exterior shading device
    2979       10080 :                         dominantGapWidth = gap(Lay);
    2980       19824 :                     } else if (Lay == nlayer) { // Interior shading device
    2981       15792 :                         dominantGapWidth = gap(Lay - 1);
    2982             :                     } else { // In-between shading device
    2983        4032 :                         dominantGapWidth = min(gap(Lay - 1), gap(Lay));
    2984             :                     }
    2985       29904 :                     Atop(Lay) *= dominantGapWidth * width;
    2986       29904 :                     Abot(Lay) *= dominantGapWidth * width;
    2987       29904 :                     Al(Lay) *= dominantGapWidth * height;
    2988       29904 :                     Ar(Lay) *= dominantGapWidth * height;
    2989       29904 :                     Ah(Lay) *= width * height;
    2990             :                 }
    2991             :             }
    2992             :         }
    2993             : 
    2994             :         // ThermalMod = 0
    2995       37990 :         CalcSHGC = 0;
    2996             : 
    2997       37990 :         Window_ID = ConstrNum;
    2998             : 
    2999             :         // vector of absorbed solar energy fractions for each layer.
    3000       37990 :         asol = 0.0;
    3001             :         // direct solar radiation
    3002       37990 :         if (CalcCondition == DataBSDFWindow::Condition::Invalid) {
    3003       37968 :             ShadeFlag = state.dataSurface->SurfWinShadingFlag(SurfNum);
    3004       75936 :             dir = state.dataHeatBal->SurfQRadSWOutIncident(SurfNum) +
    3005       37968 :                   state.dataHeatBal->EnclSolQSWRad(state.dataSurface->Surface(SurfNum).SolarEnclIndex); // TODO, check , !
    3006             :             //                  currently using Exterior beam plus diffuse solar incident on surface
    3007             :             //                  plus zone short wave.  CHECK
    3008             :             // if (dir.ne.0.0d0) then
    3009      144480 :             for (IGlass = 1; IGlass <= nlayer; ++IGlass) {
    3010             :                 // IF (dir > 0.0D0 ) THEN
    3011      106512 :                 asol(IGlass) = state.dataHeatBal->SurfWinQRadSWwinAbs(SurfNum, IGlass);
    3012             :                 // ELSE
    3013             :                 //  asol(IGLASS) = 0.0D0
    3014             :                 // ENDIF
    3015             :             }
    3016             :             // end if
    3017             : 
    3018             :             // Add contribution of IR from zone internal gains (lights, equipment and people). This is absorbed in zone-side layer and it
    3019             :             // is assumed that nothing is transmitted through
    3020       37968 :             asol(nlayer) += state.dataHeatBal->SurfQdotRadIntGainsInPerArea(SurfNum);
    3021             : 
    3022       37968 :             presure = state.dataEnvrn->OutBaroPress;
    3023             : 
    3024             :             // Instead of doing temperature guess get solution from previous iteration.  That should be much better than guess
    3025      250992 :             for (k = 1; k <= 2 * nlayer; ++k) {
    3026      213024 :                 theta(k) = state.dataSurface->SurfaceWindow(SurfNum).ThetaFace(k);
    3027             :             }
    3028             :         }
    3029             : 
    3030             :         // Standard conditions run (winter and summer)
    3031       37990 :         if (CalcCondition == DataBSDFWindow::Condition::Winter) {
    3032          11 :             tind = 294.15;
    3033          11 :             tout = 255.15;
    3034          11 :             hcout = 26.0;
    3035          11 :             wso = 5.5;
    3036          11 :             dir = 0.0;
    3037       37979 :         } else if (CalcCondition == DataBSDFWindow::Condition::Summer) {
    3038          11 :             tind = 297.15;
    3039          11 :             tout = 305.15;
    3040          11 :             hcout = 15.0;
    3041          11 :             wso = 2.75;
    3042          11 :             dir = 783.0;
    3043          11 :             CalcSHGC = 1;
    3044             :         }
    3045             : 
    3046             :         // Common condition data
    3047       37990 :         if (CalcCondition != DataBSDFWindow::Condition::Invalid) {
    3048          22 :             trmin = tind;
    3049          22 :             outir = 0.0;
    3050          22 :             tsky = tout;
    3051          22 :             wsi = 0.0;
    3052          22 :             fclr = 1.0;
    3053          22 :             ibc(1) = 0;
    3054          22 :             ibc(2) = 0;
    3055          22 :             presure = 101325.0;
    3056          22 :             iwd = 0; // Windward wind direction
    3057          22 :             isky = 0;
    3058          22 :             esky = 1.0;
    3059          22 :             height = 1.0;
    3060          22 :             heightt = 1.0;
    3061          22 :             width = 1.0;
    3062          22 :             tilt = 90.0;
    3063             :             // Just to make initial quess different from absolute zero
    3064          22 :             theta = 273.15;
    3065             :         }
    3066             : 
    3067       37990 :         if (SurfNum != 0)
    3068       37968 :             edgeGlCorrFac = state.dataSurface->SurfWinEdgeGlCorrFac(SurfNum);
    3069             :         else
    3070          22 :             edgeGlCorrFac = 1;
    3071             : 
    3072             :         //  call TARCOG
    3073       37990 :         int constexpr Debug_mode = 0;
    3074       37990 :         TARCOG90(state,
    3075             :                  nlayer,
    3076             :                  iwd,
    3077             :                  tout,
    3078             :                  tind,
    3079             :                  trmin,
    3080             :                  wso,
    3081             :                  wsi,
    3082             :                  dir,
    3083             :                  outir,
    3084             :                  isky,
    3085             :                  tsky,
    3086             :                  esky,
    3087             :                  fclr,
    3088             :                  VacuumPressure,
    3089             :                  VacuumMaxGapThickness,
    3090             :                  CalcDeflection,
    3091             :                  Pa,
    3092             :                  Pini,
    3093             :                  Tini,
    3094             :                  gap,
    3095             :                  GapDefMax,
    3096             :                  thick,
    3097             :                  scon,
    3098             :                  YoungsMod,
    3099             :                  PoissonsRat,
    3100             :                  tir,
    3101             :                  emis,
    3102             :                  totsol,
    3103             :                  tilt,
    3104             :                  asol,
    3105             :                  height,
    3106             :                  heightt,
    3107             :                  width,
    3108             :                  presure,
    3109             :                  iprop,
    3110             :                  frct,
    3111             :                  gcon,
    3112             :                  gvis,
    3113             :                  gcp,
    3114             :                  wght,
    3115             :                  gama,
    3116             :                  nmix,
    3117             :                  SupportPlr,
    3118             :                  PillarSpacing,
    3119             :                  PillarRadius,
    3120             :                  theta,
    3121             :                  LayerDef,
    3122             :                  q,
    3123             :                  qv,
    3124             :                  ufactor,
    3125             :                  sc,
    3126             :                  hflux,
    3127             :                  hcin,
    3128             :                  hcout,
    3129             :                  hrin,
    3130             :                  hrout,
    3131             :                  hin,
    3132             :                  hout,
    3133             :                  hcgap,
    3134             :                  hrgap,
    3135             :                  shgc,
    3136             :                  nperr,
    3137             :                  tarcogErrorMessage,
    3138             :                  shgct,
    3139             :                  tamb,
    3140             :                  troom,
    3141             :                  ibc,
    3142             :                  Atop,
    3143             :                  Abot,
    3144             :                  Al,
    3145             :                  Ar,
    3146             :                  Ah,
    3147             :                  SlatThick,
    3148             :                  SlatWidth,
    3149             :                  SlatAngle,
    3150             :                  SlatCond,
    3151             :                  SlatSpacing,
    3152             :                  SlatCurve,
    3153             :                  vvent,
    3154             :                  tvent,
    3155             :                  LayerType,
    3156             :                  nslice,
    3157             :                  LaminateA,
    3158             :                  LaminateB,
    3159             :                  sumsol,
    3160             :                  hg,
    3161             :                  hr,
    3162             :                  hs,
    3163             :                  he,
    3164             :                  hi,
    3165             :                  Ra,
    3166             :                  Nu,
    3167             :                  standard,
    3168             :                  ThermalMod,
    3169             :                  Debug_mode,
    3170             :                  Debug_dir,
    3171             :                  Debug_file,
    3172             :                  Window_ID,
    3173             :                  IGU_ID,
    3174             :                  ShadeEmisRatioOut,
    3175             :                  ShadeEmisRatioIn,
    3176             :                  ShadeHcRatioOut,
    3177             :                  ShadeHcRatioIn,
    3178             :                  HcUnshadedOut,
    3179             :                  HcUnshadedIn,
    3180             :                  Keff,
    3181             :                  ShadeGapKeffConv,
    3182             :                  SDScalar,
    3183             :                  CalcSHGC,
    3184             :                  NumOfIterations,
    3185             :                  edgeGlCorrFac);
    3186             : 
    3187             :         // process results from TARCOG
    3188       37990 :         if ((nperr > 0) && (nperr < 1000)) { // process error signal from tarcog
    3189             : 
    3190           0 :             ShowSevereError(state, "Window tarcog returned an error");
    3191           0 :             tarcogErrorMessage = "message = \"" + tarcogErrorMessage + "\"";
    3192           0 :             ShowContinueErrorTimeStamp(state, tarcogErrorMessage);
    3193           0 :             if (CalcCondition == DataBSDFWindow::Condition::Invalid) {
    3194           0 :                 ShowContinueError(state, "surface name = " + state.dataSurface->Surface(SurfNum).Name);
    3195             :             }
    3196           0 :             ShowContinueError(state, "construction name = " + state.dataConstruction->Construct(ConstrNum).Name);
    3197           0 :             ShowFatalError(state, "halting because of error in tarcog");
    3198       37990 :         } else if (CalcCondition == DataBSDFWindow::Condition::Winter) {
    3199          11 :             state.dataHeatBal->NominalU(ConstrNum) = ufactor;
    3200       37979 :         } else if (CalcCondition == DataBSDFWindow::Condition::Summer) {
    3201             :             // tempInt = SurfaceWindow(SurfNum)%ComplexFen%CurrentState
    3202             :             // tempReal = SurfaceWindow(SurfNum)%ComplexFen%State(tempInt)%WinDiffTrans
    3203             : 
    3204             :             // Sum1 = 0.0d0
    3205             :             // Sum2 = 0.0d0
    3206             :             // do  j = 1 , ComplexWind(SurfNum)%Geom%Inc%NBasis     !Incident ray loop
    3207             :             //  Sum2 = Sum2 + ComplexWind(SurfNum)%Geom%Inc%Lamda (j)
    3208             :             //  do  m = 1 , ComplexWind(SurfNum)%Geom%Trn%NBasis     !Outgoing ray loop
    3209             :             //    Sum1 =Sum1 + ComplexWind(SurfNum)%Geom%Inc%Lamda(j) * ComplexWind(SurfNum)%Geom%Trn%Lamda(m) * &
    3210             :             //      & Construct(ConstrNum)%BSDFInput%SolFrtTrans ( j , m )
    3211             :             //  end do      !Outgoing ray loop
    3212             :             // end do      !Incident ray loop
    3213             :             // if (Sum2 > 0.0d0) THEN
    3214             :             //  tempReal = Sum1/Sum2
    3215             :             // else
    3216             :             //  tempReal = 0.0d0
    3217             :             //  CALL ShowWarningError(state, 'BSDF--Inc basis has zero projected solid angle')
    3218             :             // endif
    3219             : 
    3220          11 :             state.dataConstruction->Construct(ConstrNum).SummerSHGC = shgc;
    3221             : 
    3222             :             // Construct(SurfNum)%VisTransNorm = SurfaceWindow(SurfNum)%ComplexFen%State(tempInt)%WinDiffVisTrans
    3223       37968 :         } else if (CalcCondition == DataBSDFWindow::Condition::Invalid) { // expect converged results...
    3224             :             // Window heat balance solution has converged.
    3225             : 
    3226       37968 :             state.dataSurface->SurfWinWindowCalcIterationsRep(SurfNum) = NumOfIterations;
    3227       37968 :             state.dataHeatBalSurf->SurfHConvInt(SurfNum) = hcin;
    3228             : 
    3229             :             // For interior shade, add convective gain from glass/shade gap air flow to zone convective gain;
    3230             :             // For all cases, get total window heat gain for reporting. See CalcWinFrameAndDividerTemps for
    3231             :             // contribution of frame and divider.
    3232             : 
    3233       37968 :             SurfInsideTemp = theta(2 * nlayer) - DataGlobalConstants::KelvinConv;
    3234       37968 :             state.dataSurface->SurfWinEffInsSurfTemp(SurfNum) = SurfInsideTemp;
    3235       37968 :             SurfOutsideTemp = theta(1) - DataGlobalConstants::KelvinConv;
    3236       37968 :             SurfOutsideEmiss = emis(1);
    3237             : 
    3238       37968 :             IncidentSolar = state.dataSurface->Surface(SurfNum).Area * state.dataHeatBal->SurfQRadSWOutIncident(SurfNum);
    3239       37968 :             if (ANY_INTERIOR_SHADE_BLIND(ShadeFlag)) {
    3240             :                 // Interior shade or blind
    3241       15792 :                 ConvHeatFlowNatural = -qv(nlayer) * height * width;
    3242             : 
    3243       15792 :                 state.dataSurface->SurfWinConvHeatFlowNatural(SurfNum) = ConvHeatFlowNatural;
    3244       15792 :                 state.dataSurface->SurfWinGapConvHtFlowRep(SurfNum) = ConvHeatFlowNatural;
    3245       15792 :                 state.dataSurface->SurfWinGapConvHtFlowRepEnergy(SurfNum) =
    3246       15792 :                     state.dataSurface->SurfWinGapConvHtFlowRep(SurfNum) * state.dataGlobal->TimeStepZoneSec;
    3247             :                 // Window heat gain from glazing and shade/blind to zone. Consists of transmitted solar, convection
    3248             :                 //   from air exiting gap, convection from zone-side of shade/blind, net IR to zone from shade and net IR to
    3249             :                 //   zone from the glass adjacent to the shade/blind (zero if shade/blind IR transmittance is zero).
    3250             :                 // Following assumes glazed area = window area (i.e., dividers ignored) in calculating
    3251             :                 //   IR to zone from glass when interior shade/blind is present.
    3252       15792 :                 ShadeArea = state.dataSurface->Surface(SurfNum).Area + state.dataSurface->SurfWinDividerArea(SurfNum);
    3253       15792 :                 sconsh = scon(ngllayer + 1) / thick(ngllayer + 1);
    3254       15792 :                 nglfacep = nglface + 2;
    3255       15792 :                 CondHeatGainShade = ShadeArea * sconsh * (theta(nglfacep - 1) - theta(nglfacep));
    3256       15792 :                 EpsShIR1 = emis(nglface + 1);
    3257       15792 :                 EpsShIR2 = emis(nglface + 2);
    3258       15792 :                 TauShIR = tir(nglface + 1);
    3259       15792 :                 RhoShIR1 = max(0.0, 1.0 - TauShIR - EpsShIR1);
    3260       15792 :                 RhoShIR2 = max(0.0, 1.0 - TauShIR - EpsShIR2);
    3261       15792 :                 RhoGlIR2 = 1.0 - emis(2 * ngllayer);
    3262       15792 :                 ShGlReflFacIR = 1.0 - RhoGlIR2 * RhoShIR1;
    3263       15792 :                 NetIRHeatGainShade =
    3264       15792 :                     ShadeArea * EpsShIR2 * (state.dataWindowComplexManager->sigma * pow_4(theta(nglfacep)) - rmir) +
    3265       15792 :                     EpsShIR1 * (state.dataWindowComplexManager->sigma * pow_4(theta(nglfacep - 1)) - rmir) * RhoGlIR2 * TauShIR / ShGlReflFacIR;
    3266       31584 :                 NetIRHeatGainGlass = ShadeArea * (emis(2 * ngllayer) * TauShIR / ShGlReflFacIR) *
    3267       15792 :                                      (state.dataWindowComplexManager->sigma * pow_4(theta(2 * ngllayer)) - rmir);
    3268       15792 :                 ConvHeatGainFrZoneSideOfShade = ShadeArea * hcin * (theta(nglfacep) - tind);
    3269       31584 :                 state.dataSurface->SurfWinHeatGain(SurfNum) = state.dataSurface->SurfWinTransSolar(SurfNum) + ConvHeatFlowNatural +
    3270       15792 :                                                               ConvHeatGainFrZoneSideOfShade + NetIRHeatGainGlass + NetIRHeatGainShade;
    3271             :                 // store components for reporting
    3272       15792 :                 state.dataSurface->SurfWinGainConvShadeToZoneRep(SurfNum) = ConvHeatGainFrZoneSideOfShade;
    3273       15792 :                 state.dataSurface->SurfWinGainIRGlazToZoneRep(SurfNum) = NetIRHeatGainGlass;
    3274       15792 :                 state.dataSurface->SurfWinGainIRShadeToZoneRep(SurfNum) = NetIRHeatGainShade;
    3275             :             } else {
    3276             :                 // Interior shade or blind not present; innermost layer is glass
    3277       22176 :                 CondHeatGainGlass =
    3278       22176 :                     state.dataSurface->Surface(SurfNum).Area * scon(nlayer) / thick(nlayer) * (theta(2 * nlayer - 1) - theta(2 * nlayer));
    3279       44352 :                 NetIRHeatGainGlass = state.dataSurface->Surface(SurfNum).Area * emis(2 * nlayer) *
    3280       22176 :                                      (state.dataWindowComplexManager->sigma * pow_4(theta(2 * nlayer)) - rmir);
    3281       22176 :                 ConvHeatGainFrZoneSideOfGlass = state.dataSurface->Surface(SurfNum).Area * hcin * (theta(2 * nlayer) - tind);
    3282       22176 :                 state.dataSurface->SurfWinHeatGain(SurfNum) =
    3283       22176 :                     state.dataSurface->SurfWinTransSolar(SurfNum) + ConvHeatGainFrZoneSideOfGlass + NetIRHeatGainGlass;
    3284             :                 // store components for reporting
    3285       22176 :                 state.dataSurface->SurfWinGainConvGlazToZoneRep(SurfNum) = ConvHeatGainFrZoneSideOfGlass;
    3286       22176 :                 state.dataSurface->SurfWinGainIRGlazToZoneRep(SurfNum) = NetIRHeatGainGlass;
    3287             :                 // need to report convective heat flow from the gap in case of exterior shade
    3288       22176 :                 if (ShadeFlag == WinShadingType::ExtShade) {
    3289       10080 :                     ConvHeatFlowNatural = -qv(2) * height * width; // qv(1) is exterior environment
    3290             : 
    3291       10080 :                     state.dataSurface->SurfWinGapConvHtFlowRep(SurfNum) = ConvHeatFlowNatural;
    3292       10080 :                     state.dataSurface->SurfWinGapConvHtFlowRepEnergy(SurfNum) =
    3293       10080 :                         state.dataSurface->SurfWinGapConvHtFlowRep(SurfNum) * state.dataGlobal->TimeStepZoneSec;
    3294             :                 }
    3295             :             }
    3296             : 
    3297             :             // Add convective heat gain from airflow window
    3298             :             // Note: effect of fan heat on gap outlet temperature is neglected since fan power (based
    3299             :             // on pressure drop through the gap) is extremely small
    3300             : 
    3301             :             // WinGapConvHtFlowRep(SurfNum) = 0.0d0
    3302             :             // WinGapConvHtFlowRepEnergy(SurfNum) = 0.0d0
    3303       37968 :             TotAirflowGap = state.dataSurface->SurfWinAirflowThisTS(SurfNum) * state.dataSurface->Surface(SurfNum).Width;
    3304       37968 :             TAirflowGapOutlet = DataGlobalConstants::KelvinConv; // TODO Need to calculate this
    3305       37968 :             TAirflowGapOutletC = TAirflowGapOutlet - DataGlobalConstants::KelvinConv;
    3306       37968 :             state.dataSurface->SurfWinTAirflowGapOutlet(SurfNum) = TAirflowGapOutletC;
    3307       37968 :             if (state.dataSurface->SurfWinAirflowThisTS(SurfNum) > 0.0) {
    3308           0 :                 ConvHeatFlowForced = sum(qv); // TODO.  figure forced ventilation heat flow in Watts
    3309             : 
    3310           0 :                 state.dataSurface->SurfWinGapConvHtFlowRep(SurfNum) = ConvHeatFlowForced;
    3311           0 :                 state.dataSurface->SurfWinGapConvHtFlowRepEnergy(SurfNum) =
    3312           0 :                     state.dataSurface->SurfWinGapConvHtFlowRep(SurfNum) * state.dataGlobal->TimeStepZoneSec;
    3313             :                 // Add heat from gap airflow to zone air if destination is inside air; save the heat gain to return
    3314             :                 // air in case it needs to be sent to the zone (due to no return air determined in HVAC simulation)
    3315           0 :                 if (state.dataSurface->SurfWinAirflowDestination(SurfNum) == WindowAirFlowDestination::Indoor ||
    3316           0 :                     state.dataSurface->SurfWinAirflowDestination(SurfNum) == WindowAirFlowDestination::Return) {
    3317           0 :                     auto &thisZoneHB = state.dataZoneTempPredictorCorrector->zoneHeatBalance(ZoneNum);
    3318           0 :                     if (state.dataSurface->SurfWinAirflowSource(SurfNum) == WindowAirFlowSource::Indoor) {
    3319           0 :                         InletAirHumRat = thisZoneHB.ZoneAirHumRat;
    3320             :                     } else { // AirflowSource = outside air
    3321           0 :                         InletAirHumRat = state.dataEnvrn->OutHumRat;
    3322             :                     }
    3323           0 :                     ZoneTemp = thisZoneHB.MAT; // this should be Tin (account for different reference temps)
    3324           0 :                     CpAirOutlet = PsyCpAirFnW(InletAirHumRat);
    3325           0 :                     CpAirZone = PsyCpAirFnW(thisZoneHB.ZoneAirHumRat);
    3326           0 :                     ConvHeatGainToZoneAir = TotAirflowGap * (CpAirOutlet * (TAirflowGapOutletC)-CpAirZone * ZoneTemp);
    3327           0 :                     if (state.dataSurface->SurfWinAirflowDestination(SurfNum) == WindowAirFlowDestination::Indoor) {
    3328           0 :                         state.dataSurface->SurfWinConvHeatGainToZoneAir(SurfNum) = ConvHeatGainToZoneAir;
    3329           0 :                         state.dataSurface->SurfWinHeatGain(SurfNum) += ConvHeatGainToZoneAir;
    3330             :                     } else {
    3331           0 :                         state.dataSurface->SurfWinRetHeatGainToZoneAir(SurfNum) = ConvHeatGainToZoneAir;
    3332             :                     }
    3333             :                 }
    3334             :                 // For AirflowDestination = ReturnAir in a controlled (i.e., conditioned) zone with return air, see CalcZoneLeavingConditions
    3335             :                 // for calculation of modification of return-air temperature due to airflow from window gaps into return air.
    3336             :             }
    3337             : 
    3338             :             // Correct WinHeatGain for interior diffuse shortwave (solar and shortwave from lights) transmitted
    3339             :             // back out window
    3340       37968 :             ConstrNum = state.dataSurface->Surface(SurfNum).Construction;
    3341             :             // ConstrNumSh = Surface(SurfNum)%ShadedConstruction
    3342             :             // IF(SurfaceWindow(SurfNum)%StormWinFlag==1) THEN
    3343             :             //  ConstrNum = Surface(SurfNum)%StormWinConstruction
    3344             :             //  ConstrNumSh = Surface(SurfNum)%StormWinShadedConstruction
    3345             :             // END IF
    3346             :             // IF(ShadeFlag <= 0) THEN
    3347             :             // TransDiff = Construct(ConstrNum).TransDiff;
    3348       37968 :             int IState = state.dataSurface->SurfaceWindow(SurfNum).ComplexFen.NumStates;
    3349       37968 :             Real64 TransDiff = state.dataSurface->SurfaceWindow(SurfNum).ComplexFen.State(IState).WinDiffTrans;
    3350             :             // ELSE IF(ShadeFlag==WinShadingType::IntShade .OR. ShadeFlag==WinShadingType::ExtShade) THEN
    3351             :             //  TransDiff = Construct(ConstrNum)%TransDiff
    3352             :             // ELSE IF(ShadeFlag==WinShadingType::IntBlind .OR. ShadeFlag==WinShadingType::ExtBlind .OR.ShadeFlag==WinShadingType::BGBlind) THEN
    3353             :             //  TransDiff = InterpSlatAng(SurfaceWindow(SurfNum)%SlatAngThisTS,SurfaceWindow(SurfNum)%MovableSlats, &
    3354             :             //                             Construct(ConstrNumSh)%BlTransDiff)
    3355             :             // ELSE IF(ShadeFlag == SwitchableGlazing) THEN
    3356             :             //  TransDiff = InterpSW(SurfaceWindow(SurfNum)%SwitchingFactor,Construct(ConstrNum)%TransDiff, &
    3357             :             //                             Construct(ConstrNumSh)%TransDiff)
    3358             :             // END IF
    3359       37968 :             state.dataSurface->SurfWinLossSWZoneToOutWinRep(SurfNum) =
    3360       37968 :                 state.dataHeatBal->EnclSolQSWRad(state.dataSurface->Surface(SurfNum).SolarEnclIndex) * state.dataSurface->Surface(SurfNum).Area *
    3361             :                 TransDiff;
    3362       37968 :             state.dataSurface->SurfWinHeatGain(SurfNum) -= state.dataSurface->SurfWinLossSWZoneToOutWinRep(SurfNum);
    3363             : 
    3364       37968 :             if (ShadeFlag == WinShadingType::IntShade || ShadeFlag == WinShadingType::ExtShade) {
    3365       25872 :                 state.dataSurface->SurfWinShadingAbsorbedSolar(SurfNum) =
    3366       51744 :                     (state.dataSurface->SurfWinExtBeamAbsByShade(SurfNum) + state.dataSurface->SurfWinExtDiffAbsByShade(SurfNum)) *
    3367       25872 :                     (state.dataSurface->Surface(SurfNum).Area + state.dataSurface->SurfWinDividerArea(SurfNum));
    3368       25872 :                 state.dataSurface->SurfWinShadingAbsorbedSolarEnergy(SurfNum) =
    3369       25872 :                     state.dataSurface->SurfWinShadingAbsorbedSolar(SurfNum) * state.dataGlobal->TimeStepZoneSec;
    3370             :             }
    3371       37968 :             if (state.dataEnvrn->SunIsUp) {
    3372       19152 :                 state.dataSurface->SurfWinSysSolTransmittance(SurfNum) =
    3373       38304 :                     state.dataSurface->SurfWinTransSolar(SurfNum) /
    3374       38304 :                     (state.dataHeatBal->SurfQRadSWOutIncident(SurfNum) *
    3375       38304 :                          (state.dataSurface->Surface(SurfNum).Area + state.dataSurface->SurfWinDividerArea(SurfNum)) +
    3376             :                      0.0001);
    3377       19152 :                 state.dataSurface->SurfWinSysSolAbsorptance(SurfNum) =
    3378       38304 :                     (state.dataHeatBal->SurfWinQRadSWwinAbsTot(SurfNum) + state.dataSurface->SurfWinShadingAbsorbedSolar(SurfNum)) /
    3379       38304 :                     (state.dataHeatBal->SurfQRadSWOutIncident(SurfNum) *
    3380       38304 :                          (state.dataSurface->Surface(SurfNum).Area + state.dataSurface->SurfWinDividerArea(SurfNum)) +
    3381             :                      0.0001);
    3382       19152 :                 state.dataSurface->SurfWinSysSolReflectance(SurfNum) =
    3383       19152 :                     1.0 - state.dataSurface->SurfWinSysSolTransmittance(SurfNum) - state.dataSurface->SurfWinSysSolAbsorptance(SurfNum);
    3384             :             } else {
    3385       18816 :                 state.dataSurface->SurfWinSysSolTransmittance(SurfNum) = 0.0;
    3386       18816 :                 state.dataSurface->SurfWinSysSolAbsorptance(SurfNum) = 0.0;
    3387       18816 :                 state.dataSurface->SurfWinSysSolReflectance(SurfNum) = 0.0;
    3388             :             }
    3389             : 
    3390             :             // Save hcv for use in divider calc with interior or exterior shade (see CalcWinFrameAndDividerTemps)
    3391       37968 :             if (ShadeFlag == WinShadingType::IntShade) state.dataSurface->SurfWinConvCoeffWithShade(SurfNum) = 0.0;
    3392             : 
    3393       37968 :             if (ShadeFlag == WinShadingType::IntShade) {
    3394       15792 :                 SurfInsideTemp = theta(2 * ngllayer + 2) - DataGlobalConstants::KelvinConv;
    3395             : 
    3396             :                 // // Get properties of inside shading layer
    3397             : 
    3398       15792 :                 Real64 EffShBlEmiss = state.dataSurface->SurfaceWindow(SurfNum).EffShBlindEmiss[0];
    3399       15792 :                 Real64 EffGlEmiss = state.dataSurface->SurfaceWindow(SurfNum).EffGlassEmiss[0];
    3400       15792 :                 state.dataSurface->SurfWinEffInsSurfTemp(SurfNum) =
    3401       31584 :                     (EffShBlEmiss * SurfInsideTemp + EffGlEmiss * (theta(2 * ngllayer) - DataGlobalConstants::KelvinConv)) /
    3402       15792 :                     (EffShBlEmiss + EffGlEmiss);
    3403             : 
    3404             :             } else {
    3405       22176 :                 SurfOutsideTemp = theta(1) - DataGlobalConstants::KelvinConv;
    3406             :             }
    3407             : 
    3408      144480 :             for (k = 1; k <= nlayer; ++k) {
    3409      106512 :                 state.dataSurface->SurfaceWindow(SurfNum).ThetaFace(2 * k - 1) = theta(2 * k - 1);
    3410      106512 :                 state.dataSurface->SurfaceWindow(SurfNum).ThetaFace(2 * k) = theta(2 * k);
    3411             : 
    3412             :                 // temperatures for reporting
    3413      106512 :                 state.dataHeatBal->SurfWinFenLaySurfTempFront(SurfNum, k) = theta(2 * k - 1) - DataGlobalConstants::KelvinConv;
    3414      106512 :                 state.dataHeatBal->SurfWinFenLaySurfTempBack(SurfNum, k) = theta(2 * k) - DataGlobalConstants::KelvinConv;
    3415             :                 // thetas(k) = theta(k)
    3416             :             }
    3417             :         }
    3418       37990 :     }
    3419             : 
    3420             :     // This function check if gas with molecular weight has already been feed into coefficients and
    3421             :     // feed arrays
    3422             : 
    3423       80690 :     void CheckGasCoefs(Real64 const currentWeight, int &indexNumber, Array1D<Real64> &wght, bool &feedData)
    3424             :     {
    3425             : 
    3426             :         // Using/Aliasing
    3427             :         using TARCOGGassesParams::maxgas;
    3428             : 
    3429             :         // Argument array dimensioning
    3430       80690 :         EP_SIZE_CHECK(wght, maxgas);
    3431             : 
    3432       80690 :         feedData = false;
    3433       80690 :         bool coeffFound = false;
    3434       80690 :         int counter = 1;
    3435      266282 :         while ((counter <= maxgas) && (wght(counter) != 0) && (!coeffFound)) {
    3436       92796 :             if (std::abs(currentWeight - wght(counter)) < 1.0e-5) {
    3437       80688 :                 coeffFound = true;
    3438             :             } else {
    3439       12108 :                 ++counter;
    3440             :             }
    3441             :         } // DO WHILE((counter.LE.maxgas).AND.(wght(couner).NE.0).AND.(.NOT.coeffFound))
    3442             : 
    3443             :         // In case coefficient is not found data needs to be stored in gas coefficients arrays
    3444       80690 :         if ((!coeffFound) && (counter < maxgas)) {
    3445           2 :             feedData = true;
    3446             :         }
    3447             : 
    3448       80690 :         indexNumber = counter;
    3449       80690 :     }
    3450             : 
    3451        3350 :     int SearchAscTable(Real64 const y,            // Value to be found in the table
    3452             :                        int const n,               // Number of values in the table
    3453             :                        Array1S<Real64> const ytab // Table of values, monotonic, ascending order
    3454             :     )
    3455             :     {
    3456             : 
    3457             :         // FUNCTION INFORMATION:
    3458             :         //       AUTHOR         Joe Klems
    3459             :         //       DATE WRITTEN   Feb 2011
    3460             :         //       MODIFIED       na
    3461             :         //       RE-ENGINEERED  na
    3462             : 
    3463             :         // PURPOSE OF THIS FUNCTION:
    3464             :         // Given an ascending monotonic table with n entries, find  an index i
    3465             :         // such that ytab(i-1) < y <= ytab(i)
    3466             : 
    3467             :         // METHODOLOGY EMPLOYED:
    3468             :         // binary search
    3469             : 
    3470             :         // Return value
    3471             :         int SearchAscTable;
    3472             : 
    3473             :         int Ih;    // Intex for upper end of interval
    3474             :         int Il;    // Index for lower end of interval
    3475             :         int Im;    // Index for midpoint of interval
    3476             :         Real64 Yh; // Table value for upper end of interval
    3477             :         Real64 Yl; // Table value for lower end of interval
    3478             :         Real64 Ym; // Table value for midpoint of interval
    3479             : 
    3480        3350 :         Yh = ytab(n);
    3481        3350 :         Yl = ytab(1);
    3482        3350 :         Ih = n;
    3483        3350 :         Il = 1;
    3484        3350 :         if (y < Yl) {
    3485           0 :             SearchAscTable = 1;
    3486           0 :             return SearchAscTable;
    3487        3350 :         } else if (y > Yh) {
    3488           0 :             SearchAscTable = n;
    3489           0 :             return SearchAscTable;
    3490             :         }
    3491             :         while (true) {
    3492       25624 :             if (Ih - Il <= 1) break;
    3493       11137 :             Im = (Ih + Il) / 2;
    3494       11137 :             Ym = ytab(Im);
    3495       11137 :             if (y <= Ym) {
    3496        3709 :                 Yh = Ym;
    3497        3709 :                 Ih = Im;
    3498             :             } else {
    3499        7428 :                 Yl = Ym;
    3500        7428 :                 Il = Im;
    3501             :             }
    3502             :         }
    3503             : 
    3504        3350 :         SearchAscTable = Ih;
    3505             : 
    3506        3350 :         return SearchAscTable;
    3507             :     }
    3508             : 
    3509             : } // namespace WindowComplexManager
    3510             : 
    3511        2313 : } // namespace EnergyPlus

Generated by: LCOV version 1.13