LCOV - code coverage report
Current view: top level - EnergyPlus - WindowComplexManager.cc (source / functions) Coverage Total Hit
Test: lcov.output.filtered Lines: 11.6 % 1655 192
Test Date: 2025-05-22 16:09:37 Functions: 40.0 % 25 10

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

Generated by: LCOV version 2.0-1