LCOV - code coverage report
Current view: top level - EnergyPlus - WindowComplexManager.cc (source / functions) Hit Total Coverage
Test: lcov.output.filtered Lines: 1245 1659 75.0 %
Date: 2024-08-24 18:31:18 Functions: 23 25 92.0 %

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

Generated by: LCOV version 1.14