LCOV - code coverage report
Current view: top level - EnergyPlus - DaylightingManager.cc (source / functions) Hit Total Coverage
Test: lcov.output.filtered Lines: 3447 4731 72.9 %
Date: 2024-08-24 18:31:18 Functions: 62 65 95.4 %

          Line data    Source code
       1             : // EnergyPlus, Copyright (c) 1996-2024, The Board of Trustees of the University of Illinois,
       2             : // The Regents of the University of California, through Lawrence Berkeley National Laboratory
       3             : // (subject to receipt of any required approvals from the U.S. Dept. of Energy), Oak Ridge
       4             : // National Laboratory, managed by UT-Battelle, Alliance for Sustainable Energy, LLC, and other
       5             : // contributors. All rights reserved.
       6             : //
       7             : // NOTICE: This Software was developed under funding from the U.S. Department of Energy and the
       8             : // U.S. Government consequently retains certain rights. As such, the U.S. Government has been
       9             : // granted for itself and others acting on its behalf a paid-up, nonexclusive, irrevocable,
      10             : // worldwide license in the Software to reproduce, distribute copies to the public, prepare
      11             : // derivative works, and perform publicly and display publicly, and to permit others to do so.
      12             : //
      13             : // Redistribution and use in source and binary forms, with or without modification, are permitted
      14             : // provided that the following conditions are met:
      15             : //
      16             : // (1) Redistributions of source code must retain the above copyright notice, this list of
      17             : //     conditions and the following disclaimer.
      18             : //
      19             : // (2) Redistributions in binary form must reproduce the above copyright notice, this list of
      20             : //     conditions and the following disclaimer in the documentation and/or other materials
      21             : //     provided with the distribution.
      22             : //
      23             : // (3) Neither the name of the University of California, Lawrence Berkeley National Laboratory,
      24             : //     the University of Illinois, U.S. Dept. of Energy nor the names of its contributors may be
      25             : //     used to endorse or promote products derived from this software without specific prior
      26             : //     written permission.
      27             : //
      28             : // (4) Use of EnergyPlus(TM) Name. If Licensee (i) distributes the software in stand-alone form
      29             : //     without changes from the version obtained under this License, or (ii) Licensee makes a
      30             : //     reference solely to the software portion of its product, Licensee must refer to the
      31             : //     software as "EnergyPlus version X" software, where "X" is the version number Licensee
      32             : //     obtained under this License and may not use a different name for the software. Except as
      33             : //     specifically required in this Section (4), Licensee shall not use in a company name, a
      34             : //     product name, in advertising, publicity, or other promotional activities any name, trade
      35             : //     name, trademark, logo, or other designation of "EnergyPlus", "E+", "e+" or confusingly
      36             : //     similar designation, without the U.S. Department of Energy's prior written consent.
      37             : //
      38             : // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
      39             : // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
      40             : // AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
      41             : // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
      42             : // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
      43             : // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
      44             : // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
      45             : // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
      46             : // POSSIBILITY OF SUCH DAMAGE.
      47             : 
      48             : // C++ Headers
      49             : #include <algorithm>
      50             : #include <cassert>
      51             : #include <cmath>
      52             : #include <string>
      53             : 
      54             : // ObjexxFCL Headers
      55             : #include <ObjexxFCL/Array.functions.hh>
      56             : #include <ObjexxFCL/Fmath.hh>
      57             : #include <ObjexxFCL/Vector3.hh>
      58             : // #include <ObjexxFCL/Vector4.hh>
      59             : #include <ObjexxFCL/member.functions.hh>
      60             : #include <ObjexxFCL/random.hh>
      61             : #include <ObjexxFCL/string.functions.hh>
      62             : 
      63             : // EnergyPlus Headers
      64             : #include <EnergyPlus/DElightManagerF.hh>
      65             : #include <EnergyPlus/Data/EnergyPlusData.hh>
      66             : #include <EnergyPlus/DataBSDFWindow.hh>
      67             : #include <EnergyPlus/DataDElight.hh>
      68             : #include <EnergyPlus/DataDaylighting.hh>
      69             : #include <EnergyPlus/DataDaylightingDevices.hh>
      70             : #include <EnergyPlus/DataEnvironment.hh>
      71             : #include <EnergyPlus/DataErrorTracking.hh>
      72             : #include <EnergyPlus/DataHeatBalance.hh>
      73             : #include <EnergyPlus/DataIPShortCuts.hh>
      74             : #include <EnergyPlus/DataPrecisionGlobals.hh>
      75             : #include <EnergyPlus/DataStringGlobals.hh>
      76             : #include <EnergyPlus/DataSurfaces.hh>
      77             : #include <EnergyPlus/DataSystemVariables.hh>
      78             : #include <EnergyPlus/DataViewFactorInformation.hh>
      79             : #include <EnergyPlus/DaylightingDevices.hh>
      80             : #include <EnergyPlus/DaylightingManager.hh>
      81             : #include <EnergyPlus/DisplayRoutines.hh>
      82             : #include <EnergyPlus/FileSystem.hh>
      83             : #include <EnergyPlus/General.hh>
      84             : #include <EnergyPlus/HeatBalanceManager.hh>
      85             : #include <EnergyPlus/InputProcessing/InputProcessor.hh>
      86             : #include <EnergyPlus/InternalHeatGains.hh>
      87             : #include <EnergyPlus/OutputProcessor.hh>
      88             : #include <EnergyPlus/OutputReportPredefined.hh>
      89             : #include <EnergyPlus/PierceSurface.hh>
      90             : #include <EnergyPlus/SQLiteProcedures.hh>
      91             : #include <EnergyPlus/ScheduleManager.hh>
      92             : #include <EnergyPlus/SolarReflectionManager.hh>
      93             : #include <EnergyPlus/SolarShading.hh>
      94             : #include <EnergyPlus/SurfaceOctree.hh>
      95             : #include <EnergyPlus/UtilityRoutines.hh>
      96             : #include <EnergyPlus/WindowComplexManager.hh>
      97             : #include <EnergyPlus/WindowManager.hh>
      98             : 
      99             : namespace EnergyPlus::Dayltg {
     100             : 
     101             : // MODULE INFORMATION
     102             : //       AUTHOR         Fred Winkelmann
     103             : //       DATE WRITTEN   July 1997, December 1998
     104             : //       MODIFIED       Oct 2004; LKL -- Efficiencies and code restructure
     105             : //                      Aug 2012: BG -- Added availability schedule
     106             : 
     107             : // PURPOSE OF THIS MODULE:
     108             : // Manages the daylighting calculations for each thermal zone that has an associated
     109             : // Daylighting:Controls object.
     110             : 
     111             : // Includes calculation of interior daylight illuminance and glare
     112             : // from each of the windows in a zone, control of window shading devices
     113             : // to reduce glare, and control of overhead electric lighting in response
     114             : // to interior daylight illuminance level at one or two user-specified
     115             : // reference points at which sensors are located.
     116             : 
     117             : // METHODOLOGY EMPLOYED:
     118             : // REFERENCES:
     119             : // "Daylighting Calculation in DOE-2," F.C.Winkelmann, LBL-11353, May 1983
     120             : // "Daylighting Simulation in the DOE-2 Building Energy Analysis Program,"
     121             : // F.C. Winkelmann and S. Selkowitz, Energy and Buildings 8(1985)271-286
     122             : 
     123             : // OTHER NOTES:
     124             : // This module was created from DOE-2.1E subroutines.
     125             : 
     126             : // Correspondence between DOE-2.1E and EnergyPlus subroutine names:
     127             : 
     128             : // DOE-2.1E    EnergyPlus                      In Module           Called from Module
     129             : // DAVREF      DayltgAveInteriorReflectance    DaylightingManager DaylightingManager
     130             : // DCOF        CalcDayltgCoefficients          DaylightingManager DaylightingManager
     131             : // DCROSS      DayltgCrossProduct              DaylightingManager DaylightingManager
     132             : // DEXTIL      DayltgCurrentExtHorizIllum      WeatherManager     WeatherManager
     133             : // DGLARE      DayltgGlare                     DaylightingManager DaylightingManager
     134             : // DHILL       DayltgExtHorizIllum             DaylightingManager DaylightingManager
     135             : // DHITSH      DayltgHitObstruction            DaylightingManager DaylightingManager
     136             : // DINTIL      DayltgInteriorIllum             DaylightingManager HeatBalanceSurfaceManager
     137             : // DLTSYS      DayltgElecLightingControl       DaylightingManager HeatBalanceSurfaceManager
     138             : // DNSOL       not used
     139             : // DPFAC       DayltgPositionFactor            DaylightingManager DaylightingManager
     140             : // DPIERC      PierceSurface                   PierceSurface      DaylightingManager
     141             : // DREFLT      DayltgInterReflectedIllum       DaylightingManager DaylightingManager
     142             : // DSKYLU      DayltgSkyLuminance              DaylightingManager DaylightingManager
     143             : // DTHLIM      DayltgAzimuthLimits             DaylightingManager DaylightingManager
     144             : // DLUMEF      DayltgLuminousEfficacy          WeatherManager     WeatherManager
     145             : 
     146             : // Using/Aliasing
     147             : using namespace DataSurfaces;
     148             : 
     149         287 : void DayltgAveInteriorReflectance(EnergyPlusData &state, int const enclNum) // Enclosure number
     150             : {
     151             : 
     152             :     // SUBROUTINE INFORMATION:
     153             :     //       AUTHOR         Fred Winkelmann
     154             :     //       DATE WRITTEN   July 1997
     155             :     //       MODIFIED       Mar 2004, FCW: add calculation of following SurfaceWindow variables:
     156             :     //                        EnclAreaMinusThisSurf, EnclAreaReflProdMinusThisSurf, RhoCeilingWall,
     157             :     //                        RhoFloorWall, FractionUpgoing. Add calculation of ZoneDaylight%floorVisRefl.
     158             : 
     159             :     // PURPOSE OF THIS SUBROUTINE:
     160             :     // Called by CalcDayltgCoefficients for each daylit zone. Determines total
     161             :     // area and area-weighted average visible reflectance of
     162             :     // all inside faces of the surfaces of a zone.  In addition, finds
     163             :     // area and average reflectance of interzone, underground and exterior
     164             :     // heat-transfer surfaces in the following categories: floor (tilt > 170 deg),
     165             :     // ceiling (tilt < 10 deg), and wall (10 < tilt < 170 deg).
     166             :     // The window reflectance values used here assume the windows have no shading
     167             :     // devices. This information is used in the calculation of the
     168             :     // internally-reflected daylighting component.
     169             : 
     170             :     // Finds total number of exterior windows in the space.
     171             : 
     172             :     // REFERENCES:
     173             :     // Based on DOE-2.1E subroutine DAVREF
     174         287 :     auto &dl = state.dataDayltg;
     175             : 
     176             :     // Total inside surface area, including windows
     177         287 :     Real64 AInsTot = 0.0;
     178             :     // Sum of products of inside surface area * vis reflectance
     179         287 :     Real64 ARHTOT = 0.0;
     180             : 
     181             :     // Area sum and area * reflectance sum for different orientations
     182         287 :     std::array<Real64, (int)FWC::Num> AR = {0.0, 0.0, 0.0};
     183         287 :     std::array<Real64, (int)FWC::Num> ARH = {0.0, 0.0, 0.0};
     184             :     // Loop over surfaces in the zone's enclosure
     185             : 
     186         287 :     auto &thisEnclosure = state.dataViewFactor->EnclSolInfo(enclNum);
     187        3450 :     for (int ISurf : thisEnclosure.SurfacePtr) {
     188        3163 :         auto const &surf = state.dataSurface->Surface(ISurf);
     189             : 
     190        3163 :         SurfaceClass IType = surf.Class;
     191             :         // Error if window has multiplier > 1 since this causes incorrect illuminance calc
     192        3163 :         if (IType == SurfaceClass::Window && surf.Multiplier > 1.0) {
     193           0 :             if (thisEnclosure.TotalEnclosureDaylRefPoints > 0) {
     194           0 :                 ShowSevereError(state, format("DayltgAveInteriorReflectance: Multiplier > 1.0 for window {} in Zone={}", surf.Name, surf.ZoneName));
     195           0 :                 ShowContinueError(state, "...not allowed since it is in a zone or enclosure with daylighting.");
     196           0 :                 ShowFatalError(state, "Program terminates due to preceding conditions.");
     197             :             } else {
     198           0 :                 ShowSevereError(state, format("DayltgAveInteriorReflectance: Multiplier > 1.0 for window {} in Zone={}", surf.Name, surf.ZoneName));
     199           0 :                 ShowContinueError(state, "...an adjacent Zone has daylighting. Simulation cannot proceed.");
     200           0 :                 ShowFatalError(state, "Program terminates due to preceding conditions.");
     201             :             }
     202             :         }
     203        3163 :         if (IType == SurfaceClass::Wall || IType == SurfaceClass::Floor || IType == SurfaceClass::Roof || IType == SurfaceClass::Window ||
     204             :             IType == SurfaceClass::Door) {
     205        2918 :             Real64 AREA = surf.Area;
     206             :             // In following, FrameArea and DividerArea can be non-zero only for exterior windows
     207        2918 :             AInsTot += AREA + state.dataSurface->SurfWinFrameArea(ISurf) * (1.0 + 0.5 * state.dataSurface->SurfWinProjCorrFrIn(ISurf)) +
     208        2918 :                        state.dataSurface->SurfWinDividerArea(ISurf) * (1.0 + state.dataSurface->SurfWinProjCorrDivIn(ISurf));
     209        2918 :             ARHTOT += AREA * state.dataConstruction->Construct(surf.Construction).ReflectVisDiffBack +
     210        2918 :                       state.dataSurface->SurfWinFrameArea(ISurf) * (1.0 + 0.5 * state.dataSurface->SurfWinProjCorrFrIn(ISurf)) *
     211        2918 :                           (1.0 - state.dataSurface->SurfWinFrameSolAbsorp(ISurf)) +
     212        2918 :                       state.dataSurface->SurfWinDividerArea(ISurf) * (1.0 + state.dataSurface->SurfWinProjCorrDivIn(ISurf)) *
     213        2918 :                           (1.0 - state.dataSurface->SurfWinDividerSolAbsorp(ISurf));
     214             : 
     215        2918 :             FWC fwc = FWC::Ceiling;                                     // Ceiling
     216        2918 :             if (surf.Tilt > 10.0 && surf.Tilt < 170.0) fwc = FWC::Wall; // Wall
     217        2918 :             if (surf.Tilt >= 170.0) fwc = FWC::Floor;                   // Floor
     218        2918 :             AR[(int)fwc] += AREA + state.dataSurface->SurfWinFrameArea(ISurf) * (1.0 + 0.5 * state.dataSurface->SurfWinProjCorrFrIn(ISurf)) +
     219        2918 :                             state.dataSurface->SurfWinDividerArea(ISurf) * (1.0 + state.dataSurface->SurfWinProjCorrDivIn(ISurf));
     220        5836 :             ARH[(int)fwc] += AREA * state.dataConstruction->Construct(surf.Construction).ReflectVisDiffBack +
     221        2918 :                              state.dataSurface->SurfWinFrameArea(ISurf) * (1.0 + 0.5 * state.dataSurface->SurfWinProjCorrFrIn(ISurf)) *
     222        2918 :                                  (1.0 - state.dataSurface->SurfWinFrameSolAbsorp(ISurf)) +
     223        2918 :                              state.dataSurface->SurfWinDividerArea(ISurf) * (1.0 + state.dataSurface->SurfWinProjCorrDivIn(ISurf)) *
     224        2918 :                                  (1.0 - state.dataSurface->SurfWinDividerSolAbsorp(ISurf));
     225             :         }
     226             :     }
     227             : 
     228             :     // Average inside surface reflectance of enclosure
     229         287 :     if (AInsTot <= 0.0) {
     230           0 :         ShowSevereError(state, format("DayltgAveInteriorReflectance: Total opaque surface area is <=0.0 in solar enclosure={}", thisEnclosure.Name));
     231           0 :         ShowFatalError(state, "Program terminates due to preceding conditions.");
     232             :     }
     233         287 :     dl->enclDaylight(enclNum).aveVisDiffReflect = ARHTOT / AInsTot;
     234             :     // Total inside surface area of enclosure
     235         287 :     dl->enclDaylight(enclNum).totInsSurfArea = AInsTot;
     236             :     // Average floor visible reflectance
     237         287 :     dl->enclDaylight(enclNum).floorVisRefl = ARH[iFWC_Ceiling] / (AR[iFWC_Ceiling] + 1.e-6);
     238             : 
     239        3450 :     for (int ISurf : thisEnclosure.SurfacePtr) {
     240        3163 :         auto const &surf = state.dataSurface->Surface(ISurf);
     241        3163 :         if (surf.Class != SurfaceClass::Wall && surf.Class != SurfaceClass::Floor && surf.Class != SurfaceClass::Roof) continue;
     242             : 
     243             :         // Remove this surface from the space inside surface area and area*reflectivity
     244             :         // The resulting areas are AP(ITILT). The resulting area*reflectivity is ARHP(ITILT).
     245             :         // Initialize gross area of surface (including subsurfaces)
     246        1865 :         Real64 ATWL = surf.Area; // This is the surface area less subsurfaces
     247             :         // Area * reflectance for this surface, excluding attached windows and doors
     248        1865 :         Real64 ARHTWL = surf.Area * state.dataConstruction->Construct(surf.Construction).ReflectVisDiffBack;
     249             : 
     250        1865 :         FWC fwc = (surf.Tilt > 45.0 && surf.Tilt < 135.0) ? FWC::Wall : ((surf.Tilt >= 135.0) ? FWC::Floor : FWC::Ceiling);
     251             : 
     252             :         // Loop over windows and doors on this wall
     253       23833 :         for (int IWinDr : thisEnclosure.SurfacePtr) {
     254       21968 :             auto const &surfWinDr = state.dataSurface->Surface(IWinDr);
     255       21968 :             if ((surfWinDr.Class != SurfaceClass::Window && surfWinDr.Class != SurfaceClass::Door) || surfWinDr.BaseSurf != ISurf) continue;
     256             : 
     257        1053 :             ATWL += surfWinDr.Area + state.dataSurface->SurfWinFrameArea(IWinDr) * (1.0 + 0.5 * state.dataSurface->SurfWinProjCorrFrIn(IWinDr)) +
     258        1053 :                     state.dataSurface->SurfWinDividerArea(IWinDr) * (1.0 + state.dataSurface->SurfWinProjCorrDivIn(IWinDr));
     259        1053 :             ARHTWL += surfWinDr.Area * state.dataConstruction->Construct(surfWinDr.Construction).ReflectVisDiffBack +
     260        1053 :                       state.dataSurface->SurfWinFrameArea(IWinDr) * (1.0 + 0.5 * state.dataSurface->SurfWinProjCorrFrIn(IWinDr)) *
     261        1053 :                           (1.0 - state.dataSurface->SurfWinFrameSolAbsorp(IWinDr)) +
     262        1053 :                       state.dataSurface->SurfWinDividerArea(IWinDr) * (1.0 + state.dataSurface->SurfWinProjCorrDivIn(IWinDr)) *
     263        1053 :                           (1.0 - state.dataSurface->SurfWinDividerSolAbsorp(IWinDr));
     264             :         }
     265             : 
     266             :         std::array<Real64, (int)FWC::Num> AP;
     267             :         std::array<Real64, (int)FWC::Num> ARHP;
     268             :         // Inside surface area of floor, walls and ceilings, minus surface ISurf and its subsurfaces
     269        7460 :         for (int iFWC = iFWC_Floor; iFWC < (int)FWC::Num; ++iFWC) {
     270        5595 :             if (iFWC == (int)fwc) {
     271        1865 :                 AP[iFWC] = AR[iFWC] - ATWL;
     272        1865 :                 ARHP[iFWC] = ARH[iFWC] - ARHTWL;
     273             :             } else {
     274        3730 :                 AP[iFWC] = AR[iFWC];
     275        3730 :                 ARHP[iFWC] = ARH[iFWC];
     276             :             }
     277             :         }
     278        1865 :         state.dataSurface->SurfaceWindow(ISurf).EnclAreaMinusThisSurf = AP;
     279        1865 :         state.dataSurface->SurfaceWindow(ISurf).EnclAreaReflProdMinusThisSurf = ARHP;
     280             :     } // for (ISurf)
     281             : 
     282        3450 :     for (int IWin : thisEnclosure.SurfacePtr) {
     283        3163 :         auto const &surf = state.dataSurface->Surface(IWin);
     284        3163 :         if (surf.Class != SurfaceClass::Window) continue;
     285             : 
     286         847 :         auto &surfWin = state.dataSurface->SurfaceWindow(IWin);
     287         847 :         auto const &zone = state.dataHeatBal->Zone(surf.Zone);
     288         847 :         int ISurf = surf.BaseSurf;
     289             : 
     290             :         // Ratio of floor-to-window-center height and average floor-to-ceiling height
     291         847 :         Real64 ETA = max(0.0, min(1.0, (surfWin.WinCenter.z - zone.OriginZ) * zone.FloorArea / zone.Volume));
     292             : 
     293         847 :         std::array<Real64, (int)FWC::Num> AP = state.dataSurface->SurfaceWindow(ISurf).EnclAreaMinusThisSurf;
     294         847 :         std::array<Real64, (int)FWC::Num> ARHP = state.dataSurface->SurfaceWindow(ISurf).EnclAreaReflProdMinusThisSurf;
     295             :         // Average reflectance seen by light moving up (RhoCeilingWall) and down (RhoFloorWall)
     296             :         // across horizontal plane through center of window
     297         847 :         surfWin.rhoCeilingWall = (ARHP[iFWC_Wall] * (1.0 - ETA) + ARHP[iFWC_Ceiling]) / (AP[iFWC_Wall] * (1.0 - ETA) + AP[iFWC_Ceiling] + 1.0e-5);
     298         847 :         surfWin.rhoFloorWall = (ARHP[iFWC_Wall] * ETA + ARHP[iFWC_Floor]) / (AP[iFWC_Wall] * ETA + AP[iFWC_Floor] + 1.e-9);
     299             : 
     300             :         // Angle factor for windows with diffusing shades. SurfaceWindow(IWin)%FractionUpgoing is
     301             :         // fraction of light from the shade that goes up toward ceiling and upper part of walls.
     302             :         // 1 - SurfaceWindow(IWin)%FractionUpgoing is fraction that goes down toward floor and lower part of walls.
     303         847 :         surfWin.fractionUpgoing = surf.Tilt / 180.0;
     304             : 
     305             :         // Daylighting shelf simplification:  All light goes up to the ceiling regardless of orientation of shelf
     306         847 :         if (state.dataSurface->SurfDaylightingShelfInd(IWin) > 0) {
     307           1 :             if (state.dataDaylightingDevicesData->Shelf(state.dataSurface->SurfDaylightingShelfInd(IWin)).InSurf > 0) surfWin.fractionUpgoing = 1.0;
     308             :         }
     309             :     } // for (IWin)
     310         287 : } // DayltgAveInteriorReflectance()
     311             : 
     312       14627 : void CalcDayltgCoefficients(EnergyPlusData &state)
     313             : {
     314             : 
     315             :     // SUBROUTINE INFORMATION:
     316             :     //       AUTHOR         Fred Winkelmann
     317             :     //       DATE WRITTEN   July 1997
     318             :     //       MODIFIED       FW, Jan 2002: add variable slat angle blinds
     319             :     //                      FW, Mar 2002: add triangular windows
     320             :     //                      FW, Oct 2002: remove warning on window discretization relative to
     321             :     //                                    reference point distance to window plane
     322             :     //                      FW, Jan 2003: add between-glass shades and blinds
     323             :     //                      FW, Apr 2003: initialize shading type to 'NOSHADE' in window loop
     324             :     //                      PE, May 2003: add light pipes (tubular daylighting devices)
     325             :     //                      FW, Jul 2003: account for possible non-zero transmittance of
     326             :     //                                    shading surfaces (previously all shading surfaces were
     327             :     //                                    assumed to be opaque)
     328             :     //                      PE, Aug 2003: add daylighting shelves
     329             :     //                      FW, Sep 2003: write the bare-window overcast sky daylight factors to the eio file
     330             :     //                      FW, Nov 2003: add exterior beam and sky solar diffuse reflection from obstructions;
     331             :     //                                    add beam solar and sky solar reflection from ground with obstructions.
     332             :     //                      FW, Nov 2003: change expression for NDIVX, NDIVY (no. of window elements in X,Y) to
     333             :     //                                    round up to nearest integer rather than down
     334             :     //                      FW, Nov 2003: add specular reflection of beam solar from obstructions
     335             :     //                      RJH, Jan 2004: add alternative daylighting analysis using DElight
     336             :     //                                     All modifications demarked with RJH (Rob Hitchcock)
     337             :     //                      FW, Feb 2004: add daylighting through interior windows
     338             :     //                      FW, Apr 2004: add light well efficiency that multiplies glazing transmittance
     339             :     //                      FW, Apr 2004: add diffusing glazing
     340             :     //                      RJH, Jul 2004: add error handling for warnings/errors returned from DElight
     341             :     //                      LKL, Oct 2004: Separate "map" and "ref" point calculations -- move some input routines to
     342             :     //                                     separate routines.
     343             : 
     344             :     // PURPOSE OF THIS SUBROUTINE:
     345             :     // Calculates daylighting factors for later use in the time-step loop.
     346             : 
     347             :     // METHODOLOGY EMPLOYED:
     348             : 
     349             :     // For each combination of exterior window and reference point in a zone,
     350             :     // calculates daylighting factors (interior illuminance / exterior illuminance)
     351             :     // and glare factors for clear and overcast skies and for windows with and
     352             :     // without shading devices. These factors are calculated for each hourly
     353             :     // sun position for design days and for selected days throughout the year.
     354             : 
     355             :     // If a target zone has one or more interior windows, also calculates daylighting
     356             :     // factors for the target zone that are associated with exterior windows in adjacent
     357             :     // zones that share interior windows with the target zone.
     358             : 
     359             :     // The daylight illuminance at a reference point from a window is determined
     360             :     // by dividing the window into rectangular elements and calculating the illuminance
     361             :     // reaching the reference point directly from each element. The illumination
     362             :     // from an element can come from the sky or ground if the window is unshaded, or from
     363             :     // a shading device illuminated by solar radiation. Also considered are the
     364             :     // illuminance contribution from interreflection among the zone's interior surfaces
     365             :     // and sunlight striking the reference point.
     366             : 
     367             :     // In calculating sky-related interior illuminance and luminance quantities,
     368             :     // the sky luminance for the different sky types are determined from distributions
     369             :     // in which the zenith luminance is normalized to 1.0 cd/m2. Similarly, sun-related
     370             :     // illuminance and luminance quantities are based on beam normal solar illuminance
     371             :     // normalized to 1.0 lux.
     372             :     // The daylight and glare factors calculated in this subroutine are used in DayltgInteriorIllum
     373             :     // to get the daylight illuminance and glare at each time step.
     374             :     // Based on this information and user-input lighting setpoint and type of lighting
     375             :     // control system, DayltgElecLightingControl then determines how much the overhead electric lighting
     376             :     // can be reduced.
     377             : 
     378             :     // REFERENCES:
     379             :     // Based on DOE-2.1E subroutine DCOF.
     380             : 
     381       14627 :     auto &dl = state.dataDayltg;
     382             : 
     383       14627 :     if (dl->CalcDayltghCoefficients_firstTime) {
     384         796 :         GetDaylightingParametersInput(state);
     385         796 :         CheckTDDsAndLightShelvesInDaylitZones(state);
     386         796 :         AssociateWindowShadingControlWithDaylighting(state);
     387         796 :         dl->CalcDayltghCoefficients_firstTime = false;
     388             :     } // End of check if firstTime
     389             : 
     390             :     // Find the total number of exterior windows associated with all Daylighting:Detailed enclosures.
     391             :     // An exterior window is associated with such a enclosure if (1) it is an exterior window in the enclosure, or
     392             :     // (2) it is an exterior window in an adjacent enclosure that shares an interior window with the enclosure.
     393             :     // Note that exterior windows in category (2) may be counted more than once if an adjacent enclosure
     394             :     // is adjacent to more than one daylit enclosure with which the adjacent enclosure shares interior windows.
     395             :     // If there are no interior windows in a building, than TotWindowsWithDayl is just the total number of
     396             :     // exterior windows in Daylighting:Detailed enclosures. Note that it is possible for a
     397             :     // Daylighting:Detailed enclosure to have zero exterior windows of its own, but it may have an interior
     398             :     // through which daylight passes from adjacent enclosures with exterior windows.
     399       14627 :     if ((int)dl->DaylRefPt.size() == 0) return;
     400         541 :     if (state.dataGlobal->BeginSimFlag) {
     401          64 :         dl->TotWindowsWithDayl = 0;
     402         785 :         for (int enclNum = 1; enclNum <= state.dataViewFactor->NumOfSolarEnclosures; ++enclNum) {
     403         721 :             dl->TotWindowsWithDayl += dl->enclDaylight(enclNum).NumOfDayltgExtWins;
     404             :         }
     405             :     }
     406             : 
     407         541 :     if (dl->TotWindowsWithDayl == 0) return;
     408             : 
     409             :     //-----------------------------------------!
     410             :     // Detailed daylighting factor calculation !
     411             :     //-----------------------------------------!
     412         541 :     if (!state.dataSysVars->DetailedSolarTimestepIntegration && !state.dataGlobal->KickOffSizing && !state.dataGlobal->KickOffSimulation) {
     413         225 :         if (state.dataGlobal->WarmupFlag) {
     414         207 :             DisplayString(state, "Calculating Detailed Daylighting Factors, Start Date=" + state.dataEnvrn->CurMnDy);
     415             :         } else {
     416          18 :             DisplayString(state, "Updating Detailed Daylighting Factors, Start Date=" + state.dataEnvrn->CurMnDy);
     417             :         }
     418             :     }
     419             : 
     420         541 :     if (state.dataGlobal->BeginSimFlag) {
     421             : 
     422             :         // Find minimum solid angle subtended by an interior window in Daylighting:Detailed zones.
     423             :         // Used in calculating daylighting through interior windows.
     424          64 :         CalcMinIntWinSolidAngs(state);
     425             : 
     426             :         // Warning if detailed daylighting has been requested for a zone with no associated exterior windows.
     427         785 :         for (int enclNum = 1; enclNum <= state.dataViewFactor->NumOfSolarEnclosures; ++enclNum) {
     428         721 :             auto const &thisEnclDaylight = dl->enclDaylight(enclNum);
     429         721 :             if (thisEnclDaylight.NumOfDayltgExtWins == 0 && thisEnclDaylight.TotalExtWindows == 0) {
     430         249 :                 for (int daylightCtrlNum : thisEnclDaylight.daylightControlIndexes) {
     431           0 :                     if (dl->daylightControl(daylightCtrlNum).TotalDaylRefPoints > 0) {
     432           0 :                         ShowWarningError(
     433             :                             state,
     434           0 :                             format("Detailed daylighting will not be done for Daylighting:Controls={}", dl->daylightControl(daylightCtrlNum).Name));
     435           0 :                         ShowContinueError(state, "because it has no associated exterior windows.");
     436             :                     }
     437         249 :                 }
     438             :             }
     439             : 
     440             :             // Find area and reflectance quantities used in calculating inter-reflected illuminance.
     441             :             // TH 9/10/2009. Need to calculate for zones without daylighting controls (TotalDaylRefPoints = 0)
     442             :             // but with adjacent zones having daylighting controls.
     443         721 :             if ((thisEnclDaylight.NumOfDayltgExtWins > 0) || thisEnclDaylight.adjEnclHasDayltgCtrl) {
     444         287 :                 DayltgAveInteriorReflectance(state, enclNum);
     445             :             }
     446             :         }
     447             :     }
     448             : 
     449         541 :     int numTDD = (int)state.dataDaylightingDevicesData->TDDPipe.size();
     450             :     // Zero daylighting factor arrays
     451         541 :     if (numTDD > 0) {
     452           5 :         int iHrBeg = state.dataSysVars->DetailedSolarTimestepIntegration ? state.dataGlobal->HourOfDay : 1;
     453           5 :         int iHrEnd = state.dataSysVars->DetailedSolarTimestepIntegration ? state.dataGlobal->HourOfDay : Constant::HoursInDay;
     454         125 :         for (int iHr = iHrBeg; iHr <= iHrEnd; ++iHr) {
     455         360 :             for (int iTDD = 1; iTDD <= numTDD; ++iTDD) {
     456         240 :                 dl->TDDTransVisBeam(iHr, iTDD) = 0.0;
     457         960 :                 dl->TDDFluxInc(iHr, iTDD) = Illums();
     458         960 :                 dl->TDDFluxTrans(iHr, iTDD) = Illums();
     459             :             }
     460             :         }
     461             :     }
     462             : 
     463         541 :     if (!state.dataSysVars->DetailedSolarTimestepIntegration) {
     464         541 :         if (state.dataGlobal->BeginDayFlag) {
     465             :             // Calculate hourly sun angles, clear sky zenith luminance, and exterior horizontal illuminance
     466         541 :             dl->sunAngles = SunAngles();
     467         541 :             dl->sunAnglesHr = {SunAngles()};
     468         541 :             dl->horIllum = {Illums()};
     469       13525 :             for (int IHR = 1; IHR <= Constant::HoursInDay; ++IHR) {
     470       12984 :                 auto const &surfSunCosHr = state.dataSurface->SurfSunCosHourly(IHR);
     471       12984 :                 if (surfSunCosHr.z < DataEnvironment::SunIsUpValue)
     472        8287 :                     continue; // Skip if sun is below horizon //Autodesk SurfSunCosHourly was uninitialized here
     473             : 
     474        4697 :                 Real64 phi = Constant::PiOvr2 - std::acos(surfSunCosHr.z);
     475        4697 :                 Real64 theta = std::atan2(surfSunCosHr.y, surfSunCosHr.x);
     476        4697 :                 dl->sunAngles = dl->sunAnglesHr[IHR] = {phi, std::sin(phi), std::cos(phi), theta};
     477             : 
     478        4697 :                 DayltgExtHorizIllum(state, dl->horIllum[IHR]);
     479             :             }
     480             :         }
     481             :     } else { // timestep integrated calculations
     482           0 :         dl->sunAngles = dl->sunAnglesHr[state.dataGlobal->HourOfDay] = {SunAngles()};
     483           0 :         dl->horIllum[state.dataGlobal->HourOfDay] = Illums();
     484           0 :         auto const &surfSunCosHr = state.dataSurface->SurfSunCosHourly(state.dataGlobal->HourOfDay);
     485           0 :         if (!(surfSunCosHr.z < DataEnvironment::SunIsUpValue)) { // Skip if sun is below horizon
     486           0 :             Real64 phi = Constant::PiOvr2 - std::acos(surfSunCosHr.z);
     487           0 :             Real64 theta = std::atan2(surfSunCosHr.y, surfSunCosHr.x);
     488           0 :             dl->sunAngles = dl->sunAnglesHr[state.dataGlobal->HourOfDay] = {phi, std::sin(phi), std::cos(phi), theta};
     489           0 :             DayltgExtHorizIllum(state, dl->horIllum[state.dataGlobal->HourOfDay]);
     490             :         }
     491             :     }
     492             : 
     493         541 :     CalcDayltgCoeffsRefMapPoints(state);
     494             : 
     495         605 :     if (dl->doSkyReporting && (!state.dataGlobal->KickOffSizing && !state.dataGlobal->KickOffSimulation) &&
     496          64 :         (dl->FirstTimeDaylFacCalc && dl->TotWindowsWithDayl > 0 &&
     497          64 :          (!state.dataSysVars->DetailedSolarTimestepIntegration || state.dataGlobal->HourOfDay == 12))) {
     498             :         // Write the bare-window four sky daylight factors at noon time to the eio file; this is done only
     499             :         // for first time that daylight factors are calculated and so is insensitive to possible variation
     500             :         // due to change in ground reflectance from month to month, or change in storm window status.
     501             :         static constexpr std::string_view Format_700("! <Sky Daylight Factors>, Sky Type, MonthAndDay, Daylighting Control Name, Enclosure Name, "
     502             :                                                      "Window Name, Reference Point, Daylight Factor\n");
     503          64 :         print(state.files.eio, Format_700);
     504         354 :         for (int controlNum = 1; controlNum <= (int)dl->daylightControl.size(); ++controlNum) {
     505         290 :             auto &thisDayltgCtrl = dl->daylightControl(controlNum);
     506         290 :             int enclNum = thisDayltgCtrl.enclIndex;
     507         290 :             auto &thisEnclDaylight = dl->enclDaylight(enclNum);
     508             : 
     509         290 :             if (thisEnclDaylight.NumOfDayltgExtWins == 0 || !thisEnclDaylight.hasSplitFluxDaylighting) continue;
     510        1135 :             for (int windowCounter = 1; windowCounter <= thisEnclDaylight.NumOfDayltgExtWins; ++windowCounter) {
     511         848 :                 int windowSurfNum = thisEnclDaylight.DayltgExtWinSurfNums(windowCounter);
     512             :                 // For this report, do not include ext wins in zone adjacent to ZoneNum since the inter-reflected
     513             :                 // component will not be calculated for these windows until the time-step loop.
     514         848 :                 if (state.dataSurface->Surface(windowSurfNum).SolarEnclIndex != enclNum) continue;
     515             :                 // Output for each reference point, for each sky. Group by sky type first
     516             : 
     517             :                 static constexpr std::array<std::string_view, (int)SkyType::Num> skyTypeStrings = {
     518             :                     "Clear Sky", "Clear Turbid Sky", "Intermediate Sky", "Overcast Sky"};
     519             : 
     520        4235 :                 for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
     521        9340 :                     for (int refPtNum = 1; refPtNum <= thisDayltgCtrl.TotalDaylRefPoints; ++refPtNum) {
     522        5952 :                         Real64 DaylFac = thisDayltgCtrl.daylFac[12](windowCounter, refPtNum, 1)[iLum_Illum].sky[iSky];
     523        5952 :                         print(state.files.eio,
     524             :                               " Sky Daylight Factors,{},{},{},{},{},{},{:.4R}\n",
     525        5952 :                               skyTypeStrings[iSky],
     526        5952 :                               state.dataEnvrn->CurMnDy,
     527        5952 :                               thisDayltgCtrl.Name,
     528        5952 :                               state.dataViewFactor->EnclSolInfo(thisDayltgCtrl.enclIndex).Name,
     529        5952 :                               state.dataSurface->Surface(windowSurfNum).Name,
     530        5952 :                               dl->DaylRefPt(thisDayltgCtrl.refPts(refPtNum).num).Name,
     531             :                               DaylFac);
     532             :                     } // for (refPtNum)
     533             :                 }     // for (iSky)
     534             :             }         // for (windowCounter)
     535             :         }             // for (controlNum)
     536          64 :         dl->FirstTimeDaylFacCalc = false;
     537          64 :         dl->doSkyReporting = false;
     538             :     } // if (detailedIntegration etc.)
     539             : 
     540             :     // Skip if no daylight windows
     541         541 :     if (dl->TotWindowsWithDayl == 0) return;
     542             : 
     543             :     // Skip if no request of reporting
     544         541 :     if ((!dl->DFSReportSizingDays) && (!dl->DFSReportAllShadowCalculationDays)) return;
     545             : 
     546             :     // Skip duplicate calls
     547           9 :     if (state.dataGlobal->KickOffSizing) return;
     548           7 :     if (state.dataGlobal->DoingSizing) return;
     549           5 :     if (state.dataGlobal->KickOffSimulation) return;
     550             : 
     551           2 :     if (dl->DFSReportSizingDays) {
     552           2 :         if (state.dataGlobal->DoWeathSim && state.dataGlobal->DoDesDaySim) {
     553           0 :             if (state.dataGlobal->KindOfSim == Constant::KindOfSim::RunPeriodWeather) return;
     554             :         }
     555             :     }
     556             : 
     557           2 :     if (dl->DFSReportAllShadowCalculationDays) {
     558           0 :         if (state.dataGlobal->KindOfSim != Constant::KindOfSim::RunPeriodWeather) return;
     559             :     }
     560             : 
     561             :     // open a new file eplusout.dfs for saving the daylight factors
     562           2 :     if (dl->CreateDFSReportFile) {
     563           1 :         InputOutputFile &dfs = state.files.dfs.ensure_open(state, "CalcDayltgCoefficients", state.files.outputControl.dfs);
     564           1 :         print(dfs, "{}\n", "This file contains daylight factors for all exterior windows of daylight enclosures.");
     565           1 :         print(dfs, "{}\n", "MonthAndDay,Enclosure Name,Zone Name,Window Name,Window State");
     566           1 :         print(dfs,
     567             :               "{}\n",
     568             :               "Hour,Reference Point,Daylight Factor for Clear Sky,Daylight Factor for Clear Turbid Sky,"
     569             :               "Daylight Factor for Intermediate Sky,Daylight Factor for Overcast Sky");
     570           1 :         dl->CreateDFSReportFile = false;
     571             :     }
     572             : 
     573          26 :     for (int controlNum = 1; controlNum <= (int)dl->daylightControl.size(); ++controlNum) {
     574          24 :         auto &thisDayltgCtrl = dl->daylightControl(controlNum);
     575          24 :         int enclNum = thisDayltgCtrl.enclIndex;
     576          24 :         auto &thisEnclDaylight = dl->enclDaylight(enclNum);
     577          24 :         if (thisEnclDaylight.NumOfDayltgExtWins == 0) continue;
     578             : 
     579          48 :         for (int windowCounter = 1; windowCounter <= thisEnclDaylight.NumOfDayltgExtWins; ++windowCounter) {
     580          24 :             int windowSurfNum = thisEnclDaylight.DayltgExtWinSurfNums(windowCounter);
     581             : 
     582             :             // For this report, do not include ext wins in zone/enclosure adjacent to ZoneNum since the inter-reflected
     583             :             // component will not be calculated for these windows until the time-step loop.
     584          24 :             if (state.dataSurface->Surface(windowSurfNum).SolarEnclIndex == enclNum) {
     585             : 
     586             :                 int ISA;
     587          24 :                 if (state.dataSurface->SurfWinMovableSlats(windowSurfNum)) {
     588             :                     // variable slat angle - MaxSlatangle sets
     589           0 :                     ISA = Material::MaxSlatAngs + 1;
     590          24 :                 } else if (state.dataSurface->Surface(windowSurfNum).HasShadeControl) {
     591             :                     // window shade or blind with fixed slat angle
     592           0 :                     ISA = 2;
     593             :                 } else {
     594             :                     // base window
     595          24 :                     ISA = 1;
     596             :                 }
     597             : 
     598             :                 // loop over each slat angle
     599          48 :                 for (int ISlatAngle = 1; ISlatAngle <= ISA; ++ISlatAngle) {
     600          24 :                     if (ISlatAngle == 1) {
     601             :                         // base window without shades, screens, or blinds
     602          24 :                         print(state.files.dfs,
     603             :                               "{},{},{},{},Base Window\n",
     604          24 :                               state.dataEnvrn->CurMnDy,
     605          24 :                               state.dataViewFactor->EnclSolInfo(enclNum).Name,
     606          24 :                               state.dataHeatBal->Zone(thisDayltgCtrl.zoneIndex).Name,
     607          24 :                               state.dataSurface->Surface(windowSurfNum).Name);
     608           0 :                     } else if (ISlatAngle == 2 && ISA == 2) {
     609             :                         // window shade or blind with fixed slat angle
     610           0 :                         print(state.files.dfs,
     611             :                               "{},{},{},{},Blind or Slat Applied\n",
     612           0 :                               state.dataEnvrn->CurMnDy,
     613           0 :                               state.dataViewFactor->EnclSolInfo(enclNum).Name,
     614           0 :                               state.dataHeatBal->Zone(thisDayltgCtrl.zoneIndex).Name,
     615           0 :                               state.dataSurface->Surface(windowSurfNum).Name);
     616             :                     } else {
     617             :                         // blind with variable slat angle
     618           0 :                         Real64 SlatAngle = 180.0 / double(Material::MaxSlatAngs - 1) * double(ISlatAngle - 2);
     619           0 :                         print(state.files.dfs,
     620             :                               "{},{},{},{},{:.1R}\n",
     621           0 :                               state.dataEnvrn->CurMnDy,
     622           0 :                               state.dataViewFactor->EnclSolInfo(enclNum).Name,
     623           0 :                               state.dataHeatBal->Zone(thisDayltgCtrl.zoneIndex).Name,
     624           0 :                               state.dataSurface->Surface(windowSurfNum).Name,
     625             :                               SlatAngle);
     626             :                     }
     627             : 
     628         600 :                     for (int IHR = 1; IHR <= Constant::HoursInDay; ++IHR) {
     629             :                         // For each Daylight Reference Point
     630         576 :                         auto &daylFacHr = thisDayltgCtrl.daylFac[IHR];
     631        1152 :                         for (int refPtNum = 1; refPtNum <= thisDayltgCtrl.TotalDaylRefPoints; ++refPtNum) {
     632         576 :                             auto &illums = daylFacHr(windowCounter, refPtNum, ISlatAngle)[iLum_Illum];
     633             : 
     634             :                             // write daylight factors - 4 sky types for each daylight ref point
     635         576 :                             print(state.files.dfs,
     636             :                                   "{},{},{:.5R},{:.5R},{:.5R},{:.5R}\n",
     637             :                                   IHR,
     638         576 :                                   dl->DaylRefPt(thisDayltgCtrl.refPts(refPtNum).num).Name,
     639         576 :                                   illums.sky[(int)SkyType::Clear],
     640         576 :                                   illums.sky[(int)SkyType::ClearTurbid],
     641         576 :                                   illums.sky[(int)SkyType::Intermediate],
     642         576 :                                   illums.sky[(int)SkyType::Overcast]);
     643             : 
     644             :                         } // for (refPtNum) Reference Point
     645             :                     }     // for (IHR) hour
     646             :                 }         // for (ISlatAngle) slat angle
     647             :             }             // if (SolarEnclIndex == enclNum)
     648             :         }                 // for (windowCounter) exterior windows in enclosure
     649             :     }                     // for (controlNum) daylighting control
     650             : } // CalcDayltgCoefficients()
     651             : 
     652         541 : void CalcDayltgCoeffsRefMapPoints(EnergyPlusData &state)
     653             : {
     654             : 
     655             :     // SUBROUTINE INFORMATION:
     656             :     //       AUTHOR         Linda Lawrie
     657             :     //       DATE WRITTEN   October 2004
     658             :     //       MODIFIED       May 2006 (RR): added exterior window screens
     659             :     //                      April 2012 (LKL); change to allow multiple maps per zone
     660             : 
     661             :     // PURPOSE OF THIS SUBROUTINE:
     662             :     // This subroutine does the daylighting coefficient calculation for the
     663             :     // daylighting and illuminance map reference points.
     664         541 :     auto &dl = state.dataDayltg;
     665             : 
     666         541 :     if (dl->VeryFirstTime) {
     667             :         // make sure all necessary surfaces match to pipes
     668          64 :         bool ErrorsFound = false;
     669         785 :         for (int enclNum = 1; enclNum <= state.dataViewFactor->NumOfSolarEnclosures; ++enclNum) {
     670        1566 :             for (int loopwin = 1; loopwin <= dl->enclDaylight(enclNum).NumOfDayltgExtWins; ++loopwin) {
     671         845 :                 int IWin = dl->enclDaylight(enclNum).DayltgExtWinSurfNums(loopwin);
     672         845 :                 if (state.dataSurface->Surface(IWin).OriginalClass != SurfaceClass::TDD_Diffuser) continue;
     673             :                 // Look up the TDD:DOME object
     674           2 :                 int PipeNum = state.dataSurface->SurfWinTDDPipeNum(IWin);
     675           2 :                 if (PipeNum == 0) {
     676           0 :                     ShowSevereError(state,
     677           0 :                                     format("GetTDDInput: Surface={}, TDD:Dome object does not reference a valid Diffuser object.",
     678           0 :                                            state.dataSurface->Surface(IWin).Name));
     679           0 :                     ShowContinueError(state, "...needs DaylightingDevice:Tubular of same name as Surface.");
     680           0 :                     ErrorsFound = true;
     681             :                 }
     682             :             }
     683             :         }
     684             : 
     685          64 :         if (ErrorsFound) {
     686           0 :             ShowFatalError(state, "Not all TubularDaylightDome objects have corresponding DaylightingDevice:Tubular objects. Program terminates.");
     687             :         }
     688          64 :         dl->VeryFirstTime = false;
     689             :     }
     690             : 
     691             :     // Calc for daylighting reference points for daylighting controls that use SplitFlux method
     692        3331 :     for (int daylightCtrlNum = 1; daylightCtrlNum <= (int)dl->daylightControl.size(); ++daylightCtrlNum) {
     693        2790 :         if (dl->daylightControl(daylightCtrlNum).DaylightMethod != DaylightingMethod::SplitFlux) continue;
     694             :         // Skip enclosures with no exterior windows or in adjacent enclosure(s) with which an interior window is shared
     695        2775 :         if (dl->enclDaylight(dl->daylightControl(daylightCtrlNum).enclIndex).NumOfDayltgExtWins == 0) continue;
     696        2775 :         CalcDayltgCoeffsRefPoints(state, daylightCtrlNum);
     697             :     }
     698         541 :     if (!state.dataGlobal->DoingSizing && !state.dataGlobal->KickOffSimulation) {
     699             :         // Calc for illuminance maps
     700         155 :         if ((int)dl->illumMaps.size() > 0) {
     701          50 :             for (int MapNum = 1; MapNum <= (int)dl->illumMaps.size(); ++MapNum) {
     702          28 :                 int mapZoneNum = dl->illumMaps(MapNum).zoneIndex;
     703          28 :                 std::string name = format("Zone={}", state.dataHeatBal->Zone(mapZoneNum).Name);
     704          28 :                 int mapSpaceNum = dl->illumMaps(MapNum).spaceIndex;
     705          28 :                 if (mapSpaceNum > 0) {
     706           2 :                     name = format("Space={}", state.dataHeatBal->space(mapSpaceNum).Name);
     707             :                 }
     708          28 :                 if (state.dataGlobal->WarmupFlag) {
     709          28 :                     DisplayString(state, format("Calculating Daylighting Coefficients (Map Points), {}", name));
     710             :                 } else {
     711           0 :                     DisplayString(state, format("Updating Daylighting Coefficients (Map Points), {}", name));
     712             :                 }
     713          28 :                 CalcDayltgCoeffsMapPoints(state, MapNum);
     714          28 :             }
     715             :         }
     716             :     }
     717         541 : } // CalcDayltgCoeffsRefMapPoints()
     718             : 
     719        2775 : void CalcDayltgCoeffsRefPoints(EnergyPlusData &state, int const daylightCtrlNum)
     720             : {
     721             : 
     722             :     // SUBROUTINE INFORMATION:
     723             :     //       AUTHOR         Linda Lawrie
     724             :     //       DATE WRITTEN   April 2012
     725             :     //       MODIFIED       November 2012 (B. Griffith), refactor for detailed timestep integration and remove duplicate code
     726             : 
     727             :     // PURPOSE OF THIS SUBROUTINE:
     728             :     // Provides calculations for Daylighting Coefficients for daylighting reference points
     729        2775 :     auto &dl = state.dataDayltg;
     730             : 
     731             :     //  glare calculation (radians)
     732             :     int IConst;            // Construction counter
     733             :     int ICtrl;             // Window control counter
     734             :     int IWin;              // Window counter
     735             :     int IWin2;             // Secondary window counter (for TDD:DOME object, if exists)
     736             :     int InShelfSurf;       // Inside daylighting shelf surface number
     737             :     WinShadingType ShType; // Window shading type
     738             :     int BlNum;             // Window Blind Number
     739             :     int LSHCAL;            // Interior shade calculation flag: 0=not yet
     740             :     //  calculated, 1=already calculated
     741             :     int NWX;     // Number of window elements in x direction for dayltg calc
     742             :     int NWY;     // Number of window elements in y direction for dayltg calc
     743             :     int NWYlim;  // For triangle, largest NWY for a given IX
     744             :     Real64 COSB; // Cosine of angle between window outward normal and ray from
     745             :     //  reference point to window element
     746             :     Real64 PHRAY;  // Altitude of ray from reference point to window element (radians)
     747             :     Real64 THRAY;  // Azimuth of ray from reference point to window element (radians)
     748             :     Real64 DOMEGA; // Solid angle subtended by window element wrt reference point (steradians)
     749             :     Real64 TVISB;  // Visible transmittance of window for COSB angle of incidence (times light well
     750             :     //   efficiency, if appropriate)
     751             :     int ISunPos; // Sun position counter; used to avoid calculating various
     752             :     //  quantities that do not depend on sun position.
     753             :     Real64 ObTrans; // Product of solar transmittances of exterior obstructions hit by ray
     754             :     // from reference point through a window element
     755             :     bool is_Rectangle;         // True if window is rectangular
     756             :     bool is_Triangle;          // True if window is triangular
     757             :     Real64 DWX;                // Horizontal dimension of window element (m)
     758             :     Real64 DWY;                // Vertical dimension of window element (m)
     759             :     Real64 DAXY;               // Area of window element
     760             :     Real64 SkyObstructionMult; // Ratio of obstructed to unobstructed sky diffuse at a ground point
     761             :     ExtWinType extWinType;     // Exterior window type (InZoneExtWin, AdjZoneExtWin, NotInOrAdjZoneExtWin)
     762             :     int BRef;
     763             :     int ILB;
     764             :     bool hitIntObs;        // True iff interior obstruction hit
     765             :     bool hitExtObs;        // True iff ray from ref pt to ext win hits an exterior obstruction
     766             :     Real64 TVISIntWin;     // Visible transmittance of int win at COSBIntWin for light from ext win
     767             :     Real64 TVISIntWinDisk; // Visible transmittance of int win at COSBIntWin for sun
     768             : 
     769        2775 :     Vector3<Real64> W2;
     770        2775 :     Vector3<Real64> W3;
     771        2775 :     Vector3<Real64> W21;
     772        2775 :     Vector3<Real64> W23;
     773        2775 :     Vector3<Real64> RREF2;
     774        2775 :     Vector3<Real64> RWIN;
     775        2775 :     Vector3<Real64> RWIN2;
     776        2775 :     Vector3<Real64> Ray;
     777        2775 :     Vector3<Real64> WNORM2;
     778        2775 :     Vector3<Real64> VIEWVC;
     779        2775 :     Vector3<Real64> U2;
     780        2775 :     Vector3<Real64> U21;
     781        2775 :     Vector3<Real64> U23;
     782        2775 :     Vector3<Real64> VIEWVC2;
     783             : 
     784             :     int WinEl; // Current window element
     785             : 
     786        2775 :     if (dl->refFirstTime && (dl->maxControlRefPoints > 0)) {
     787          64 :         dl->RefErrIndex.allocate(dl->maxControlRefPoints, state.dataSurface->TotSurfaces);
     788          64 :         dl->RefErrIndex = 0;
     789          64 :         dl->refFirstTime = false;
     790             :     }
     791             : 
     792        2775 :     auto &thisDayltgCtrl = dl->daylightControl(daylightCtrlNum);
     793        2775 :     auto &thisEnclDaylight = dl->enclDaylight(thisDayltgCtrl.enclIndex);
     794        2775 :     int zoneNum = thisDayltgCtrl.zoneIndex;
     795             :     // Azimuth of view vector in absolute coord sys
     796        2775 :     Real64 AZVIEW = (thisDayltgCtrl.ViewAzimuthForGlare + state.dataHeatBal->Zone(zoneNum).RelNorth + state.dataHeatBal->BuildingAzimuth +
     797        2775 :                      state.dataHeatBal->BuildingRotationAppendixG) *
     798        2775 :                     Constant::DegToRadians;
     799             :     // View vector components in absolute coord sys
     800        2775 :     VIEWVC = {std::sin(AZVIEW), std::cos(AZVIEW), 0.0};
     801             : 
     802        7395 :     for (auto &refPt : thisDayltgCtrl.refPts) {
     803        4620 :         refPt.lums[iLum_Illum] = 0.0; // Daylight illuminance at reference points (lux)
     804        4620 :         refPt.glareIndex = 0.0;       // Glare index at reference points
     805       19776 :         for (auto &extWin : refPt.extWins) {
     806       15156 :             extWin.solidAng = extWin.solidAngWtd = 0.0;
     807       15156 :             extWin.lums[iLum_Illum] = extWin.lums[iLum_Back] = extWin.lums[iLum_Source] = {0.0, 0.0};
     808             :         }
     809             :     }
     810             : 
     811        2775 :     int iHrBeg = state.dataSysVars->DetailedSolarTimestepIntegration ? state.dataGlobal->HourOfDay : 1;
     812        2775 :     int iHrEnd = state.dataSysVars->DetailedSolarTimestepIntegration ? state.dataGlobal->HourOfDay : Constant::HoursInDay;
     813        2775 :     int numExtWins = thisEnclDaylight.NumOfDayltgExtWins;
     814        2775 :     int numRefPts = thisDayltgCtrl.TotalDaylRefPoints;
     815        2775 :     int numSlatAngs = state.dataSurface->actualMaxSlatAngs + 1;
     816             : 
     817       69375 :     for (int iHr = iHrBeg; iHr <= iHrEnd; ++iHr) {
     818       66600 :         auto &daylFacHr = thisDayltgCtrl.daylFac[iHr];
     819      269616 :         for (int iWin = 1; iWin <= numExtWins; ++iWin) {
     820      566760 :             for (int iRefPt = 1; iRefPt <= numRefPts; ++iRefPt) {
     821     1091232 :                 for (int iSlatAng = 1; iSlatAng <= numSlatAngs; ++iSlatAng) {
     822      727488 :                     auto &daylFac = daylFacHr(iWin, iRefPt, iSlatAng);
     823     2909952 :                     daylFac[iLum_Illum] = Illums();
     824     2909952 :                     daylFac[iLum_Source] = Illums();
     825     2909952 :                     daylFac[iLum_Back] = Illums();
     826             :                 } // for (iSlatAng)
     827             :             }     // for (iRefPt)
     828             :         }         // for (iWin)
     829             :     }             // for (iHr)
     830             : 
     831        2775 :     BRef = 0;
     832             : 
     833        7395 :     for (int IL = 1; IL <= thisDayltgCtrl.TotalDaylRefPoints; ++IL) {
     834        4620 :         auto &refPt = thisDayltgCtrl.refPts(IL);
     835             :         // Reference point in absolute coordinate system
     836        4620 :         Vector3<Real64> RREF = refPt.absCoords;
     837             : 
     838             :         //           -------------
     839             :         // ---------- WINDOW LOOP ----------
     840             :         //           -------------
     841       19776 :         for (int loopwin = 1; loopwin <= thisEnclDaylight.NumOfDayltgExtWins; ++loopwin) {
     842             : 
     843       15156 :             FigureDayltgCoeffsAtPointsSetupForWindow(state,
     844             :                                                      daylightCtrlNum,
     845             :                                                      IL,
     846             :                                                      loopwin,
     847             :                                                      CalledFor::RefPoint,
     848             :                                                      RREF,
     849             :                                                      VIEWVC,
     850             :                                                      IWin,
     851             :                                                      IWin2,
     852             :                                                      NWX,
     853             :                                                      NWY,
     854             :                                                      W2,
     855             :                                                      W3,
     856             :                                                      W21,
     857             :                                                      W23,
     858             :                                                      LSHCAL,
     859             :                                                      InShelfSurf,
     860             :                                                      ICtrl,
     861             :                                                      ShType,
     862             :                                                      BlNum,
     863             :                                                      WNORM2,
     864             :                                                      extWinType,
     865             :                                                      IConst,
     866             :                                                      RREF2,
     867             :                                                      DWX,
     868             :                                                      DWY,
     869             :                                                      DAXY,
     870             :                                                      U2,
     871             :                                                      U23,
     872             :                                                      U21,
     873             :                                                      VIEWVC2,
     874             :                                                      is_Rectangle,
     875             :                                                      is_Triangle);
     876             :             //           ---------------------
     877             :             // ---------- WINDOW ELEMENT LOOP ----------
     878             :             //           ---------------------
     879             : 
     880       15156 :             WinEl = 0;
     881             : 
     882      206362 :             for (int IX = 1; IX <= NWX; ++IX) {
     883      191206 :                 if (is_Rectangle) {
     884      191206 :                     NWYlim = NWY;
     885           0 :                 } else if (is_Triangle) {
     886           0 :                     NWYlim = NWY - IX + 1;
     887             :                 }
     888             : 
     889      772700 :                 for (int IY = 1; IY <= NWYlim; ++IY) {
     890             : 
     891      581494 :                     ++WinEl;
     892             : 
     893      581494 :                     FigureDayltgCoeffsAtPointsForWindowElements(state,
     894             :                                                                 daylightCtrlNum,
     895             :                                                                 IL,
     896             :                                                                 loopwin,
     897             :                                                                 CalledFor::RefPoint,
     898             :                                                                 WinEl,
     899             :                                                                 IWin,
     900             :                                                                 IWin2,
     901             :                                                                 IX,
     902             :                                                                 IY,
     903             :                                                                 SkyObstructionMult,
     904             :                                                                 W2,
     905             :                                                                 W21,
     906             :                                                                 W23,
     907             :                                                                 RREF,
     908             :                                                                 NWYlim,
     909             :                                                                 VIEWVC2,
     910             :                                                                 DWX,
     911             :                                                                 DWY,
     912             :                                                                 DAXY,
     913             :                                                                 U2,
     914             :                                                                 U23,
     915             :                                                                 U21,
     916             :                                                                 RWIN,
     917             :                                                                 RWIN2,
     918             :                                                                 Ray,
     919             :                                                                 PHRAY,
     920             :                                                                 LSHCAL,
     921             :                                                                 COSB,
     922             :                                                                 ObTrans,
     923             :                                                                 TVISB,
     924             :                                                                 DOMEGA,
     925             :                                                                 THRAY,
     926             :                                                                 hitIntObs,
     927             :                                                                 hitExtObs,
     928             :                                                                 WNORM2,
     929             :                                                                 extWinType,
     930             :                                                                 IConst,
     931             :                                                                 RREF2,
     932             :                                                                 is_Triangle,
     933             :                                                                 TVISIntWin,
     934             :                                                                 TVISIntWinDisk);
     935             : 
     936             :                     //           -------------------
     937             :                     // ---------- SUN POSITION LOOP ----------
     938             :                     //           -------------------
     939             : 
     940             :                     // Sun position counter. Used to avoid calculating various quantities
     941             :                     // that do not depend on sun position.
     942             : 
     943      581494 :                     if (!state.dataSysVars->DetailedSolarTimestepIntegration) {
     944      581494 :                         ISunPos = 0;
     945    14537350 :                         for (int IHR = 1; IHR <= Constant::HoursInDay; ++IHR) {
     946             : 
     947    13955856 :                             FigureDayltgCoeffsAtPointsForSunPosition(state,
     948             :                                                                      daylightCtrlNum,
     949             :                                                                      IL,
     950             :                                                                      IX,
     951             :                                                                      NWX,
     952             :                                                                      IY,
     953             :                                                                      NWYlim,
     954             :                                                                      WinEl,
     955             :                                                                      IWin,
     956             :                                                                      IWin2,
     957             :                                                                      IHR,
     958             :                                                                      ISunPos,
     959             :                                                                      SkyObstructionMult,
     960             :                                                                      RWIN2,
     961             :                                                                      Ray,
     962             :                                                                      PHRAY,
     963             :                                                                      LSHCAL,
     964             :                                                                      InShelfSurf,
     965             :                                                                      COSB,
     966             :                                                                      ObTrans,
     967             :                                                                      TVISB,
     968             :                                                                      DOMEGA,
     969             :                                                                      ICtrl,
     970             :                                                                      ShType,
     971             :                                                                      BlNum,
     972             :                                                                      THRAY,
     973             :                                                                      WNORM2,
     974             :                                                                      extWinType,
     975             :                                                                      IConst,
     976             :                                                                      AZVIEW,
     977             :                                                                      RREF2,
     978             :                                                                      hitIntObs,
     979             :                                                                      hitExtObs,
     980             :                                                                      CalledFor::RefPoint,
     981             :                                                                      TVISIntWin,
     982             :                                                                      TVISIntWinDisk);
     983             : 
     984             :                         }    // End of hourly sun position loop, IHR
     985             :                     } else { // timestep integrated
     986           0 :                         if (state.dataEnvrn->SunIsUp && !dl->MySunIsUpFlag) {
     987           0 :                             ISunPos = 0;
     988           0 :                             dl->MySunIsUpFlag = true;
     989           0 :                         } else if (state.dataEnvrn->SunIsUp && dl->MySunIsUpFlag) {
     990           0 :                             ISunPos = 1;
     991           0 :                         } else if (!state.dataEnvrn->SunIsUp && dl->MySunIsUpFlag) {
     992           0 :                             dl->MySunIsUpFlag = false;
     993           0 :                             ISunPos = -1;
     994           0 :                         } else if (!state.dataEnvrn->SunIsUp && !dl->MySunIsUpFlag) {
     995           0 :                             ISunPos = -1;
     996             :                         }
     997             : 
     998           0 :                         FigureDayltgCoeffsAtPointsForSunPosition(state,
     999             :                                                                  daylightCtrlNum,
    1000             :                                                                  IL,
    1001             :                                                                  IX,
    1002             :                                                                  NWX,
    1003             :                                                                  IY,
    1004             :                                                                  NWYlim,
    1005             :                                                                  WinEl,
    1006             :                                                                  IWin,
    1007             :                                                                  IWin2,
    1008           0 :                                                                  state.dataGlobal->HourOfDay,
    1009             :                                                                  ISunPos,
    1010             :                                                                  SkyObstructionMult,
    1011             :                                                                  RWIN2,
    1012             :                                                                  Ray,
    1013             :                                                                  PHRAY,
    1014             :                                                                  LSHCAL,
    1015             :                                                                  InShelfSurf,
    1016             :                                                                  COSB,
    1017             :                                                                  ObTrans,
    1018             :                                                                  TVISB,
    1019             :                                                                  DOMEGA,
    1020             :                                                                  ICtrl,
    1021             :                                                                  ShType,
    1022             :                                                                  BlNum,
    1023             :                                                                  THRAY,
    1024             :                                                                  WNORM2,
    1025             :                                                                  extWinType,
    1026             :                                                                  IConst,
    1027             :                                                                  AZVIEW,
    1028             :                                                                  RREF2,
    1029             :                                                                  hitIntObs,
    1030             :                                                                  hitExtObs,
    1031             :                                                                  CalledFor::RefPoint,
    1032             :                                                                  TVISIntWin,
    1033             :                                                                  TVISIntWinDisk);
    1034             :                     }
    1035             : 
    1036             :                 } // End of window Y-element loop, IY
    1037             :             }     // End of window X-element loop, IX
    1038             : 
    1039             :             // Loop again over hourly sun positions and calculate daylight factors by adding
    1040             :             // direct and inter-reflected illum components, then dividing by exterior horiz illum.
    1041             :             // Also calculate corresponding glare factors.
    1042             : 
    1043       15156 :             ILB = BRef + IL;
    1044             : 
    1045       15156 :             if (!state.dataSysVars->DetailedSolarTimestepIntegration) {
    1046       15156 :                 ISunPos = 0;
    1047      378900 :                 for (int IHR = 1; IHR <= Constant::HoursInDay; ++IHR) {
    1048      363744 :                     FigureRefPointDayltgFactorsToAddIllums(state, daylightCtrlNum, ILB, IHR, ISunPos, IWin, loopwin, NWX, NWY, ICtrl);
    1049             : 
    1050             :                 } // End of sun position loop, IHR
    1051             :             } else {
    1052           0 :                 if (state.dataEnvrn->SunIsUp && !dl->MySunIsUpFlag) {
    1053           0 :                     ISunPos = 0;
    1054           0 :                     dl->MySunIsUpFlag = true;
    1055           0 :                 } else if (state.dataEnvrn->SunIsUp && dl->MySunIsUpFlag) {
    1056           0 :                     ISunPos = 1;
    1057           0 :                 } else if (!state.dataEnvrn->SunIsUp && dl->MySunIsUpFlag) {
    1058           0 :                     dl->MySunIsUpFlag = false;
    1059           0 :                     ISunPos = -1;
    1060           0 :                 } else if (!state.dataEnvrn->SunIsUp && !dl->MySunIsUpFlag) {
    1061           0 :                     ISunPos = -1;
    1062             :                 }
    1063           0 :                 FigureRefPointDayltgFactorsToAddIllums(
    1064           0 :                     state, daylightCtrlNum, ILB, state.dataGlobal->HourOfDay, ISunPos, IWin, loopwin, NWX, NWY, ICtrl);
    1065             :             }
    1066             :         } // End of window loop, loopwin - IWin
    1067             : 
    1068        4620 :     } // End of reference point loop, IL
    1069        2775 : }
    1070             : 
    1071          28 : void CalcDayltgCoeffsMapPoints(EnergyPlusData &state, int const mapNum)
    1072             : {
    1073             : 
    1074             :     // SUBROUTINE INFORMATION:
    1075             :     //       AUTHOR         Linda Lawrie
    1076             :     //       DATE WRITTEN   April 2012
    1077             :     //       MODIFIED      November 2012 (B. Griffith), refactor for detailed timestep integration and remove duplicate code
    1078             : 
    1079             :     // PURPOSE OF THIS SUBROUTINE:
    1080             :     // Provides calculations for Daylighting Coefficients for map illuminance points
    1081             : 
    1082             :     // METHODOLOGY EMPLOYED:
    1083             :     // Was previously part of CalcDayltgCoeffsRefMapPoints -- broken out to all multiple
    1084             :     // maps per zone
    1085          28 :     auto &dl = state.dataDayltg;
    1086             : 
    1087             :     //  In the following four variables, I=1 for clear sky, 2 for overcast.
    1088             :     int numRefPts; // Number of daylighting reference points in a zone
    1089             :     int IL;        // Reference point counter
    1090             :     //  glare calculation (radians)
    1091             :     int IConst;            // Construction counter
    1092             :     int ICtrl;             // Window control counter
    1093             :     int IWin;              // Window counter
    1094             :     int IWin2;             // Secondary window counter (for TDD:DOME object, if exists)
    1095             :     int InShelfSurf;       // Inside daylighting shelf surface number
    1096             :     WinShadingType ShType; // Window shading type
    1097             :     int BlNum;             // Window Blind Number
    1098             :     int LSHCAL;            // Interior shade calculation flag: 0=not yet
    1099             :     //  calculated, 1=already calculated
    1100             :     int NWX;     // Number of window elements in x direction for dayltg calc
    1101             :     int NWY;     // Number of window elements in y direction for dayltg calc
    1102             :     int NWYlim;  // For triangle, largest NWY for a given IX
    1103             :     Real64 DWX;  // Horizontal dimension of window element (m)
    1104             :     Real64 DWY;  // Vertical dimension of window element (m)
    1105             :     Real64 COSB; // Cosine of angle between window outward normal and ray from
    1106             :     //  reference point to window element
    1107             :     Real64 PHRAY;  // Altitude of ray from reference point to window element (radians)
    1108             :     Real64 THRAY;  // Azimuth of ray from reference point to window element (radians)
    1109             :     Real64 DOMEGA; // Solid angle subtended by window element wrt reference point (steradians)
    1110             :     Real64 TVISB;  // Visible transmittance of window for COSB angle of incidence (times light well
    1111             :     //   efficiency, if appropriate)
    1112             :     int ISunPos; // Sun position counter; used to avoid calculating various
    1113             :     //  quantities that do not depend on sun position.
    1114             :     Real64 ObTrans; // Product of solar transmittances of exterior obstructions hit by ray
    1115             :     // from reference point through a window element
    1116             :     bool is_Rectangle;         // True if window is rectangular
    1117             :     bool is_Triangle;          // True if window is triangular
    1118             :     Real64 DAXY;               // Area of window element
    1119             :     Real64 SkyObstructionMult; // Ratio of obstructed to unobstructed sky diffuse at a ground point
    1120             :     ExtWinType extWinType;     // Exterior window type (InZoneExtWin, AdjZoneExtWin, NotInOrAdjZoneExtWin)
    1121             :     int ILB;
    1122             :     bool hitIntObs;        // True iff interior obstruction hit
    1123             :     bool hitExtObs;        // True iff ray from ref pt to ext win hits an exterior obstruction
    1124             :     Real64 TVISIntWin;     // Visible transmittance of int win at COSBIntWin for light from ext win
    1125             :     Real64 TVISIntWinDisk; // Visible transmittance of int win at COSBIntWin for sun
    1126             :     int WinEl;             // window elements counter
    1127             : 
    1128          28 :     Vector3<Real64> W2;
    1129          28 :     Vector3<Real64> W3;
    1130          28 :     Vector3<Real64> W21;
    1131          28 :     Vector3<Real64> W23;
    1132          28 :     Vector3<Real64> RREF2;
    1133          28 :     Vector3<Real64> RWIN;
    1134          28 :     Vector3<Real64> RWIN2;
    1135          28 :     Vector3<Real64> Ray;
    1136          28 :     Vector3<Real64> WNORM2;
    1137          28 :     Vector3<Real64> VIEWVC;
    1138          28 :     Vector3<Real64> U2;
    1139          28 :     Vector3<Real64> U21;
    1140          28 :     Vector3<Real64> U23;
    1141          28 :     Vector3<Real64> VIEWVC2;
    1142             : 
    1143          28 :     if (dl->mapFirstTime && (int)dl->illumMaps.size() > 0) {
    1144           6 :         IL = -999;
    1145          15 :         for (int MapNum = 1; MapNum <= (int)dl->illumMaps.size(); ++MapNum) {
    1146           9 :             IL = max(IL, dl->illumMaps(MapNum).TotalMapRefPoints);
    1147             :         }
    1148           6 :         dl->MapErrIndex.dimension(IL, state.dataSurface->TotSurfaces, 0);
    1149           6 :         dl->mapFirstTime = false;
    1150             :     }
    1151             : 
    1152          28 :     auto &illumMap = dl->illumMaps(mapNum);
    1153          28 :     int enclNum = illumMap.enclIndex;
    1154          28 :     auto &thisEnclDaylight = dl->enclDaylight(enclNum);
    1155             : 
    1156             :     // Azimuth of view vector in absolute coord sys - set to zero here, because glare isn't calculated for map points
    1157             :     // but these are arguments to some of the functions that are shared with regular reference points, so initalize here.
    1158          28 :     Real64 AZVIEW = 0.0;
    1159             :     // View vector components in absolute coord sys
    1160          28 :     VIEWVC = {0.0, 0.0, 0.0};
    1161             : 
    1162          28 :     numRefPts = illumMap.TotalMapRefPoints;
    1163          28 :     int numSlatAngs = state.dataSurface->actualMaxSlatAngs + 1;
    1164          28 :     int numExtWins = thisEnclDaylight.NumOfDayltgExtWins;
    1165             : 
    1166        2278 :     for (auto &refPt : illumMap.refPts) {
    1167        2250 :         refPt.lums[iLum_Illum] = 0.0; // Daylight illuminance at reference points (lux)
    1168       15950 :         for (int iExtWin = 1; iExtWin <= numExtWins; ++iExtWin) {
    1169       13700 :             refPt.winLums(iExtWin) = {0.0, 0.0};
    1170             :         }
    1171             :     }
    1172             : 
    1173          28 :     int iHrBeg = state.dataSysVars->DetailedSolarTimestepIntegration ? state.dataGlobal->HourOfDay : 1;
    1174          28 :     int iHrEnd = state.dataSysVars->DetailedSolarTimestepIntegration ? state.dataGlobal->HourOfDay : Constant::HoursInDay;
    1175             : 
    1176         700 :     for (int iHr = iHrBeg; iHr <= iHrEnd; ++iHr) {
    1177         672 :         auto &daylFacHr = illumMap.daylFac[iHr];
    1178        4224 :         for (int iWin = 1; iWin <= numExtWins; ++iWin) {
    1179      332352 :             for (int iRefPt = 1; iRefPt <= numRefPts; ++iRefPt) {
    1180      986400 :                 for (int iSlatAng = 1; iSlatAng <= numSlatAngs; ++iSlatAng) {
    1181     2630400 :                     daylFacHr(iWin, iRefPt, iSlatAng) = Illums();
    1182             :                 }
    1183             :             }
    1184             :         }
    1185             :     }
    1186             : 
    1187        2278 :     for (int IL = 1; IL <= numRefPts; ++IL) {
    1188        2250 :         auto &refPt = illumMap.refPts(IL);
    1189        2250 :         Vector3<Real64> RREF = refPt.absCoords;
    1190             : 
    1191             :         //           -------------
    1192             :         // ---------- WINDOW LOOP ----------
    1193             :         //           -------------
    1194             : 
    1195       15950 :         for (int loopwin = 1; loopwin <= numExtWins; ++loopwin) {
    1196             : 
    1197             :             // daylightingCtrlNum parameter is unused for map points
    1198       13700 :             FigureDayltgCoeffsAtPointsSetupForWindow(state,
    1199             :                                                      0,
    1200             :                                                      IL,
    1201             :                                                      loopwin,
    1202             :                                                      CalledFor::MapPoint,
    1203             :                                                      RREF,
    1204             :                                                      VIEWVC,
    1205             :                                                      IWin,
    1206             :                                                      IWin2,
    1207             :                                                      NWX,
    1208             :                                                      NWY,
    1209             :                                                      W2,
    1210             :                                                      W3,
    1211             :                                                      W21,
    1212             :                                                      W23,
    1213             :                                                      LSHCAL,
    1214             :                                                      InShelfSurf,
    1215             :                                                      ICtrl,
    1216             :                                                      ShType,
    1217             :                                                      BlNum,
    1218             :                                                      WNORM2,
    1219             :                                                      extWinType,
    1220             :                                                      IConst,
    1221             :                                                      RREF2,
    1222             :                                                      DWX,
    1223             :                                                      DWY,
    1224             :                                                      DAXY,
    1225             :                                                      U2,
    1226             :                                                      U23,
    1227             :                                                      U21,
    1228             :                                                      VIEWVC2,
    1229             :                                                      is_Rectangle,
    1230             :                                                      is_Triangle,
    1231             :                                                      mapNum);
    1232             :             //           ---------------------
    1233             :             // ---------- WINDOW ELEMENT LOOP ----------
    1234             :             //           ---------------------
    1235       13700 :             WinEl = 0;
    1236             : 
    1237       90726 :             for (int IX = 1; IX <= NWX; ++IX) {
    1238       77026 :                 if (is_Rectangle) {
    1239       77026 :                     NWYlim = NWY;
    1240           0 :                 } else if (is_Triangle) {
    1241           0 :                     NWYlim = NWY - IX + 1;
    1242             :                 }
    1243             : 
    1244     2309108 :                 for (int IY = 1; IY <= NWYlim; ++IY) {
    1245             : 
    1246     2232082 :                     ++WinEl;
    1247             : 
    1248             :                     // daylightingCtrlNum parameter is unused for map points
    1249     2232082 :                     FigureDayltgCoeffsAtPointsForWindowElements(state,
    1250             :                                                                 0,
    1251             :                                                                 IL,
    1252             :                                                                 loopwin,
    1253             :                                                                 CalledFor::MapPoint,
    1254             :                                                                 WinEl,
    1255             :                                                                 IWin,
    1256             :                                                                 IWin2,
    1257             :                                                                 IX,
    1258             :                                                                 IY,
    1259             :                                                                 SkyObstructionMult,
    1260             :                                                                 W2,
    1261             :                                                                 W21,
    1262             :                                                                 W23,
    1263             :                                                                 RREF,
    1264             :                                                                 NWYlim,
    1265             :                                                                 VIEWVC2,
    1266             :                                                                 DWX,
    1267             :                                                                 DWY,
    1268             :                                                                 DAXY,
    1269             :                                                                 U2,
    1270             :                                                                 U23,
    1271             :                                                                 U21,
    1272             :                                                                 RWIN,
    1273             :                                                                 RWIN2,
    1274             :                                                                 Ray,
    1275             :                                                                 PHRAY,
    1276             :                                                                 LSHCAL,
    1277             :                                                                 COSB,
    1278             :                                                                 ObTrans,
    1279             :                                                                 TVISB,
    1280             :                                                                 DOMEGA,
    1281             :                                                                 THRAY,
    1282             :                                                                 hitIntObs,
    1283             :                                                                 hitExtObs,
    1284             :                                                                 WNORM2,
    1285             :                                                                 extWinType,
    1286             :                                                                 IConst,
    1287             :                                                                 RREF2,
    1288             :                                                                 is_Triangle,
    1289             :                                                                 TVISIntWin,
    1290             :                                                                 TVISIntWinDisk,
    1291             :                                                                 mapNum);
    1292             :                     //           -------------------
    1293             :                     // ---------- SUN POSITION LOOP ----------
    1294             :                     //           -------------------
    1295             : 
    1296             :                     // Sun position counter. Used to avoid calculating various quantities
    1297             :                     // that do not depend on sun position.
    1298     2232082 :                     if (!state.dataSysVars->DetailedSolarTimestepIntegration) {
    1299     2232082 :                         ISunPos = 0;
    1300    55802050 :                         for (int IHR = 1; IHR <= Constant::HoursInDay; ++IHR) {
    1301             :                             // daylightingCtrlNum parameter is unused for map points
    1302    53569968 :                             FigureDayltgCoeffsAtPointsForSunPosition(state,
    1303             :                                                                      0,
    1304             :                                                                      IL,
    1305             :                                                                      IX,
    1306             :                                                                      NWX,
    1307             :                                                                      IY,
    1308             :                                                                      NWYlim,
    1309             :                                                                      WinEl,
    1310             :                                                                      IWin,
    1311             :                                                                      IWin2,
    1312             :                                                                      IHR,
    1313             :                                                                      ISunPos,
    1314             :                                                                      SkyObstructionMult,
    1315             :                                                                      RWIN2,
    1316             :                                                                      Ray,
    1317             :                                                                      PHRAY,
    1318             :                                                                      LSHCAL,
    1319             :                                                                      InShelfSurf,
    1320             :                                                                      COSB,
    1321             :                                                                      ObTrans,
    1322             :                                                                      TVISB,
    1323             :                                                                      DOMEGA,
    1324             :                                                                      ICtrl,
    1325             :                                                                      ShType,
    1326             :                                                                      BlNum,
    1327             :                                                                      THRAY,
    1328             :                                                                      WNORM2,
    1329             :                                                                      extWinType,
    1330             :                                                                      IConst,
    1331             :                                                                      AZVIEW,
    1332             :                                                                      RREF2,
    1333             :                                                                      hitIntObs,
    1334             :                                                                      hitExtObs,
    1335             :                                                                      CalledFor::MapPoint,
    1336             :                                                                      TVISIntWin,
    1337             :                                                                      TVISIntWinDisk,
    1338             :                                                                      mapNum);
    1339             :                         } // End of hourly sun position loop, IHR
    1340             :                     } else {
    1341           0 :                         if (state.dataEnvrn->SunIsUp && !dl->CalcDayltgCoeffsMapPointsMySunIsUpFlag) {
    1342           0 :                             ISunPos = 0;
    1343           0 :                             dl->CalcDayltgCoeffsMapPointsMySunIsUpFlag = true;
    1344           0 :                         } else if (state.dataEnvrn->SunIsUp && dl->CalcDayltgCoeffsMapPointsMySunIsUpFlag) {
    1345           0 :                             ISunPos = 1;
    1346           0 :                         } else if (!state.dataEnvrn->SunIsUp && dl->CalcDayltgCoeffsMapPointsMySunIsUpFlag) {
    1347           0 :                             dl->CalcDayltgCoeffsMapPointsMySunIsUpFlag = false;
    1348           0 :                             ISunPos = -1;
    1349           0 :                         } else if (!state.dataEnvrn->SunIsUp && !dl->CalcDayltgCoeffsMapPointsMySunIsUpFlag) {
    1350           0 :                             ISunPos = -1;
    1351             :                         }
    1352             :                         // daylightingCtrlNum parameter is unused for map points
    1353           0 :                         FigureDayltgCoeffsAtPointsForSunPosition(state,
    1354             :                                                                  0,
    1355             :                                                                  IL,
    1356             :                                                                  IX,
    1357             :                                                                  NWX,
    1358             :                                                                  IY,
    1359             :                                                                  NWYlim,
    1360             :                                                                  WinEl,
    1361             :                                                                  IWin,
    1362             :                                                                  IWin2,
    1363           0 :                                                                  state.dataGlobal->HourOfDay,
    1364             :                                                                  ISunPos,
    1365             :                                                                  SkyObstructionMult,
    1366             :                                                                  RWIN2,
    1367             :                                                                  Ray,
    1368             :                                                                  PHRAY,
    1369             :                                                                  LSHCAL,
    1370             :                                                                  InShelfSurf,
    1371             :                                                                  COSB,
    1372             :                                                                  ObTrans,
    1373             :                                                                  TVISB,
    1374             :                                                                  DOMEGA,
    1375             :                                                                  ICtrl,
    1376             :                                                                  ShType,
    1377             :                                                                  BlNum,
    1378             :                                                                  THRAY,
    1379             :                                                                  WNORM2,
    1380             :                                                                  extWinType,
    1381             :                                                                  IConst,
    1382             :                                                                  AZVIEW,
    1383             :                                                                  RREF2,
    1384             :                                                                  hitIntObs,
    1385             :                                                                  hitExtObs,
    1386             :                                                                  CalledFor::MapPoint,
    1387             :                                                                  TVISIntWin,
    1388             :                                                                  TVISIntWinDisk,
    1389             :                                                                  mapNum);
    1390             :                     }
    1391             :                 } // End of window Y-element loop, IY
    1392             :             }     // End of window X-element loop, IX
    1393             : 
    1394       13700 :             if (!state.dataSysVars->DetailedSolarTimestepIntegration) {
    1395             :                 // Loop again over hourly sun positions and calculate daylight factors by adding
    1396             :                 // direct and inter-reflected illum components, then dividing by exterior horiz illum.
    1397             :                 // Also calculate corresponding glare factors.
    1398       13700 :                 ILB = IL;
    1399      342500 :                 for (int IHR = 1; IHR <= Constant::HoursInDay; ++IHR) {
    1400      328800 :                     FigureMapPointDayltgFactorsToAddIllums(state, mapNum, ILB, IHR, IWin, loopwin, ICtrl);
    1401             :                 } // End of sun position loop, IHR
    1402             :             } else {
    1403           0 :                 ILB = IL;
    1404           0 :                 FigureMapPointDayltgFactorsToAddIllums(state, mapNum, ILB, state.dataGlobal->HourOfDay, IWin, loopwin, ICtrl);
    1405             :             }
    1406             : 
    1407             :         } // End of window loop, loopwin - IWin
    1408             : 
    1409        2250 :     } // End of reference point loop, IL
    1410          28 : }
    1411             : 
    1412       28856 : void FigureDayltgCoeffsAtPointsSetupForWindow(EnergyPlusData &state,
    1413             :                                               int const daylightCtrlNum, // zero if called for map points
    1414             :                                               int const iRefPoint,
    1415             :                                               int const loopwin,
    1416             :                                               CalledFor const CalledFrom,    // indicate  which type of routine called this routine
    1417             :                                               Vector3<Real64> const &RREF,   // Location of a reference point in absolute coordinate system
    1418             :                                               Vector3<Real64> const &VIEWVC, // View vector in absolute coordinate system
    1419             :                                               int &IWin,
    1420             :                                               int &IWin2,
    1421             :                                               int &NWX,
    1422             :                                               int &NWY,
    1423             :                                               Vector3<Real64> &W2,     // Second vertex of window
    1424             :                                               Vector3<Real64> &W3,     // Third vertex of window
    1425             :                                               Vector3<Real64> &W21,    // Vector from window vertex 2 to window vertex 1
    1426             :                                               Vector3<Real64> &W23,    // Vector from window vertex 2 to window vertex 3
    1427             :                                               int &LSHCAL,             // Interior shade calculation flag:  0=not yet calculated, 1=already calculated
    1428             :                                               int &InShelfSurf,        // Inside daylighting shelf surface number
    1429             :                                               int &ICtrl,              // Window control counter
    1430             :                                               WinShadingType &ShType,  // Window shading type
    1431             :                                               int &BlNum,              // Window blind number
    1432             :                                               Vector3<Real64> &WNORM2, // Unit vector normal to window
    1433             :                                               ExtWinType &extWinType,  // Exterior window type (InZoneExtWin, AdjZoneExtWin, NotInOrAdjZoneExtWin)
    1434             :                                               int &IConst,             // Construction counter
    1435             :                                               Vector3<Real64> &RREF2,  // Location of virtual reference point in absolute coordinate system
    1436             :                                               Real64 &DWX,             // Horizontal dimension of window element (m)
    1437             :                                               Real64 &DWY,             // Vertical dimension of window element (m)
    1438             :                                               Real64 &DAXY,            // Area of window element
    1439             :                                               Vector3<Real64> &U2,     // Second vertex of window for TDD:DOME (if exists)
    1440             :                                               Vector3<Real64> &U23,    // Vector from window vertex 2 to window vertex 3 for TDD:DOME (if exists)
    1441             :                                               Vector3<Real64> &U21,    // Vector from window vertex 2 to window vertex 1 for TDD:DOME (if exists)
    1442             :                                               Vector3<Real64> &VIEWVC2, // Virtual view vector in absolute coordinate system
    1443             :                                               bool &is_Rectangle,       // True if window is rectangular
    1444             :                                               bool &is_Triangle,        // True if window is triangular
    1445             :                                               int const MapNum)
    1446             : {
    1447             :     // SUBROUTINE INFORMATION:
    1448             :     //       AUTHOR         B. Griffith
    1449             :     //       DATE WRITTEN   November 2012, refactor from legacy code by Fred Winklemann
    1450             : 
    1451             :     // PURPOSE OF THIS SUBROUTINE:
    1452             :     // collect code to setup calculations for each window for daylighting coefficients
    1453             : 
    1454             :     // METHODOLOGY EMPLOYED:
    1455             :     // switch as need to serve both reference points and map points based on calledFrom
    1456       28856 :     auto &dl = state.dataDayltg;
    1457             : 
    1458             :     int ShelfNum; // Daylighting shelf object number
    1459             :     int NDIVX;    // Number of window x divisions for daylighting calc
    1460             :     int NDIVY;    // Number of window y divisions for daylighting calc
    1461             :     Real64 ALF;   // Distance from reference point to window plane (m)
    1462             :     Real64 D1a;   // Projection of vector from window origin to reference
    1463             :     //  on window X  axis (m)
    1464             :     Real64 D1b; // Projection of vector from window origin to reference
    1465             :     //  on window Y axis (m)
    1466             :     Real64 SolidAngExtWin;    // Approx. solid angle subtended by an ext. window wrt ref pt
    1467             :     Real64 SolidAngMinIntWin; // Approx. smallest solid angle subtended by an int. window wrt ref pt
    1468             :     Real64 SolidAngRatio;     // Ratio of SolidAngExtWin and SolidAngMinIntWin
    1469             :     Real64 SinCornerAng;      // For triangle, sine of corner angle of window element
    1470             : 
    1471       28856 :     int zoneNum = 0; // zone number
    1472       28856 :     int enclNum = 0; // enclosure number
    1473             : 
    1474       28856 :     Vector3<Real64> W1 = {0.0, 0.0, 0.0};
    1475       28856 :     Vector3<Real64> WC = {0.0, 0.0, 0.0};
    1476             : 
    1477       28856 :     if (CalledFrom == CalledFor::RefPoint) {
    1478       15156 :         auto &daylCtrl = dl->daylightControl(daylightCtrlNum);
    1479       15156 :         daylCtrl.refPts(iRefPoint).extWins(loopwin).solidAng = 0.0;
    1480       15156 :         daylCtrl.refPts(iRefPoint).extWins(loopwin).solidAngWtd = 0.0;
    1481       15156 :         zoneNum = daylCtrl.zoneIndex;
    1482       15156 :         enclNum = daylCtrl.enclIndex;
    1483       13700 :     } else if (CalledFrom == CalledFor::MapPoint) {
    1484       13700 :         assert(MapNum > 0);
    1485       13700 :         auto const &illumMap = dl->illumMaps(MapNum);
    1486       13700 :         zoneNum = illumMap.zoneIndex;
    1487       13700 :         enclNum = illumMap.enclIndex;
    1488             :     }
    1489       28856 :     IWin = dl->enclDaylight(enclNum).DayltgExtWinSurfNums(loopwin);
    1490             : 
    1491       28856 :     auto &surf = state.dataSurface->Surface(IWin);
    1492       28856 :     auto &surfWin = state.dataSurface->SurfaceWindow(IWin);
    1493             : 
    1494       28856 :     if (state.dataSurface->Surface(surf.BaseSurf).SolarEnclIndex == enclNum) {
    1495       28652 :         extWinType = ExtWinType::InZone;
    1496             :     } else {
    1497         204 :         extWinType = ExtWinType::AdjZone;
    1498             :     }
    1499             : 
    1500       28856 :     IConst = state.dataSurface->SurfActiveConstruction(IWin);
    1501             : 
    1502             :     // For thermochromic windows, the daylight and glare factors are calculated for a base window cosntruction
    1503             :     //  at base TC layer temperature. During each time step calculations at DayltgInteriorIllum,
    1504             :     //  DayltgInteriorMapIllum, and DayltgGlare, the daylight and glare factors are adjusted by the visible
    1505             :     //  transmittance ratio = VT of actual TC window based on last hour TC layer temperature / VT of the base TC window
    1506       28856 :     if (state.dataConstruction->Construct(IConst).TCFlag == 1) {
    1507             :         // For thermochromic windows, use the base window construction at base temperature of the TC layer
    1508           0 :         IConst = state.dataConstruction->Construct(IConst).TCMasterConst;
    1509             :     }
    1510             : 
    1511       28856 :     ICtrl = surf.activeWindowShadingControl;
    1512       28856 :     ShType = WinShadingType::NoShade; // 'NOSHADE'
    1513       28856 :     BlNum = 0;
    1514             :     // ScNum = 0; //Unused Set but never used
    1515       28856 :     if (surf.HasShadeControl) ShType = state.dataSurface->WindowShadingControl(ICtrl).ShadingType;
    1516       28856 :     BlNum = state.dataSurface->SurfWinBlindNumber(IWin);
    1517             :     // ScNum = SurfaceWindow( IWin ).ScreenNumber; //Unused Set but never used
    1518             : 
    1519       28856 :     ShelfNum = state.dataSurface->SurfDaylightingShelfInd(IWin);
    1520       28856 :     if (ShelfNum > 0) {
    1521         210 :         InShelfSurf = state.dataDaylightingDevicesData->Shelf(state.dataSurface->SurfDaylightingShelfInd(IWin))
    1522         210 :                           .InSurf; // Inside daylighting shelf present if > 0
    1523             :     } else {
    1524       28646 :         InShelfSurf = 0;
    1525             :     }
    1526             : 
    1527       28856 :     is_Rectangle = false;
    1528       28856 :     is_Triangle = false;
    1529       28856 :     if (surf.Sides == 3) is_Triangle = true;
    1530       28856 :     if (surf.Sides == 4) is_Rectangle = true;
    1531             : 
    1532       28856 :     if (is_Rectangle) {
    1533             :         // Vertices of window (numbered counter-clockwise starting at upper left as viewed
    1534             :         // from inside of room). Assumes original vertices are numbered counter-clockwise from
    1535             :         // upper left as viewed from outside.
    1536       28856 :         W3 = surf.Vertex(2);
    1537       28856 :         W2 = surf.Vertex(3);
    1538       28856 :         W1 = surf.Vertex(4);
    1539           0 :     } else if (is_Triangle) {
    1540           0 :         W3 = surf.Vertex(2);
    1541           0 :         W2 = surf.Vertex(3);
    1542           0 :         W1 = surf.Vertex(1);
    1543             :     }
    1544             : 
    1545             :     // Shade/blind calculation flag
    1546       28856 :     LSHCAL = 0;
    1547             : 
    1548             :     // Visible transmittance at normal incidence
    1549       28856 :     state.dataSurface->SurfWinVisTransSelected(IWin) =
    1550       28856 :         General::POLYF(1.0, state.dataConstruction->Construct(IConst).TransVisBeamCoef) * surfWin.glazedFrac;
    1551             :     // For windows with switchable glazing, ratio of visible transmittance at normal
    1552             :     // incidence for fully switched (dark) state to that of unswitched state
    1553       28856 :     state.dataSurface->SurfWinVisTransRatio(IWin) = 1.0;
    1554       28856 :     if (ICtrl > 0) {
    1555       12662 :         if (ShType == WinShadingType::SwitchableGlazing) {
    1556       12562 :             int IConstShaded = surf.activeShadedConstruction; // Shaded construction counter
    1557       12562 :             state.dataSurface->SurfWinVisTransRatio(IWin) =
    1558       12562 :                 General::SafeDivide(General::POLYF(1.0, state.dataConstruction->Construct(IConstShaded).TransVisBeamCoef),
    1559       12562 :                                     General::POLYF(1.0, state.dataConstruction->Construct(IConst).TransVisBeamCoef));
    1560             :         }
    1561             :     }
    1562             : 
    1563             :     // Unit vectors from window vertex 2 to 1 and 2 to 3,
    1564             :     // center point of window, and vector from ref pt to center of window
    1565       28856 :     W21 = W1 - W2;
    1566       28856 :     W23 = W3 - W2;
    1567       28856 :     Real64 HW = W21.magnitude();
    1568       28856 :     Real64 WW = W23.magnitude();
    1569       28856 :     if (is_Rectangle) {
    1570       28856 :         WC = W2 + (W23 + W21) / 2.0;
    1571           0 :     } else if (is_Triangle) {
    1572           0 :         WC = W2 + (W23 + W21) / 3.0;
    1573             :     }
    1574       28856 :     state.dataSurface->SurfaceWindow(IWin).WinCenter = WC;
    1575       28856 :     Vector3<Real64> REFWC = WC - RREF;
    1576             :     // Unit vectors
    1577       28856 :     W21 /= HW;
    1578       28856 :     W23 /= WW;
    1579             : 
    1580             :     // Unit vector normal to window (pointing away from room)
    1581       28856 :     Vector3<Real64> WNORM = surf.lcsz;
    1582             : 
    1583             :     // Initialize number of window elements
    1584       28856 :     NDIVX = 40;
    1585       28856 :     NDIVY = 40;
    1586             : 
    1587             :     // Distance from ref point to window plane
    1588       28856 :     ALF = std::abs(dot(WNORM, REFWC));
    1589       28856 :     if (CalledFrom == CalledFor::RefPoint) {
    1590             :         // Check if ref point to close to window due to input error (0.1524 m below is 0.5 ft)
    1591       15156 :         if (ALF < 0.1524 && extWinType == ExtWinType::InZone) {
    1592             :             // Ref pt is close to window plane. Get vector from window
    1593             :             // origin to projection of ref pt on window plane.
    1594           0 :             Vector3<Real64> W2REF = RREF + ALF * WNORM - W2;
    1595             : 
    1596           0 :             D1a = dot(W2REF, W23);
    1597           0 :             D1b = dot(W2REF, W21);
    1598             : 
    1599             :             //            ! Error message if ref pt is too close to window.
    1600           0 :             if (D1a > 0.0 && D1b > 0.0 && D1b <= HW && D1a <= WW) {
    1601           0 :                 ShowSevereError(
    1602             :                     state,
    1603           0 :                     format("CalcDaylightCoeffRefPoints: Daylighting calculation cannot be done for Daylighting:Controls={} because reference point "
    1604             :                            "#{} is less than 0.15m (6\") from window plane {}",
    1605           0 :                            dl->daylightControl(daylightCtrlNum).Name,
    1606             :                            iRefPoint,
    1607           0 :                            surf.Name));
    1608           0 :                 ShowContinueError(state, format("Distance=[{:.5R}]. This is too close; check position of reference point.", ALF));
    1609           0 :                 ShowFatalError(state, "Program terminates due to preceding condition.");
    1610             :             }
    1611       15156 :         } else if (ALF < 0.1524 && extWinType == ExtWinType::AdjZone) {
    1612           0 :             if (dl->RefErrIndex(iRefPoint, IWin) == 0) { // only show error message once
    1613           0 :                 ShowWarningError(state,
    1614           0 :                                  format("CalcDaylightCoeffRefPoints: For Daylghting:Controls=\"{}\" External Window=\"{}\"in Zone=\"{}\" reference "
    1615             :                                         "point is less than 0.15m (6\") from window plane ",
    1616           0 :                                         dl->daylightControl(daylightCtrlNum).Name,
    1617           0 :                                         surf.Name,
    1618           0 :                                         state.dataHeatBal->Zone(surf.Zone).Name));
    1619           0 :                 ShowContinueError(state,
    1620           0 :                                   format("Distance=[{:.1R} m] to ref point=[{:.1R},{:.1R},{:.1R}], Inaccuracy in Daylighting Calcs may result.",
    1621             :                                          ALF,
    1622           0 :                                          RREF.x,
    1623           0 :                                          RREF.y,
    1624           0 :                                          RREF.z));
    1625           0 :                 dl->RefErrIndex(iRefPoint, IWin) = 1;
    1626             :             }
    1627             :         }
    1628       13700 :     } else if (CalledFrom == CalledFor::MapPoint) {
    1629       13700 :         if (ALF < 0.1524 && extWinType == ExtWinType::AdjZone) {
    1630           0 :             if (dl->MapErrIndex(iRefPoint, IWin) == 0) { // only show error message once
    1631           0 :                 ShowWarningError(state,
    1632           0 :                                  format("CalcDaylightCoeffMapPoints: For Zone=\"{}\" External Window=\"{}\"in Zone=\"{}\" map point is less than "
    1633             :                                         "0.15m (6\") from window plane ",
    1634           0 :                                         state.dataHeatBal->Zone(zoneNum).Name,
    1635           0 :                                         surf.Name,
    1636           0 :                                         state.dataHeatBal->Zone(surf.Zone).Name));
    1637           0 :                 ShowContinueError(
    1638             :                     state,
    1639           0 :                     format("Distance=[{:.1R} m] map point=[{:.1R},{:.1R},{:.1R}], Inaccuracy in Map Calcs may result.", ALF, RREF.x, RREF.y, RREF.z));
    1640           0 :                 dl->MapErrIndex(iRefPoint, IWin) = 1;
    1641             :             }
    1642             :         }
    1643             :     }
    1644             :     // Number of window elements in X and Y for daylighting calculation
    1645       28856 :     if (ALF > 0.1524) {
    1646       27576 :         NDIVX = 1 + int(4.0 * WW / ALF);
    1647       27576 :         NDIVY = 1 + int(4.0 * HW / ALF);
    1648             :     }
    1649             : 
    1650       28856 :     if (extWinType == ExtWinType::AdjZone) {
    1651             :         // Adjust number of exterior window elements to give acceptable number of rays through
    1652             :         // interior windows in the zone (for accuracy of interior window daylighting calculation)
    1653         204 :         SolidAngExtWin = General::SafeDivide(((surf.Area + state.dataSurface->SurfWinDividerArea(IWin)) / surf.Multiplier), pow_2(ALF));
    1654         204 :         SolidAngMinIntWin = dl->enclDaylight(enclNum).MinIntWinSolidAng;
    1655         204 :         SolidAngRatio = max(1.0, SolidAngExtWin / SolidAngMinIntWin);
    1656         204 :         NDIVX *= std::sqrt(SolidAngRatio);
    1657         204 :         NDIVY *= std::sqrt(SolidAngRatio);
    1658             :     }
    1659             : 
    1660       28856 :     NWX = min(40, NDIVX);
    1661       28856 :     NWY = min(40, NDIVY);
    1662             : 
    1663             :     // Discretization of triangle is simpler if NWX = NWY
    1664       28856 :     if (is_Triangle) {
    1665           0 :         NWX = max(NWX, NWY);
    1666           0 :         NWY = NWX;
    1667             :     }
    1668             : 
    1669             :     // Edge lengths of window elements
    1670       28856 :     DWX = WW / NWX;
    1671       28856 :     DWY = HW / NWY;
    1672             : 
    1673             :     // Azimuth and altitude of window normal
    1674       28856 :     surfWin.phi = std::asin(WNORM.z);
    1675       28856 :     surfWin.theta = (std::abs(WNORM.x) > 1.0e-5 || std::abs(WNORM.y) > 1.0e-5) ? std::atan2(WNORM.y, WNORM.x) : 0.0;
    1676             : 
    1677             :     // Recalculation of values for TDD:DOME
    1678       28856 :     if (surf.OriginalClass == SurfaceClass::TDD_Diffuser) {
    1679             : 
    1680             :         // Look up the TDD:DOME object
    1681         420 :         int PipeNum = state.dataSurface->SurfWinTDDPipeNum(IWin);
    1682         420 :         IWin2 = state.dataDaylightingDevicesData->TDDPipe(PipeNum).Dome;
    1683             : 
    1684         420 :         auto &surf2 = state.dataSurface->Surface(IWin2);
    1685         420 :         auto &surfWin2 = state.dataSurface->SurfaceWindow(IWin2);
    1686             : 
    1687             :         // Calculate reference point coords relative to the diffuser coordinate system
    1688             :         // W21, W23, and WNORM are the unit vectors
    1689         420 :         Vector3<Real64> REFD = {dot(REFWC, W21), dot(REFWC, W23), dot(REFWC, WNORM)};
    1690             : 
    1691             :         // Calculate view vector coords relative to the diffuser coordinate system
    1692         420 :         Vector3<Real64> VIEWVD = {dot(VIEWVC, W21), dot(VIEWVC, W23), dot(VIEWVC, WNORM)};
    1693             : 
    1694         420 :         Vector3<Real64> U3 = surf2.Vertex(2);
    1695         420 :         U2 = surf2.Vertex(3);
    1696         420 :         Vector3<Real64> U1;
    1697             : 
    1698         420 :         if (surf2.Sides == 4) {
    1699             :             // Vertices of window (numbered counter-clockwise starting
    1700             :             // at upper left as viewed from inside of room)
    1701             :             // Assumes original vertices are numbered counter-clockwise from
    1702             :             // upper left as viewed from outside.
    1703         420 :             U3 = surf2.Vertex(2);
    1704         420 :             U2 = surf2.Vertex(3);
    1705         420 :             U1 = surf2.Vertex(4);
    1706           0 :         } else if (surf2.Sides == 3) {
    1707           0 :             U3 = surf2.Vertex(2);
    1708           0 :             U2 = surf2.Vertex(3);
    1709           0 :             U1 = surf2.Vertex(1);
    1710             :         }
    1711             : 
    1712             :         // Unit vectors from window vertex 2 to 1 and 2 to 3,
    1713             :         // center point of window, and vector from ref pt to center of window
    1714         420 :         U21 = U1 - U2;
    1715         420 :         U23 = U3 - U2;
    1716         420 :         HW = U21.magnitude();
    1717         420 :         WW = U23.magnitude();
    1718         420 :         if (surf2.Sides == 4) {
    1719         420 :             WC = U2 + (U23 + U21) / 2.0;
    1720           0 :         } else if (surf2.Sides == 3) {
    1721           0 :             WC = U2 + (U23 + U21) / 3.0;
    1722             :         }
    1723         420 :         state.dataSurface->SurfaceWindow(IWin2).WinCenter = WC;
    1724             :         // Unit vectors
    1725         420 :         U21 /= HW;
    1726         420 :         U23 /= WW;
    1727             : 
    1728             :         // Unit vector normal to dome (pointing away from TDD)
    1729             :         // These are specific to the exterior.
    1730             :         // NOTE:  Preserve WNORM for later in the code.
    1731         420 :         WNORM2 = cross(U21, U23).normalize();
    1732             : 
    1733             :         // Azimuth and altitude of dome normal
    1734             :         // These are specific to the exterior.
    1735         420 :         surfWin2.phi = std::asin(WNORM2.z);
    1736         420 :         surfWin2.theta = (std::abs(WNORM2.x) > 1.0e-5 || std::abs(WNORM2.y) > 1.0e-5) ? std::atan2(WNORM2.y, WNORM2.x) : 0.0;
    1737             : 
    1738             :         // Calculate new virtual reference point coords relative to dome coord system
    1739             :         // W21, W23, and WNORM2 are now the unit vectors for the dome coord system
    1740         420 :         REFWC = REFD.x * U21 + REFD.y * U23 + REFD.z * WNORM2;
    1741         420 :         RREF2 = WC - REFWC;
    1742             : 
    1743             :         // Calculate new virtual view vector coords relative to dome coord system
    1744         420 :         VIEWVC2 = VIEWVD.x * U21 + VIEWVD.y * U23 + VIEWVD.z * WNORM2;
    1745             : 
    1746             :         // Copy several values from the diffuser so that DayltgInterReflectedIllum works correctly
    1747             :         // These are specific to the interior.
    1748         420 :         surfWin2.rhoCeilingWall = surfWin.rhoCeilingWall;
    1749         420 :         surfWin2.rhoFloorWall = surfWin.rhoFloorWall;
    1750         420 :         surfWin2.fractionUpgoing = surfWin.fractionUpgoing;
    1751         420 :         surfWin2.glazedFrac = surfWin.glazedFrac;
    1752             : 
    1753         420 :     } else {
    1754             :         // This is not a TDD:DIFFUSER.  Make sure nothing is messed up for a regular window.
    1755       28436 :         IWin2 = IWin;
    1756       28436 :         WNORM2 = WNORM;
    1757       28436 :         RREF2 = RREF;
    1758       28436 :         VIEWVC2 = VIEWVC;
    1759             : 
    1760       28436 :         U2 = W2;
    1761       28436 :         U21 = W21;
    1762       28436 :         U23 = W23;
    1763             :     }
    1764             : 
    1765             :     // Initialize bsdf daylighting coefficients here.  Only one time initialization
    1766       28856 :     if (state.dataSurface->SurfWinWindowModelType(IWin) == WindowModel::BSDF) {
    1767          18 :         if (!state.dataBSDFWindow->ComplexWind(IWin).DaylightingInitialized) {
    1768          18 :             int NRefPts = 0;
    1769          18 :             if (CalledFrom == CalledFor::MapPoint) {
    1770           0 :                 NRefPts = dl->illumMaps(MapNum).TotalMapRefPoints;
    1771          18 :             } else if (CalledFrom == CalledFor::RefPoint) {
    1772          18 :                 NRefPts = dl->daylightControl(daylightCtrlNum).TotalDaylRefPoints;
    1773             :             }
    1774          18 :             InitializeCFSDaylighting(state, daylightCtrlNum, IWin, NWX, NWY, RREF, NRefPts, iRefPoint, CalledFrom, MapNum);
    1775             :             // if ((WinEl == (NWX * NWY)).and.(CalledFrom == CalledForMapPoint).and.(NRefPts == iRefPoint)) then
    1776          18 :             if ((CalledFrom == CalledFor::MapPoint) && (NRefPts == iRefPoint)) {
    1777           0 :                 state.dataBSDFWindow->ComplexWind(IWin).DaylightingInitialized = true;
    1778             :             }
    1779             :         }
    1780             :     }
    1781             : 
    1782       28856 :     int iHrBeg = state.dataSysVars->DetailedSolarTimestepIntegration ? state.dataGlobal->HourOfDay : 1;
    1783       28856 :     int iHrEnd = state.dataSysVars->DetailedSolarTimestepIntegration ? state.dataGlobal->HourOfDay : Constant::HoursInDay;
    1784       28856 :     int numSlatAngs = state.dataSurface->actualMaxSlatAngs + 1;
    1785             : 
    1786      721400 :     for (int iHr = iHrBeg; iHr <= iHrEnd; ++iHr) {
    1787     2077632 :         for (int iSlatAng = 1; iSlatAng <= numSlatAngs; ++iSlatAng) {
    1788             :             // Initialize sky and sun components of direct illuminance (arrays EDIRSK, EDIRSU, EDIRSUdisk)
    1789             :             // and average window luminance (arrays AVWLSK, AVWLSU, AVWLSUdisk), at ref pt.
    1790     5540352 :             dl->dirIllum(iHr, iSlatAng) = Illums();
    1791     5540352 :             dl->avgWinLum(iHr, iSlatAng) = Illums();
    1792             :         }
    1793             :     }
    1794             : 
    1795       28856 :     if (CalledFrom == CalledFor::RefPoint) {
    1796             :         // Initialize solid angle subtended by window wrt ref pt
    1797             :         // and solid angle weighted by glare position factor
    1798       15156 :         state.dataSurface->SurfaceWindow(IWin).refPts(iRefPoint).solidAng = 0.0;
    1799       15156 :         state.dataSurface->SurfaceWindow(IWin).refPts(iRefPoint).solidAngWtd = 0.0;
    1800             :     }
    1801             :     // Area of window element
    1802       28856 :     if (is_Rectangle) {
    1803       28856 :         DAXY = DWX * DWY;
    1804           0 :     } else if (is_Triangle) {
    1805           0 :         SinCornerAng = std::sqrt(1.0 - pow_2(dot(W21, W23)));
    1806           0 :         DAXY = DWX * DWY * SinCornerAng;
    1807             :     }
    1808       28856 : }
    1809             : 
    1810     2813576 : void FigureDayltgCoeffsAtPointsForWindowElements(
    1811             :     EnergyPlusData &state,
    1812             :     int const daylightCtrlNum, // Current daylighting control number (only used when called from RefPoint)
    1813             :     int const iRefPoint,
    1814             :     int const loopwin,
    1815             :     CalledFor const CalledFrom, // indicate  which type of routine called this routine
    1816             :     int const WinEl,            // Current window element number
    1817             :     int const IWin,
    1818             :     int const IWin2,
    1819             :     int const iXelement,
    1820             :     int const iYelement,
    1821             :     Real64 &SkyObstructionMult,
    1822             :     Vector3<Real64> const &W2,      // Second vertex of window
    1823             :     Vector3<Real64> const &W21,     // Vector from window vertex 2 to window vertex 1
    1824             :     Vector3<Real64> const &W23,     // Vector from window vertex 2 to window vertex 3
    1825             :     Vector3<Real64> const &RREF,    // Location of a reference point in absolute coordinate system
    1826             :     int const NWYlim,               // For triangle, largest NWY for a given IX
    1827             :     Vector3<Real64> const &VIEWVC2, // Virtual view vector in absolute coordinate system
    1828             :     Real64 const DWX,               // Horizontal dimension of window element (m)
    1829             :     Real64 const DWY,               // Vertical dimension of window element (m)
    1830             :     Real64 const DAXY,              // Area of window element
    1831             :     Vector3<Real64> const &U2,      // Second vertex of window for TDD:DOME (if exists)
    1832             :     Vector3<Real64> const &U23,     // Vector from window vertex 2 to window vertex 3 for TDD:DOME (if exists)
    1833             :     Vector3<Real64> const &U21,     // Vector from window vertex 2 to window vertex 1 for TDD:DOME (if exists)
    1834             :     Vector3<Real64> &RWIN,          // Center of a window element for TDD:DOME (if exists) in abs coord sys
    1835             :     Vector3<Real64> &RWIN2,         // Center of a window element for TDD:DOME (if exists) in abs coord sys
    1836             :     Vector3<Real64> &Ray,           // Unit vector along ray from reference point to window element
    1837             :     Real64 &PHRAY,                  // Altitude of ray from reference point to window element (radians)
    1838             :     int &LSHCAL,                    // Interior shade calculation flag:  0=not yet calculated, 1=already calculated
    1839             :     Real64 &COSB,                   // Cosine of angle between window outward normal and ray from reference point to window element
    1840             :     Real64 &ObTrans,                // Product of solar transmittances of exterior obstructions hit by ray
    1841             :     Real64 &TVISB,                  // Visible transmittance of window for COSB angle of incidence (times light well
    1842             :     Real64 &DOMEGA,                 // Solid angle subtended by window element wrt reference point (steradians)
    1843             :     Real64 &THRAY,                  // Azimuth of ray from reference point to window element (radians)
    1844             :     bool &hitIntObs,                // True iff interior obstruction hit
    1845             :     bool &hitExtObs,                // True iff ray from ref pt to ext win hits an exterior obstruction
    1846             :     Vector3<Real64> const &WNORM2,  // Unit vector normal to window
    1847             :     ExtWinType const extWinType,    // Exterior window type (InZoneExtWin, AdjZoneExtWin, NotInOrAdjZoneExtWin)
    1848             :     int const IConst,               // Construction counter
    1849             :     Vector3<Real64> const &RREF2,   // Location of virtual reference point in absolute coordinate system
    1850             :     bool const is_Triangle,
    1851             :     Real64 &TVISIntWin,     // Visible transmittance of int win at COSBIntWin for light from ext win
    1852             :     Real64 &TVISIntWinDisk, // Visible transmittance of int win at COSBIntWin for sun
    1853             :     int const MapNum)
    1854             : {
    1855             : 
    1856             :     // SUBROUTINE INFORMATION:
    1857             :     //       AUTHOR         B. Griffith
    1858             :     //       DATE WRITTEN   November 2012, refactor from legacy code by Fred Winklemann
    1859             : 
    1860             :     // PURPOSE OF THIS SUBROUTINE:
    1861             :     // collect code to do calculations for each window element for daylighting coefficients
    1862             : 
    1863             :     // REFERENCES:
    1864             :     // switch as need to serve both reference points and map points based on calledFrom
    1865     2813576 :     auto &dl = state.dataDayltg;
    1866             : 
    1867             :     Real64 RR; // Distance from ref point to intersection of view vector
    1868             :     //  and plane normal to view vector and window element (m)
    1869             :     Real64 ASQ; // Square of distance from above intersection to window element (m2)
    1870             :     Real64 YD;  // Vertical displacement of window element wrt ref point
    1871             : 
    1872             :     Real64 COSBIntWin; // Cos of angle between int win outward normal and ray betw ref pt and
    1873             :     //  exterior window element or between ref pt and sun
    1874             : 
    1875             :     // Local complex fenestration variables
    1876             :     Real64 TransBeam; // Obstructions transmittance for incoming BSDF rays (temporary variable)
    1877             : 
    1878     2813576 :     auto &surfWin = state.dataSurface->SurfaceWindow(IWin);
    1879             : 
    1880     2813576 :     ++LSHCAL;
    1881     2813576 :     SkyObstructionMult = 1.0;
    1882             : 
    1883             :     // Center of win element in absolute coord sys
    1884     2813576 :     RWIN = W2 + (double(iXelement) - 0.5) * W23 * DWX + (double(iYelement) - 0.5) * W21 * DWY;
    1885             : 
    1886             :     // Center of win element on TDD:DOME in absolute coord sys
    1887             :     // If no TDD, RWIN2 = RWIN
    1888     2813576 :     RWIN2 = U2 + (double(iXelement) - 0.5) * U23 * DWX + (double(iYelement) - 0.5) * U21 * DWY;
    1889             : 
    1890             :     // Distance between ref pt and window element
    1891     2813576 :     Real64 DIS = distance(RWIN, RREF);
    1892             : 
    1893             :     // Unit vector along ray from ref pt to element
    1894     2813576 :     Ray = (RWIN - RREF) / DIS;
    1895             : 
    1896             :     // Cosine of angle between ray and window outward normal
    1897     2813576 :     COSB = dot(WNORM2, Ray);
    1898             : 
    1899             :     // If COSB > 0, direct light from window can reach ref pt. Otherwise go to loop
    1900             :     // over sun position and calculate inter-reflected component of illuminance
    1901     2813576 :     if (COSB <= 0.0) return;
    1902             : 
    1903             :     // Azimuth (-pi to pi) and altitude (-pi/2 to pi/2) of ray. Azimuth = 0 is along east.
    1904     2813576 :     PHRAY = std::asin(Ray.z);
    1905     2813576 :     if (std::abs(Ray.x) > 1.0e-5 || std::abs(Ray.y) > 1.0e-5) {
    1906     2813571 :         THRAY = std::atan2(Ray.y, Ray.x);
    1907             :     } else {
    1908           5 :         THRAY = 0.0;
    1909             :     }
    1910             : 
    1911             :     // Solid angle subtended by element wrt ref pt.
    1912     2813576 :     Real64 DAXY1 = DAXY; // For triangle, area of window element at end of column
    1913             :     // For triangle, at end of Y column only one half of parallelopiped's area contributes
    1914     2813576 :     if (is_Triangle && iYelement == NWYlim) DAXY1 = 0.5 * DAXY;
    1915     2813576 :     DOMEGA = DAXY1 * COSB / (DIS * DIS);
    1916             : 
    1917             :     // Calculate position factor (used in glare calculation) for this
    1918             :     // win element / ref pt / view-vector combination
    1919     2813576 :     Real64 POSFAC = 0.0;
    1920             : 
    1921             :     // Distance from ref pt to intersection of view vector and plane
    1922             :     // normal to view vector containing the window element
    1923             : 
    1924     2813576 :     if (CalledFrom == CalledFor::RefPoint) {
    1925      581494 :         RR = DIS * dot(Ray, VIEWVC2);
    1926      581494 :         if (RR > 0.0) {
    1927             :             // Square of distance from above intersection point to win element
    1928      528371 :             ASQ = DIS * DIS - RR * RR;
    1929             :             // Vertical displacement of win element wrt ref pt
    1930      528371 :             YD = RWIN2.z - RREF2.z;
    1931             :             // Horizontal and vertical displacement ratio and position factor
    1932      528371 :             Real64 XR = std::sqrt(std::abs(ASQ - YD * YD)) / RR;
    1933      528371 :             Real64 YR = std::abs(YD / RR);
    1934      528371 :             POSFAC = DayltgGlarePositionFactor(XR, YR);
    1935             :         }
    1936             :     }
    1937             : 
    1938     2813576 :     hitIntObs = false;
    1939     2813576 :     int IntWinHitNum = 0;   // Surface number of interior window that is intersected
    1940     2813576 :     bool hitIntWin = false; // Ray from ref pt passes through interior window
    1941     2813576 :     TVISIntWinDisk = 0.0;   // Init Value
    1942     2813576 :     TVISIntWin = 0.0;
    1943             : 
    1944     2813576 :     Vector3<Real64> HitPtIntWin = {0.0, 0.0, 0.0};
    1945     2813576 :     auto &surf = state.dataSurface->Surface(IWin);
    1946     2813576 :     if (surf.OriginalClass == SurfaceClass::TDD_Diffuser) {
    1947             :         // Look up the TDD:DOME object
    1948         420 :         int PipeNum = state.dataSurface->SurfWinTDDPipeNum(IWin);
    1949             :         // Unshaded visible transmittance of TDD for a single ray from sky/ground element
    1950         420 :         TVISB = TransTDD(state, PipeNum, COSB, RadType::VisibleBeam) * surfWin.glazedFrac;
    1951             : 
    1952             :     } else { // Regular window
    1953     2813156 :         if (state.dataSurface->SurfWinWindowModelType(IWin) != WindowModel::BSDF) {
    1954             :             // Vis trans of glass for COSB incidence angle
    1955     2813012 :             TVISB = General::POLYF(COSB, state.dataConstruction->Construct(IConst).TransVisBeamCoef) * surfWin.glazedFrac * surfWin.lightWellEff;
    1956             :         } else {
    1957             :             // Complex fenestration needs to use different equation for visible transmittance.  That will be calculated later
    1958             :             // in the code since it depends on different incoming directions.  For now, just put zero to differentiate from
    1959             :             // regular windows
    1960         144 :             TVISB = 0.0;
    1961             :         }
    1962     2813156 :         if (extWinType == ExtWinType::AdjZone) {
    1963       23680 :             int zoneNum = 0;
    1964       23680 :             if (CalledFrom == CalledFor::RefPoint) {
    1965          40 :                 zoneNum = dl->daylightControl(daylightCtrlNum).zoneIndex;
    1966       23640 :             } else if (CalledFrom == CalledFor::MapPoint) {
    1967       23640 :                 assert(MapNum > 0);
    1968       23640 :                 zoneNum = dl->illumMaps(MapNum).zoneIndex;
    1969             :             }
    1970             :             // Does ray pass through an interior window in zone (ZoneNum) containing the ref point?
    1971       47360 :             for (int const spaceNum : state.dataHeatBal->Zone(zoneNum).spaceIndexes) {
    1972       23680 :                 auto const &thisSpace = state.dataHeatBal->space(spaceNum);
    1973       26948 :                 for (int IntWin = thisSpace.WindowSurfaceFirst; IntWin <= thisSpace.WindowSurfaceLast; ++IntWin) {
    1974       23680 :                     auto const &surfIntWin = state.dataSurface->Surface(IntWin);
    1975             :                     // in develop this was Surface(IntWin).Class == SurfaceClass::Window && Surface(IntWin).ExtBoundCond >= 1
    1976       23680 :                     if (surfIntWin.ExtBoundCond < 1) continue;
    1977             : 
    1978       23680 :                     if (state.dataSurface->Surface(surfIntWin.ExtBoundCond).Zone != surf.Zone) continue;
    1979             : 
    1980       23680 :                     hitIntWin = PierceSurface(state, IntWin, RREF, Ray, HitPtIntWin);
    1981       23680 :                     if (hitIntWin) {
    1982       20412 :                         IntWinHitNum = IntWin;
    1983       20412 :                         COSBIntWin = dot(surfIntWin.OutNormVec, Ray);
    1984       20412 :                         if (COSBIntWin <= 0.0) {
    1985           0 :                             hitIntWin = false;
    1986           0 :                             IntWinHitNum = 0;
    1987           0 :                             continue;
    1988             :                         }
    1989       20412 :                         TVISIntWin = General::POLYF(COSBIntWin, state.dataConstruction->Construct(surfIntWin.Construction).TransVisBeamCoef);
    1990       20412 :                         TVISB *= TVISIntWin;
    1991       20412 :                         break; // Ray passes thru interior window; exit from DO loop
    1992             :                     }
    1993             :                 }
    1994       23680 :             } // End of loop over surfaces in zone ZoneNum
    1995             : 
    1996       23680 :             if (!hitIntWin) {
    1997             :                 // Ray does not pass through an int win in ZoneNum. Therefore, it hits the opaque part
    1998             :                 // of a surface between ref point in ZoneNum and ext win element in adjacent zone.
    1999        3268 :                 hitIntObs = true;
    2000             :             }
    2001             :         } // End of check if this is an ext win in an adjacent zone
    2002             :     }     // End of check if TDD:Diffuser or regular exterior window or complex fenestration
    2003             : 
    2004             :     // Check for interior obstructions
    2005     2813576 :     if (extWinType == ExtWinType::InZone && !hitIntObs) {
    2006             :         // Check for obstruction between reference point and window element
    2007             :         // Returns hitIntObs = true iff obstruction is hit
    2008             :         // (Example of interior obstruction is a wall in an L-shaped room that lies
    2009             :         // between reference point and window.)
    2010     2789896 :         hitIntObs = DayltgHitInteriorObstruction(state, IWin, RREF, RWIN);
    2011             :     }
    2012             : 
    2013     2813576 :     if (extWinType == ExtWinType::AdjZone && IntWinHitNum > 0 && !hitIntObs) {
    2014             :         // Check for obstruction between ref point and interior window through which ray passes
    2015       20412 :         hitIntObs = DayltgHitInteriorObstruction(state, IntWinHitNum, RREF, HitPtIntWin);
    2016       20412 :         if (!hitIntObs) {
    2017             :             // Check for obstruction between intersection point on int window and ext win element
    2018       20412 :             hitIntObs = DayltgHitBetWinObstruction(state, IntWinHitNum, IWin, HitPtIntWin, RWIN);
    2019             :         }
    2020             :     }
    2021     2813576 :     if (CalledFrom == CalledFor::RefPoint) {
    2022             :         // Glare calculations only done for regular reference points, not for maps
    2023      581494 :         if (!hitIntObs) {
    2024      581424 :             if (extWinType == ExtWinType::InZone || (extWinType == ExtWinType::AdjZone && hitIntWin)) {
    2025             :                 // Increment solid angle subtended by portion of window above ref pt
    2026      581424 :                 surfWin.refPts(iRefPoint).solidAng += DOMEGA;
    2027      581424 :                 dl->daylightControl(daylightCtrlNum).refPts(iRefPoint).extWins(loopwin).solidAng += DOMEGA;
    2028             :                 // Increment position-factor-modified solid angle
    2029      581424 :                 surfWin.refPts(iRefPoint).solidAngWtd += DOMEGA * POSFAC;
    2030      581424 :                 dl->daylightControl(daylightCtrlNum).refPts(iRefPoint).extWins(loopwin).solidAngWtd += DOMEGA * POSFAC;
    2031             :             }
    2032             :         }
    2033             :     }
    2034     2813576 :     if (hitIntObs) ObTrans = 0.0;
    2035             : 
    2036     2813576 :     hitExtObs = false;
    2037     2813576 :     if (!hitIntObs) {
    2038             :         // No interior obstruction was hit.
    2039             :         // Check for exterior obstructions between window element and sky/ground.
    2040             :         // Get product of transmittances of obstructions hit by ray.
    2041             :         // ObTrans = 1.0 will be returned if no exterior obstructions are hit.
    2042             : 
    2043     2778990 :         if (state.dataSurface->SurfWinWindowModelType(IWin) != WindowModel::BSDF) {
    2044             :             // the IHR (now HourOfDay) here is/was not correct, this is outside of hour loop
    2045             :             // the hour is used to query schedule for transmission , not sure what to do
    2046             :             // it will work for detailed and never did work correctly before.
    2047     2778846 :             ObTrans = DayltgHitObstruction(state, state.dataGlobal->HourOfDay, IWin2, RWIN2, Ray);
    2048     2778846 :             if (ObTrans < 1.0) hitExtObs = true;
    2049             :         } else {
    2050             :             // Transmittance from exterior obstruction surfaces is calculated here. This needs to be done for each timestep
    2051             :             // in order to account for changes in exterior surface transmittances
    2052         144 :             int CplxFenState = surfWin.ComplexFen.CurrentState;
    2053         144 :             auto &complexWinDayltgGeom = state.dataBSDFWindow->ComplexWind(IWin).DaylghtGeom(CplxFenState);
    2054         144 :             int NReflSurf = 0; // Number of blocked beams for complex fenestration
    2055         144 :             if (CalledFrom == CalledFor::RefPoint) {
    2056         144 :                 NReflSurf = complexWinDayltgGeom.RefPoint(iRefPoint).NReflSurf(WinEl);
    2057           0 :             } else if (CalledFrom == CalledFor::MapPoint) {
    2058           0 :                 NReflSurf = complexWinDayltgGeom.IlluminanceMap(iRefPoint, MapNum).NReflSurf(WinEl);
    2059             :             }
    2060             :             int RayIndex;
    2061         144 :             for (int ICplxFen = 1; ICplxFen <= NReflSurf; ++ICplxFen) {
    2062           0 :                 if (CalledFrom == CalledFor::RefPoint) {
    2063           0 :                     RayIndex = complexWinDayltgGeom.RefPoint(iRefPoint).RefSurfIndex(ICplxFen, WinEl);
    2064           0 :                 } else if (CalledFrom == CalledFor::MapPoint) {
    2065           0 :                     RayIndex = complexWinDayltgGeom.IlluminanceMap(iRefPoint, MapNum).RefSurfIndex(ICplxFen, WinEl);
    2066             :                 }
    2067           0 :                 Vector3<Real64> RayVector = state.dataBSDFWindow->ComplexWind(IWin).Geom(CplxFenState).sInc(RayIndex);
    2068             :                 // It will get product of all transmittances
    2069           0 :                 TransBeam = DayltgHitObstruction(state, state.dataGlobal->HourOfDay, IWin, RWIN, RayVector);
    2070             :                 // IF (TransBeam > 0.0d0) ObTrans = TransBeam
    2071           0 :                 if (CalledFrom == CalledFor::RefPoint) {
    2072           0 :                     complexWinDayltgGeom.RefPoint(iRefPoint).TransOutSurf(ICplxFen, WinEl) = TransBeam;
    2073           0 :                 } else if (CalledFrom == CalledFor::MapPoint) {
    2074           0 :                     complexWinDayltgGeom.IlluminanceMap(iRefPoint, MapNum).TransOutSurf(ICplxFen, WinEl) = TransBeam;
    2075             :                 }
    2076           0 :             }
    2077             :             // This will avoid obstruction multiplier calculations for non-CFS window
    2078         144 :             ObTrans = 0.0;
    2079             :         }
    2080             :     }
    2081             : 
    2082     2813576 :     if (state.dataSurface->CalcSolRefl && PHRAY < 0.0 && ObTrans > 1.0e-6) {
    2083             :         // Calculate effect of obstructions on shading of sky diffuse reaching the ground point hit
    2084             :         // by the ray. This effect is given by the ratio SkyObstructionMult =
    2085             :         // (obstructed sky diffuse at ground point)/(unobstructed sky diffuse at ground point).
    2086             :         // This ratio is calculated for an isotropic sky.
    2087             :         // Ground point hit by the ray:
    2088         220 :         Real64 Alfa = std::acos(-Ray.z);
    2089         220 :         Real64 Beta = std::atan2(Ray.y, Ray.x);
    2090             :         // Distance between ground hit point and proj'n of center of window element onto ground (m)
    2091         220 :         Real64 HorDis = (RWIN2.z - state.dataSurface->GroundLevelZ) * std::tan(Alfa);
    2092         220 :         Vector3<Real64> GroundHitPt = {RWIN2.x + HorDis * std::cos(Beta), RWIN2.y + HorDis * std::sin(Beta), state.dataSurface->GroundLevelZ};
    2093             : 
    2094         220 :         SkyObstructionMult =
    2095         220 :             CalcObstrMultiplier(state, GroundHitPt, DataSurfaces::AltAngStepsForSolReflCalc, DataSurfaces::AzimAngStepsForSolReflCalc);
    2096         220 :     } // End of check if solar reflection calculation is in effect
    2097     2813576 : } // FigureDayltgCoeffsAtPointsForWindowElements()
    2098             : 
    2099          18 : void InitializeCFSDaylighting(EnergyPlusData &state,
    2100             :                               int const daylightCtrlNum,       // Current daylighting control number
    2101             :                               int const IWin,                  // Complex fenestration number
    2102             :                               int const NWX,                   // Number of horizontal divisions
    2103             :                               int const NWY,                   // Number of vertical divisions
    2104             :                               Vector3<Real64> const &RefPoint, // reference point coordinates
    2105             :                               int const NRefPts,               // Number of reference points
    2106             :                               int const iRefPoint,             // Reference points counter
    2107             :                               CalledFor const CalledFrom,
    2108             :                               int const MapNum)
    2109             : {
    2110             :     // SUBROUTINE INFORMATION:
    2111             :     //       AUTHOR         Simon Vidanovic
    2112             :     //       DATE WRITTEN   April 2013
    2113             : 
    2114             :     // PURPOSE OF THIS SUBROUTINE:
    2115             :     // For incoming BSDF window direction calculates whether bin is coming from sky, ground or reflected surface.
    2116             :     // Routine also calculates intersection points with ground and exterior reflection surfaces.
    2117          18 :     auto &dl = state.dataDayltg;
    2118             : 
    2119             :     // Object Data
    2120          18 :     DataBSDFWindow::BSDFDaylghtPosition elPos; // altitude and azimuth of intersection element
    2121          18 :     Vector Vec;                                // temporary vector variable
    2122             : 
    2123          18 :     int NumOfWinEl = NWX * NWY; // Number of window elements
    2124             : 
    2125          18 :     auto &surf = state.dataSurface->Surface(IWin);
    2126          18 :     Real64 DWX = surf.Width / NWX;  // Window element width
    2127          18 :     Real64 DWY = surf.Height / NWY; // Window element height
    2128             : 
    2129          18 :     int zoneNum = dl->daylightControl(daylightCtrlNum).zoneIndex;
    2130          18 :     Real64 AZVIEW = (dl->daylightControl(daylightCtrlNum).ViewAzimuthForGlare + state.dataHeatBal->Zone(zoneNum).RelNorth +
    2131          18 :                      state.dataHeatBal->BuildingAzimuth + state.dataHeatBal->BuildingRotationAppendixG) *
    2132          18 :                     Constant::DegToRadians;
    2133             : 
    2134             :     // Perform necessary calculations for window coordinates and vectors.  This will be used to calculate centroids for
    2135             :     // each window element
    2136          18 :     Vector3<Real64> W1 = {0.0, 0.0, 0.0};
    2137          18 :     Vector3<Real64> W2 = {0.0, 0.0, 0.0};
    2138          18 :     Vector3<Real64> W3 = {0.0, 0.0, 0.0};
    2139             : 
    2140          18 :     if (surf.Sides == 4) {
    2141          18 :         W3 = surf.Vertex(2);
    2142          18 :         W2 = surf.Vertex(3);
    2143          18 :         W1 = surf.Vertex(4);
    2144           0 :     } else if (surf.Sides == 3) {
    2145           0 :         W3 = surf.Vertex(2);
    2146           0 :         W2 = surf.Vertex(3);
    2147           0 :         W1 = surf.Vertex(1);
    2148             :     }
    2149             : 
    2150          18 :     Vector3<Real64> W21 = W1 - W2;
    2151          18 :     W21 /= surf.Height;
    2152          18 :     Vector3<Real64> W23 = W3 - W2;
    2153          18 :     W23 /= surf.Width;
    2154          18 :     Vector3<Real64> WNorm = surf.lcsz;
    2155             : 
    2156          18 :     Real64 WinElArea = DWX * DWY;
    2157          18 :     if (surf.Sides == 3) {
    2158           0 :         WinElArea *= std::sqrt(1.0 - pow_2(dot(W21, W23)));
    2159             :     }
    2160             : 
    2161          18 :     auto &complexWin = state.dataBSDFWindow->ComplexWind(IWin);
    2162             : 
    2163          18 :     if (CalledFrom == CalledFor::MapPoint) {
    2164             : 
    2165           0 :         if (!allocated(complexWin.IlluminanceMap)) {
    2166           0 :             complexWin.IlluminanceMap.allocate(NRefPts, (int)dl->illumMaps.size());
    2167             :         }
    2168             : 
    2169           0 :         AllocateForCFSRefPointsGeometry(complexWin.IlluminanceMap(iRefPoint, MapNum), NumOfWinEl);
    2170             : 
    2171          18 :     } else if (CalledFrom == CalledFor::RefPoint) {
    2172          18 :         if (!allocated(complexWin.RefPoint)) {
    2173           2 :             complexWin.RefPoint.allocate(NRefPts);
    2174             :         }
    2175             : 
    2176          18 :         AllocateForCFSRefPointsGeometry(complexWin.RefPoint(iRefPoint), NumOfWinEl);
    2177             :     }
    2178             : 
    2179             :     //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    2180             :     //! Allocation for each complex fenestration state reference points
    2181             :     //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    2182          18 :     if (!allocated(complexWin.DaylghtGeom)) {
    2183           2 :         complexWin.DaylghtGeom.allocate(state.dataBSDFWindow->ComplexWind(IWin).NumStates);
    2184             :     }
    2185             : 
    2186             :     // Calculation needs to be performed for each state
    2187          36 :     for (int CurFenState = 1; CurFenState <= complexWin.NumStates; ++CurFenState) {
    2188             :         // number of incident basis directions for current state
    2189          18 :         int NBasis = complexWin.Geom(CurFenState).Inc.NBasis;
    2190             :         // number of outgoing basis directions for current state
    2191          18 :         int NTrnBasis = complexWin.Geom(CurFenState).Trn.NBasis;
    2192             : 
    2193          18 :         if (CalledFrom == CalledFor::MapPoint) {
    2194           0 :             if ((int)dl->illumMaps.size() > 0) {
    2195             :                 // illuminance map for each state
    2196           0 :                 if (!allocated(complexWin.DaylghtGeom(CurFenState).IlluminanceMap)) {
    2197           0 :                     complexWin.DaylghtGeom(CurFenState).IlluminanceMap.allocate(NRefPts, (int)dl->illumMaps.size());
    2198             :                 }
    2199             : 
    2200           0 :                 AllocateForCFSRefPointsState(
    2201           0 :                     state, complexWin.DaylghtGeom(CurFenState).IlluminanceMap(iRefPoint, MapNum), NumOfWinEl, NBasis, NTrnBasis);
    2202             : 
    2203           0 :                 InitializeCFSStateData(state,
    2204           0 :                                        complexWin.DaylghtGeom(CurFenState).IlluminanceMap(iRefPoint, MapNum),
    2205             :                                        complexWin.IlluminanceMap(iRefPoint, MapNum),
    2206             :                                        daylightCtrlNum,
    2207             :                                        IWin,
    2208             :                                        RefPoint,
    2209             :                                        CurFenState,
    2210             :                                        NBasis,
    2211             :                                        NTrnBasis,
    2212             :                                        AZVIEW,
    2213             :                                        NWX,
    2214             :                                        NWY,
    2215             :                                        W2,
    2216             :                                        W21,
    2217             :                                        W23,
    2218             :                                        DWX,
    2219             :                                        DWY,
    2220             :                                        WNorm,
    2221             :                                        WinElArea);
    2222             :             }
    2223             : 
    2224          18 :         } else if (CalledFrom == CalledFor::RefPoint) {
    2225          18 :             if (!allocated(complexWin.DaylghtGeom(CurFenState).RefPoint)) {
    2226           2 :                 complexWin.DaylghtGeom(CurFenState).RefPoint.allocate(NRefPts);
    2227             :             }
    2228             : 
    2229          18 :             AllocateForCFSRefPointsState(state, complexWin.DaylghtGeom(CurFenState).RefPoint(iRefPoint), NumOfWinEl, NBasis, NTrnBasis);
    2230             : 
    2231          36 :             InitializeCFSStateData(state,
    2232          18 :                                    complexWin.DaylghtGeom(CurFenState).RefPoint(iRefPoint),
    2233             :                                    complexWin.RefPoint(iRefPoint),
    2234             :                                    daylightCtrlNum,
    2235             :                                    IWin,
    2236             :                                    RefPoint,
    2237             :                                    CurFenState,
    2238             :                                    NBasis,
    2239             :                                    NTrnBasis,
    2240             :                                    AZVIEW,
    2241             :                                    NWX,
    2242             :                                    NWY,
    2243             :                                    W2,
    2244             :                                    W21,
    2245             :                                    W23,
    2246             :                                    DWX,
    2247             :                                    DWY,
    2248             :                                    WNorm,
    2249             :                                    WinElArea);
    2250             :         }
    2251             :     }
    2252          18 : } // InitializeCFSDaylighting()
    2253             : 
    2254          18 : void InitializeCFSStateData(EnergyPlusData &state,
    2255             :                             DataBSDFWindow::BSDFRefPoints &StateRefPoint,
    2256             :                             DataBSDFWindow::BSDFRefPointsGeomDescr &DaylghtGeomDescr,
    2257             :                             [[maybe_unused]] int const daylightCtrlNum, // Current daylighting control number
    2258             :                             int const iWin,
    2259             :                             Vector3<Real64> const &RefPoint, // reference point
    2260             :                             int const CurFenState,
    2261             :                             int const NBasis,
    2262             :                             int const NTrnBasis,
    2263             :                             Real64 const AZVIEW,
    2264             :                             int const NWX,
    2265             :                             int const NWY,
    2266             :                             Vector3<Real64> const &W2,
    2267             :                             Vector3<Real64> const &W21,
    2268             :                             Vector3<Real64> const &W23,
    2269             :                             Real64 const DWX,
    2270             :                             Real64 const DWY,
    2271             :                             Vector3<Real64> const &WNorm, // unit vector from window (point towards outside)
    2272             :                             Real64 const WinElArea)
    2273             : {
    2274             :     // SUBROUTINE INFORMATION:
    2275             :     //       AUTHOR         Simon Vidanovic
    2276             :     //       DATE WRITTEN   June 2013
    2277             : 
    2278             :     // PURPOSE OF THIS SUBROUTINE:
    2279             :     // Initialize daylight state data for current
    2280             : 
    2281             :     // SUBROUTINE LOCAL VARIABLES
    2282             :     int curWinEl;
    2283             :     bool hit;
    2284             :     int TotHits;
    2285             :     Real64 DotProd; // Temporary variable for manipulating dot product .dot.
    2286             :     int NSky;
    2287             :     int NGnd;
    2288             :     int NReflSurf;
    2289             :     int MaxTotHits;
    2290             :     Real64 LeastHitDsq; // dist^2 from window element center to hit point
    2291             :     Real64 HitDsq;
    2292             :     Real64 TransRSurf;
    2293             :     int J;
    2294             : 
    2295          18 :     Vector3<Real64> RWin;
    2296          18 :     Vector3<Real64> V;
    2297          18 :     Vector3<Real64> GroundHitPt;
    2298             : 
    2299             :     // temporary arrays for surfaces
    2300             :     // Each complex fenestration state can have different number of basis elements
    2301             :     // This is the reason for making these temporary arrays local
    2302          18 :     Array1D_int TmpSkyInd(NBasis, 0);                                         // Temporary sky index list
    2303          18 :     Array1D_int TmpGndInd(NBasis, 0);                                         // Temporary gnd index list
    2304          18 :     Array1D<Real64> TmpGndMultiplier(NBasis, 0.0);                            // Temporary ground obstruction multiplier
    2305          18 :     Array1D_int TmpRfSfInd(NBasis, 0);                                        // Temporary RefSurfIndex
    2306          18 :     Array1D_int TmpRfRyNH(NBasis, 0);                                         // Temporary RefRayNHits
    2307          36 :     Array2D_int TmpHSurfNo(state.dataSurface->TotSurfaces, NBasis, 0);        // Temporary HitSurfNo
    2308          36 :     Array2D<Real64> TmpHSurfDSq(state.dataSurface->TotSurfaces, NBasis, 0.0); // Temporary HitSurfDSq
    2309             : 
    2310             :     // Object Data
    2311          18 :     Vector3<Real64> Centroid;                                                                                  // current window element centroid
    2312          18 :     Vector3<Real64> HitPt;                                                                                     // surface hit point
    2313          36 :     Array1D<Vector3<Real64>> TmpGndPt(NBasis, Vector3<Real64>(0.0, 0.0, 0.0));                                 // Temporary ground intersection list
    2314          36 :     Array2D<Vector3<Real64>> TmpHitPt(state.dataSurface->TotSurfaces, NBasis, Vector3<Real64>(0.0, 0.0, 0.0)); // Temporary HitPt
    2315             : 
    2316          18 :     CFSRefPointPosFactor(state, RefPoint, StateRefPoint, iWin, CurFenState, NTrnBasis, AZVIEW);
    2317             : 
    2318          18 :     auto &surf = state.dataSurface->Surface(iWin);
    2319             : 
    2320          18 :     curWinEl = 0;
    2321             :     // loop through window elements. This will calculate sky, ground and reflection bins for each window element
    2322          54 :     for (int IX = 1; IX <= NWX; ++IX) {
    2323         180 :         for (int IY = 1; IY <= NWY; ++IY) {
    2324             : 
    2325         144 :             ++curWinEl;
    2326             : 
    2327             :             // centroid coordinates for current window element
    2328         144 :             Centroid = W2 + (double(IX) - 0.5) * W23 * DWX + (double(IY) - 0.5) * W21 * DWY;
    2329         144 :             RWin = Centroid;
    2330             : 
    2331         144 :             CFSRefPointSolidAngle(state, RefPoint, RWin, WNorm, StateRefPoint, DaylghtGeomDescr, iWin, CurFenState, NTrnBasis, curWinEl, WinElArea);
    2332             : 
    2333         144 :             NSky = 0;
    2334         144 :             NGnd = 0;
    2335         144 :             NReflSurf = 0;
    2336         144 :             MaxTotHits = 0;
    2337             :             // Calculation of potential surface obstruction for each incoming direction
    2338       21024 :             for (int IRay = 1; IRay <= NBasis; ++IRay) {
    2339             : 
    2340       20880 :                 hit = false;
    2341       20880 :                 TotHits = 0;
    2342      187920 :                 for (int JSurf = 1; JSurf <= state.dataSurface->TotSurfaces; ++JSurf) {
    2343      167040 :                     auto &surf2 = state.dataSurface->Surface(JSurf);
    2344             : 
    2345             :                     // the following test will cycle on anything except exterior surfaces and shading surfaces
    2346      167040 :                     if (surf2.HeatTransSurf && surf2.ExtBoundCond != ExternalEnvironment) continue;
    2347             :                     //  skip the base surface containing the window and any other subsurfaces of that surface
    2348       41760 :                     if (JSurf == surf.BaseSurf || surf2.BaseSurf == surf.BaseSurf) continue;
    2349             :                     //  skip surfaces that face away from the window
    2350           0 :                     DotProd = dot(state.dataBSDFWindow->ComplexWind(iWin).Geom(CurFenState).sInc(IRay), surf2.NewellSurfaceNormalVector);
    2351           0 :                     if (DotProd >= 0) continue;
    2352           0 :                     hit = PierceSurface(state, JSurf, Centroid, state.dataBSDFWindow->ComplexWind(iWin).Geom(CurFenState).sInc(IRay), HitPt);
    2353           0 :                     if (!hit) continue; // Miss: Try next surface
    2354           0 :                     if (TotHits == 0) {
    2355             :                         // First hit for this ray
    2356           0 :                         TotHits = 1;
    2357           0 :                         ++NReflSurf;
    2358           0 :                         TmpRfSfInd(NReflSurf) = IRay;
    2359           0 :                         TmpRfRyNH(NReflSurf) = 1;
    2360           0 :                         TmpHSurfNo(1, NReflSurf) = JSurf;
    2361           0 :                         TmpHitPt(1, NReflSurf) = HitPt;
    2362           0 :                         V = HitPt - Centroid;                // vector array from window ctr to hit pt
    2363           0 :                         LeastHitDsq = V.magnitude_squared(); // dist^2 window ctr to hit pt
    2364           0 :                         TmpHSurfDSq(1, NReflSurf) = LeastHitDsq;
    2365           0 :                         if (!surf2.HeatTransSurf && surf2.SchedShadowSurfIndex != 0) {
    2366           0 :                             TransRSurf = 1.0; // If a shadowing surface may have a scheduled transmittance, treat it here as completely transparent
    2367             :                         } else {
    2368           0 :                             TransRSurf = 0.0;
    2369             :                         }
    2370             :                     } else {
    2371           0 :                         V = HitPt - Centroid;
    2372           0 :                         HitDsq = V.magnitude_squared();
    2373           0 :                         if (HitDsq >= LeastHitDsq) {
    2374           0 :                             if (TransRSurf > 0.0) { // forget the new hit if the closer hit is opaque
    2375           0 :                                 J = TotHits + 1;
    2376           0 :                                 if (TotHits > 1) {
    2377           0 :                                     for (int I = 2; I <= TotHits; ++I) {
    2378           0 :                                         if (HitDsq < TmpHSurfDSq(I, NReflSurf)) {
    2379           0 :                                             J = I;
    2380           0 :                                             break;
    2381             :                                         }
    2382             :                                     }
    2383           0 :                                     if (!surf2.HeatTransSurf && surf2.SchedShadowSurfIndex == 0) {
    2384             :                                         //  The new hit is opaque, so we can drop all the hits further away
    2385           0 :                                         TmpHSurfNo(J, NReflSurf) = JSurf;
    2386           0 :                                         TmpHitPt(J, NReflSurf) = HitPt;
    2387           0 :                                         TmpHSurfDSq(J, NReflSurf) = HitDsq;
    2388           0 :                                         TotHits = J;
    2389             :                                     } else {
    2390             :                                         //  The new hit is scheduled (presumed transparent), so keep the more distant hits
    2391             :                                         //     Note that all the hists in the list will be transparent except the last,
    2392             :                                         //       which may be either transparent or opaque
    2393           0 :                                         if (TotHits >= J) {
    2394           0 :                                             for (int I = TotHits; I >= J; --I) {
    2395           0 :                                                 TmpHSurfNo(I + 1, NReflSurf) = TmpHSurfNo(I, NReflSurf);
    2396           0 :                                                 TmpHitPt(I + 1, NReflSurf) = TmpHitPt(I, NReflSurf);
    2397           0 :                                                 TmpHSurfDSq(I + 1, NReflSurf) = TmpHSurfDSq(I, NReflSurf);
    2398             :                                             }
    2399           0 :                                             TmpHSurfNo(J, NReflSurf) = JSurf;
    2400           0 :                                             TmpHitPt(J, NReflSurf) = HitPt;
    2401           0 :                                             TmpHSurfDSq(J, NReflSurf) = HitDsq;
    2402           0 :                                             ++TotHits;
    2403             :                                         }
    2404             :                                     } // if (.NOT.Surface(JSurf)%HeatTransSurf .AND. Surface(JSurf)%SchedShadowSurfIndex == 0)  then
    2405             :                                 }     // if (TotHits > 1) then
    2406             :                             }         // if (TransRSurf  > 0.0d0) then
    2407             :                         } else {      // if (HitDsq >= LeastHitDsq) then
    2408             :                             //  A new closest hit.  If it is opaque, drop the current hit list,
    2409             :                             //    otherwise add it at the front
    2410           0 :                             LeastHitDsq = HitDsq;
    2411           0 :                             if (!surf2.HeatTransSurf && surf2.SchedShadowSurfIndex != 0) {
    2412           0 :                                 TransRSurf = 1.0; // New closest hit is transparent, keep the existing hit list
    2413           0 :                                 for (int I = TotHits; I >= 1; --I) {
    2414           0 :                                     TmpHSurfNo(I + 1, NReflSurf) = TmpHSurfNo(I, NReflSurf);
    2415           0 :                                     TmpHitPt(I + 1, NReflSurf) = TmpHitPt(I, NReflSurf);
    2416           0 :                                     TmpHSurfDSq(I + 1, NReflSurf) = TmpHSurfDSq(I, NReflSurf);
    2417           0 :                                     ++TotHits;
    2418             :                                 }
    2419           0 :                             } else {
    2420           0 :                                 TransRSurf = 0.0; // New closest hit is opaque, drop the existing hit list
    2421           0 :                                 TotHits = 1;
    2422             :                             }
    2423           0 :                             TmpHSurfNo(1, NReflSurf) = JSurf; // In either case the new hit is put in position 1
    2424           0 :                             TmpHitPt(1, NReflSurf) = HitPt;
    2425           0 :                             TmpHSurfDSq(1, NReflSurf) = LeastHitDsq;
    2426             :                         }
    2427             :                     }
    2428             :                 } // do JSurf = 1, TotSurfaces
    2429       20880 :                 if (TotHits <= 0) {
    2430       20880 :                     auto &sIncRay = state.dataBSDFWindow->ComplexWind(iWin).Geom(CurFenState).sInc(IRay);
    2431             :                     // This ray reached the sky or ground unobstructed
    2432       20880 :                     if (sIncRay.z < 0.0) {
    2433             :                         // A ground ray
    2434        9216 :                         ++NGnd;
    2435        9216 :                         TmpGndInd(NGnd) = IRay;
    2436        9216 :                         TmpGndPt(NGnd).x = Centroid.x - (sIncRay.x / sIncRay.z) * Centroid.z;
    2437        9216 :                         TmpGndPt(NGnd).y = Centroid.y - (sIncRay.y / sIncRay.z) * Centroid.z;
    2438        9216 :                         TmpGndPt(NGnd).z = 0.0;
    2439             : 
    2440             :                         // for solar reflectance calculations, need to precalculate obstruction multipliers
    2441        9216 :                         if (state.dataSurface->CalcSolRefl) {
    2442        9216 :                             GroundHitPt = TmpGndPt(NGnd);
    2443        9216 :                             TmpGndMultiplier(NGnd) =
    2444        9216 :                                 CalcObstrMultiplier(state, GroundHitPt, AltAngStepsForSolReflCalc, DataSurfaces::AzimAngStepsForSolReflCalc);
    2445             :                         }
    2446             :                     } else {
    2447             :                         // A sky ray
    2448       11664 :                         ++NSky;
    2449       11664 :                         TmpSkyInd(NSky) = IRay;
    2450             :                     }
    2451             :                 } else {
    2452             :                     // Save the number of hits for this ray
    2453           0 :                     TmpRfRyNH(NReflSurf) = TotHits;
    2454             :                 }
    2455       20880 :                 MaxTotHits = max(MaxTotHits, TotHits);
    2456             :             } // do IRay = 1, ComplexWind(IWin)%Geom(CurFenState)%Inc%NBasis
    2457             : 
    2458             :             // Fill up state data for current window element data
    2459         144 :             StateRefPoint.NSky(curWinEl) = NSky;
    2460         144 :             StateRefPoint.SkyIndex({1, NSky}, curWinEl) = TmpSkyInd({1, NSky});
    2461             : 
    2462         144 :             StateRefPoint.NGnd(curWinEl) = NGnd;
    2463         144 :             StateRefPoint.GndIndex({1, NGnd}, curWinEl) = TmpGndInd({1, NGnd});
    2464         144 :             StateRefPoint.GndPt({1, NGnd}, curWinEl) = TmpGndPt({1, NGnd});
    2465         144 :             StateRefPoint.GndObstrMultiplier({1, NGnd}, curWinEl) = TmpGndMultiplier({1, NGnd});
    2466             : 
    2467         144 :             StateRefPoint.NReflSurf(curWinEl) = NReflSurf;
    2468         144 :             StateRefPoint.RefSurfIndex({1, NReflSurf}, curWinEl) = TmpRfSfInd({1, NReflSurf});
    2469         144 :             StateRefPoint.RefRayNHits({1, NReflSurf}, curWinEl) = TmpRfRyNH({1, NReflSurf});
    2470         144 :             StateRefPoint.HitSurfNo({1, MaxTotHits}, {1, NReflSurf}, curWinEl) = TmpHSurfNo({1, MaxTotHits}, {1, NReflSurf});
    2471         144 :             StateRefPoint.HitSurfDSq({1, MaxTotHits}, {1, NReflSurf}, curWinEl) = TmpHSurfDSq({1, MaxTotHits}, {1, NReflSurf});
    2472         144 :             StateRefPoint.HitPt({1, MaxTotHits}, {1, NReflSurf}, curWinEl) = TmpHitPt({1, MaxTotHits}, {1, NReflSurf});
    2473             :         } // do IY = 1, NWY
    2474             :     }     // do IX = 1, NWX
    2475          18 : }
    2476             : 
    2477          18 : void AllocateForCFSRefPointsState(
    2478             :     EnergyPlusData &state, DataBSDFWindow::BSDFRefPoints &StateRefPoint, int const NumOfWinEl, int const NBasis, int const NTrnBasis)
    2479             : {
    2480             :     // SUBROUTINE INFORMATION:
    2481             :     //       AUTHOR         Simon Vidanovic
    2482             :     //       DATE WRITTEN   June 2013
    2483             : 
    2484             :     // PURPOSE OF THIS SUBROUTINE:
    2485             :     // Memory allocation for complex fenestration systems reference points geometry
    2486             : 
    2487          18 :     if (!allocated(StateRefPoint.NSky)) {
    2488           2 :         StateRefPoint.NSky.allocate(NumOfWinEl);
    2489           2 :         StateRefPoint.NSky = 0;
    2490             :     }
    2491             : 
    2492          18 :     if (!allocated(StateRefPoint.SkyIndex)) {
    2493           2 :         StateRefPoint.SkyIndex.allocate(NBasis, NumOfWinEl);
    2494           2 :         StateRefPoint.SkyIndex = 0;
    2495             :     }
    2496             : 
    2497          18 :     if (!allocated(StateRefPoint.NGnd)) {
    2498           2 :         StateRefPoint.NGnd.allocate(NumOfWinEl);
    2499           2 :         StateRefPoint.NGnd = 0;
    2500             :     }
    2501             : 
    2502          18 :     if (!allocated(StateRefPoint.GndIndex)) {
    2503           2 :         StateRefPoint.GndIndex.allocate(NBasis, NumOfWinEl);
    2504           2 :         StateRefPoint.GndIndex = 0;
    2505             :     }
    2506             : 
    2507          18 :     if (!allocated(StateRefPoint.GndPt)) {
    2508           2 :         StateRefPoint.GndPt.allocate(NBasis, NumOfWinEl);
    2509           2 :         StateRefPoint.GndPt = Vector(0.0, 0.0, 0.0);
    2510             :     }
    2511             : 
    2512          18 :     if (!allocated(StateRefPoint.GndObstrMultiplier)) {
    2513           2 :         StateRefPoint.GndObstrMultiplier.allocate(NBasis, NumOfWinEl);
    2514           2 :         StateRefPoint.GndObstrMultiplier = 0.0;
    2515             :     }
    2516             : 
    2517          18 :     if (!allocated(StateRefPoint.NReflSurf)) {
    2518           2 :         StateRefPoint.NReflSurf.allocate(NumOfWinEl);
    2519           2 :         StateRefPoint.NReflSurf = 0;
    2520             :     }
    2521             : 
    2522          18 :     if (!allocated(StateRefPoint.RefSurfIndex)) {
    2523           2 :         StateRefPoint.RefSurfIndex.allocate(NBasis, NumOfWinEl);
    2524           2 :         StateRefPoint.RefSurfIndex = 0;
    2525             :     }
    2526             : 
    2527          18 :     if (!allocated(StateRefPoint.TransOutSurf)) {
    2528           2 :         StateRefPoint.TransOutSurf.allocate(NBasis, NumOfWinEl);
    2529           2 :         StateRefPoint.TransOutSurf = 1.0;
    2530             :     }
    2531             : 
    2532          18 :     if (!allocated(StateRefPoint.RefRayNHits)) {
    2533           2 :         StateRefPoint.RefRayNHits.allocate(NBasis, NumOfWinEl);
    2534           2 :         StateRefPoint.RefRayNHits = 0;
    2535             :     }
    2536             : 
    2537          18 :     if (!allocated(StateRefPoint.HitSurfNo)) {
    2538           2 :         StateRefPoint.HitSurfNo.allocate(state.dataSurface->TotSurfaces, NBasis, NumOfWinEl);
    2539           2 :         StateRefPoint.HitSurfNo = 0;
    2540             :     }
    2541             : 
    2542          18 :     if (!allocated(StateRefPoint.HitSurfDSq)) {
    2543           2 :         StateRefPoint.HitSurfDSq.allocate(state.dataSurface->TotSurfaces, NBasis, NumOfWinEl);
    2544           2 :         StateRefPoint.HitSurfDSq = 0.0;
    2545             :     }
    2546             : 
    2547          18 :     if (!allocated(StateRefPoint.HitPt)) {
    2548           2 :         StateRefPoint.HitPt.allocate(state.dataSurface->TotSurfaces, NBasis, NumOfWinEl);
    2549           2 :         StateRefPoint.HitPt = Vector(0.0, 0.0, 0.0);
    2550             :     }
    2551             : 
    2552          18 :     if (!allocated(StateRefPoint.RefPointIndex)) {
    2553           2 :         StateRefPoint.RefPointIndex.allocate(NumOfWinEl);
    2554           2 :         StateRefPoint.RefPointIndex = 0;
    2555             :     }
    2556             : 
    2557          18 :     if (!allocated(StateRefPoint.RefPointIntersection)) {
    2558           2 :         StateRefPoint.RefPointIntersection.allocate(NTrnBasis);
    2559           2 :         StateRefPoint.RefPointIntersection = false;
    2560             :     }
    2561             : 
    2562          18 :     if (!allocated(StateRefPoint.RefPtIntPosFac)) {
    2563           2 :         StateRefPoint.RefPtIntPosFac.allocate(NTrnBasis);
    2564           2 :         StateRefPoint.RefPtIntPosFac = 0.0;
    2565             :     }
    2566          18 : }
    2567             : 
    2568          18 : void AllocateForCFSRefPointsGeometry(DataBSDFWindow::BSDFRefPointsGeomDescr &RefPointsGeomDescr, int const NumOfWinEl)
    2569             : {
    2570             :     // SUBROUTINE INFORMATION:
    2571             :     //       AUTHOR         Simon Vidanovic
    2572             :     //       DATE WRITTEN   June 2013
    2573             : 
    2574             :     // PURPOSE OF THIS SUBROUTINE:
    2575             :     // Memory allocation for complex fenestration systems reference points geometry
    2576             : 
    2577             :     // SUBROUTINE LOCAL VARIABLES
    2578             : 
    2579          18 :     if (!allocated(RefPointsGeomDescr.SolidAngle)) {
    2580           2 :         RefPointsGeomDescr.SolidAngle.allocate(NumOfWinEl);
    2581           2 :         RefPointsGeomDescr.SolidAngle = 0.0;
    2582             :     }
    2583             : 
    2584          18 :     if (!allocated(RefPointsGeomDescr.SolidAngleVec)) {
    2585           2 :         RefPointsGeomDescr.SolidAngleVec.allocate(NumOfWinEl);
    2586           2 :         RefPointsGeomDescr.SolidAngleVec = Vector(0.0, 0.0, 0.0);
    2587             :     }
    2588          18 : }
    2589             : 
    2590         144 : void CFSRefPointSolidAngle(EnergyPlusData &state,
    2591             :                            Vector3<Real64> const &RefPoint,
    2592             :                            Vector3<Real64> const &RWin,
    2593             :                            Vector3<Real64> const &WNorm,
    2594             :                            DataBSDFWindow::BSDFRefPoints &RefPointMap,
    2595             :                            DataBSDFWindow::BSDFRefPointsGeomDescr &RefPointGeomMap,
    2596             :                            int const iWin,
    2597             :                            int const CurFenState,
    2598             :                            int const NTrnBasis,
    2599             :                            int const curWinEl,
    2600             :                            Real64 const WinElArea)
    2601             : {
    2602             :     // SUBROUTINE INFORMATION:
    2603             :     //       AUTHOR         Simon Vidanovic
    2604             :     //       DATE WRITTEN   June 2013
    2605             : 
    2606             :     // PURPOSE OF THIS SUBROUTINE:
    2607             :     // Calculate position factor for given reference point.
    2608             : 
    2609             :     // calculate vector from center of window element to the current reference point
    2610         144 :     Vector3<Real64> Ray = RefPoint - RWin;
    2611             : 
    2612             :     // figure out outgoing beam direction from current reference point
    2613         144 :     Real64 BestMatch = 0.0;
    2614       21024 :     for (int iTrnRay = 1; iTrnRay <= NTrnBasis; ++iTrnRay) {
    2615       20880 :         Vector3<Real64> const &V = state.dataBSDFWindow->ComplexWind(iWin).Geom(CurFenState).sTrn(iTrnRay);
    2616       20880 :         Real64 temp = dot(Ray, V);
    2617       20880 :         if (temp > BestMatch) {
    2618         702 :             BestMatch = temp;
    2619         702 :             RefPointMap.RefPointIndex(curWinEl) = iTrnRay;
    2620             :         }
    2621             :     }
    2622             : 
    2623             :     // calculate solid view angle
    2624         144 :     Real64 Dist = Ray.magnitude();
    2625         144 :     Vector3<Real64> RayNorm = Ray / (-Dist);
    2626         144 :     RefPointGeomMap.SolidAngleVec(curWinEl) = RayNorm;
    2627         144 :     Real64 CosB = dot(WNorm, RayNorm);
    2628         144 :     RefPointGeomMap.SolidAngle(curWinEl) = WinElArea * CosB / (Dist * Dist);
    2629         144 : }
    2630             : 
    2631          18 : void CFSRefPointPosFactor(EnergyPlusData &state,
    2632             :                           Vector3<Real64> const &RefPoint,
    2633             :                           DataBSDFWindow::BSDFRefPoints &RefPointMap,
    2634             :                           int const iWin,
    2635             :                           int const CurFenState,
    2636             :                           int const NTrnBasis,
    2637             :                           Real64 const AZVIEW)
    2638             : {
    2639             :     // SUBROUTINE INFORMATION:
    2640             :     //       AUTHOR         Simon Vidanovic
    2641             :     //       DATE WRITTEN   June 2013
    2642             : 
    2643             :     // PURPOSE OF THIS SUBROUTINE:
    2644             :     // Calculate position factor for given reference point.
    2645             : 
    2646          18 :     auto const &sTrn = state.dataBSDFWindow->ComplexWind(iWin).Geom(CurFenState).sTrn;
    2647        2628 :     for (int iTrnRay = 1; iTrnRay <= NTrnBasis; ++iTrnRay) {
    2648        2610 :         Vector3<Real64> V = sTrn(iTrnRay);
    2649        2610 :         V.negate();
    2650             : 
    2651        2610 :         Vector3<Real64> InterPoint;
    2652             : 
    2653        2610 :         bool hit = PierceSurface(state, iWin, RefPoint, V, InterPoint);
    2654        2610 :         if (hit) {
    2655         234 :             RefPointMap.RefPointIntersection(iTrnRay) = true;
    2656             : 
    2657         234 :             DataBSDFWindow::BSDFDaylghtPosition elPos = WindowComplexManager::DaylghtAltAndAzimuth(V);
    2658             : 
    2659         234 :             Real64 XR = std::tan(std::abs(Constant::PiOvr2 - AZVIEW - elPos.Azimuth) + 0.001);
    2660         234 :             Real64 YR = std::tan(elPos.Altitude + 0.001);
    2661         234 :             RefPointMap.RefPtIntPosFac(iTrnRay) = DayltgGlarePositionFactor(XR, YR);
    2662             :         }
    2663        2610 :     }
    2664          18 : } // CFSRefPointPosFactor()
    2665             : 
    2666       15516 : Real64 CalcObstrMultiplier(EnergyPlusData &state,
    2667             :                            Vector3<Real64> const &GroundHitPt, // Coordinates of point that ray hits ground (m)
    2668             :                            int const AltSteps,                 // Number of steps in altitude angle for solar reflection calc
    2669             :                            int const AzimSteps                 // Number of steps in azimuth angle of solar reflection calc
    2670             : )
    2671             : {
    2672             : 
    2673             :     // SUBROUTINE INFORMATION:
    2674             :     //       AUTHOR         Simon Vidanovic
    2675             :     //       DATE WRITTEN   April 2013, refactor from legacy code by Fred Winklemann
    2676             : 
    2677             :     // PURPOSE OF THIS SUBROUTINE:
    2678             :     // collect code to do obstruction multiplier from ground point
    2679             : 
    2680             :     // METHODOLOGY EMPLOYED:
    2681             :     // Send rays upward from hit point and see which ones are unobstructed and so go to sky.
    2682             :     // Divide hemisphere centered at ground hit point into elements of altitude Phi and
    2683             :     // azimuth Theta and create upward-going ground ray unit vector at each Phi,Theta pair.
    2684             :     // Phi = 0 at the horizon; Phi = Pi/2 at the zenith.
    2685             : 
    2686             :     // Locals
    2687       15516 :     auto &dl = state.dataDayltg;
    2688             : 
    2689             :     bool hitObs; // True iff obstruction is hit
    2690             : 
    2691       15516 :     Vector3<Real64> URay;     // Unit vector in (Phi,Theta) direction
    2692       15516 :     Vector3<Real64> ObsHitPt; // Unit vector in (Phi,Theta) direction
    2693             : 
    2694       15516 :     assert(AzimSteps <= DataSurfaces::AzimAngStepsForSolReflCalc);
    2695             : 
    2696       15516 :     Real64 DPhi = Constant::PiOvr2 / (AltSteps / 2.0); // Phi increment (radians)
    2697       15516 :     Real64 DTheta = Constant::Pi / AzimSteps;          // Theta increment (radians)
    2698             : 
    2699             :     // Tuned Precompute Phi trig table
    2700       15516 :     if (AltSteps != dl->AltSteps_last) {
    2701          18 :         for (int IPhi = 1, IPhi_end = (AltSteps / 2); IPhi <= IPhi_end; ++IPhi) {
    2702          15 :             Real64 Phi = (IPhi - 0.5) * DPhi;
    2703          15 :             dl->cos_Phi[IPhi] = std::cos(Phi);
    2704          15 :             dl->sin_Phi[IPhi] = std::sin(Phi);
    2705             :         }
    2706           3 :         dl->AltSteps_last = AltSteps;
    2707             :     }
    2708             :     // Tuned Precompute Theta trig table
    2709       15516 :     if (AzimSteps != dl->AzimSteps_last) {
    2710          57 :         for (int ITheta = 1; ITheta <= 2 * AzimSteps; ++ITheta) {
    2711          54 :             Real64 Theta = (ITheta - 0.5) * DTheta;
    2712          54 :             dl->cos_Theta[ITheta] = std::cos(Theta);
    2713          54 :             dl->sin_Theta[ITheta] = std::sin(Theta);
    2714             :         }
    2715           3 :         dl->AzimSteps_last = AzimSteps;
    2716             :     }
    2717             : 
    2718       15516 :     Real64 SkyGndObs = 0.0;   // Obstructed sky irradiance at a ground point
    2719       15516 :     Real64 SkyGndUnObs = 0.0; // Unobstructed sky irradiance at a ground point
    2720             : 
    2721             :     // Altitude loop
    2722       93096 :     for (int IPhi = 1, IPhi_end = (AltSteps / 2); IPhi <= IPhi_end; ++IPhi) {
    2723       77580 :         Real64 sinPhi = dl->sin_Phi[IPhi]; // sinPhi
    2724       77580 :         Real64 cosPhi = dl->cos_Phi[IPhi]; // cosPhi
    2725             : 
    2726             :         // Third component of ground ray unit vector in (Theta,Phi) direction
    2727       77580 :         URay.z = sinPhi;
    2728       77580 :         Real64 dOmegaGnd = cosPhi * DTheta * DPhi; // Solid angle element of ray from ground point (steradians)
    2729             :         // Cosine of angle of incidence of ground ray on ground plane
    2730       77580 :         Real64 CosIncAngURay = sinPhi;
    2731       77580 :         Real64 IncAngSolidAngFac = CosIncAngURay * dOmegaGnd / Constant::Pi; // CosIncAngURay*dOmegaGnd/Pi
    2732             :         // Azimuth loop
    2733     1474020 :         for (int ITheta = 1; ITheta <= 2 * AzimSteps; ++ITheta) {
    2734     1396440 :             URay.x = cosPhi * dl->cos_Theta[ITheta];
    2735     1396440 :             URay.y = cosPhi * dl->sin_Theta[ITheta];
    2736     1396440 :             SkyGndUnObs += IncAngSolidAngFac;
    2737             :             // Does this ground ray hit an obstruction?
    2738     1396440 :             hitObs = false;
    2739     1396440 :             if (state.dataSurface->TotSurfaces < octreeCrossover) { // Linear search through surfaces
    2740             : 
    2741     3919492 :                 for (int ObsSurfNum : state.dataSurface->AllShadowPossObstrSurfaceList) {
    2742     2876654 :                     hitObs = PierceSurface(state, ObsSurfNum, GroundHitPt, URay, ObsHitPt); // Check if ray pierces surface
    2743     2876654 :                     if (hitObs) break;
    2744     1396440 :                 }
    2745             : 
    2746             :             } else { // Surface octree search
    2747             : 
    2748             :                 // Lambda function for the octree to test for surface hit
    2749           0 :                 auto surfaceHit = [&GroundHitPt, &hitObs, &URay, &ObsHitPt](SurfaceData const &surface) -> bool {
    2750           0 :                     if (surface.IsShadowPossibleObstruction) {
    2751           0 :                         hitObs = PierceSurface(surface, GroundHitPt, URay, ObsHitPt); // Check if ray pierces surface
    2752           0 :                         return hitObs;
    2753             :                     } else {
    2754           0 :                         return false;
    2755             :                     }
    2756           0 :                 };
    2757             : 
    2758             :                 // Check octree surface candidates until a hit is found, if any
    2759           0 :                 Vector3<Real64> const URay_inv(SurfaceOctreeCube::safe_inverse(URay));
    2760           0 :                 state.dataHeatBalMgr->surfaceOctree.hasSurfaceRayIntersectsCube(GroundHitPt, URay, URay_inv, surfaceHit);
    2761           0 :             }
    2762             : 
    2763     1396440 :             if (hitObs) continue; // Obstruction hit
    2764             :             // Sky is hit
    2765     1042838 :             SkyGndObs += IncAngSolidAngFac;
    2766             :         } // End of azimuth loop
    2767             :     }     // End of altitude loop
    2768             : 
    2769             :     // in case ground point is surrounded by obstructions (SkyGndUnObs == 0), then multiplier will be equal to zero
    2770             :     // This should not happen anyway because in that case ray would not be able to reach ground point
    2771       31032 :     return (SkyGndUnObs != 0.0) ? (SkyGndObs / SkyGndUnObs) : 0.0;
    2772       15516 : } // CalcObstrMultiplier()
    2773             : 
    2774    67525824 : void FigureDayltgCoeffsAtPointsForSunPosition(
    2775             :     EnergyPlusData &state,
    2776             :     int const daylightCtrlNum, // Daylighting control index
    2777             :     int const iRefPoint,
    2778             :     int const iXelement,
    2779             :     int const NWX, // Number of window elements in x direction for dayltg calc
    2780             :     int const iYelement,
    2781             :     int const NWY,   // Number of window elements in y direction for dayltg calc
    2782             :     int const WinEl, // Current window element counter
    2783             :     int const IWin,
    2784             :     int const IWin2,
    2785             :     int const iHour,
    2786             :     int &ISunPos,
    2787             :     Real64 const SkyObstructionMult,
    2788             :     Vector3<Real64> const &RWIN2, // Center of a window element for TDD:DOME (if exists) in abs coord sys
    2789             :     Vector3<Real64> const &Ray,   // Unit vector along ray from reference point to window element
    2790             :     Real64 const PHRAY,           // Altitude of ray from reference point to window element (radians)
    2791             :     int const LSHCAL,             // Interior shade calculation flag:  0=not yet calculated, 1=already calculated
    2792             :     int const InShelfSurf,        // Inside daylighting shelf surface number
    2793             :     Real64 const COSB,            // Cosine of angle between window outward normal and ray from reference point to window element
    2794             :     Real64 const ObTrans,         // Product of solar transmittances of exterior obstructions hit by ray from reference point through a window element
    2795             :     Real64 const TVISB,           // Visible transmittance of window for COSB angle of incidence (times light well efficiency, if appropriate)
    2796             :     Real64 const DOMEGA,          // Solid angle subtended by window element wrt reference point (steradians)
    2797             :     int const ICtrl,              // Window control counter
    2798             :     WinShadingType const ShType,  // Window shading type
    2799             :     int const BlNum,              // Window blind number
    2800             :     Real64 const THRAY,           // Azimuth of ray from reference point to window element (radians)
    2801             :     Vector3<Real64> const &WNORM2, // Unit vector normal to window
    2802             :     ExtWinType const extWinType,   // Exterior window type (InZoneExtWin, AdjZoneExtWin, NotInOrAdjZoneExtWin)
    2803             :     int const IConst,              // Construction counter
    2804             :     Real64 const AZVIEW,           // Azimuth of view vector in absolute coord system for glare calculation (radians)
    2805             :     Vector3<Real64> const &RREF2,  // Location of virtual reference point in absolute coordinate system
    2806             :     bool const hitIntObs,          // True iff interior obstruction hit
    2807             :     bool const hitExtObs,          // True iff ray from ref pt to ext win hits an exterior obstruction
    2808             :     CalledFor const CalledFrom,    // indicate  which type of routine called this routine
    2809             :     Real64 TVISIntWin,             // Visible transmittance of int win at COSBIntWin for light from ext win
    2810             :     Real64 &TVISIntWinDisk,        // Visible transmittance of int win at COSBIntWin for sun
    2811             :     int const MapNum)
    2812             : {
    2813             : 
    2814             :     // SUBROUTINE INFORMATION:
    2815             :     //       AUTHOR         B. Griffith
    2816             :     //       DATE WRITTEN   November 2012, refactor from legacy code by Fred Winklemann
    2817             : 
    2818             :     // PURPOSE OF THIS SUBROUTINE:
    2819             :     // collect code for calculations sun position aspects for daylighting coefficients
    2820             : 
    2821             :     // METHODOLOGY EMPLOYED:
    2822             :     // switch as need to serve both reference points and map points based on calledFrom
    2823             : 
    2824    67917672 :     if (state.dataSurface->SurfSunCosHourly(iHour).z < DataEnvironment::SunIsUpValue) return;
    2825             : 
    2826    33061172 :     auto &dl = state.dataDayltg;
    2827             : 
    2828             :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    2829    33061172 :     Vector3<Real64> RREF{0.0, 0.0, 0.0}; // Location of a reference point in absolute coordinate system //Autodesk Was used uninitialized:
    2830             : 
    2831             :     Real64 ObTransDisk;     // Product of solar transmittances of exterior obstructions hit by ray from reference point to sun
    2832             :     Real64 LumAtHitPtFrSun; // Luminance at hit point of obstruction by reflection of direct light from sun (cd/m2)
    2833             : 
    2834             :     Real64 TVISS; // Direct solar visible transmittance of window at given angle of incidence
    2835             :     //  (times light well efficiency, if appropriate)
    2836             :     Real64 XAVWL; // XAVWL*TVISS is contribution of window luminance from solar disk (cd/m2)
    2837             : 
    2838             :     bool hitObs;          // True iff obstruction is hit
    2839             :     Real64 ObsVisRefl;    // Visible reflectance of obstruction
    2840             :     Real64 SkyReflVisLum; // Reflected sky luminance at hit point divided by
    2841             : 
    2842             :     Real64 SpecReflectance; // Specular reflectance of a reflecting surface
    2843             :     Real64 TVisRefl;        // Bare window vis trans for reflected beam
    2844             :     //  (times light well efficiency, if appropriate)
    2845             :     Real64 PHSUNrefl; // Altitude angle of reflected sun (radians)
    2846             :     Real64 THSUNrefl; // Azimuth anggle of reflected sun (radians)
    2847             : 
    2848             :     Real64 COSBIntWin; // Cos of angle between int win outward normal and ray betw ref pt and
    2849             :     //  exterior window element or between ref pt and sun
    2850             :     Real64 TVisIntWinMult;     // Interior window vis trans multiplier for ext win in adjacent zone
    2851             :     Real64 TVisIntWinDiskMult; // Interior window vis trans solar disk multiplier for ext win in adj zone
    2852             :     Real64 WindowSolidAngleDaylightPoint;
    2853             : 
    2854    33061172 :     ++ISunPos;
    2855             : 
    2856             :     // Altitude of sun (degrees)
    2857    33061172 :     dl->sunAngles = dl->sunAnglesHr[iHour];
    2858             : 
    2859             :     // First time through, call routine to calculate inter-reflected illuminance
    2860             :     // at reference point and luminance of window with shade, screen or blind.
    2861             : 
    2862             :     // Rob/TH - Not sure whether this call is necessary for interior zones with interior windows only.
    2863             :     //  new code would be -
    2864             :     // IF (LSHCAL == 1 .AND. ExtWinType /= AdjZoneExtWin) CALL DayltgInterReflectedIllum(ISunPos,IHR,ZoneNum,IWin2)
    2865    33061172 :     int enclNum = 0; // enclosure index
    2866    33061172 :     int zoneNum = 0; // zone index
    2867    33061172 :     if (CalledFrom == CalledFor::RefPoint) {
    2868     6276188 :         zoneNum = dl->daylightControl(daylightCtrlNum).zoneIndex;
    2869     6276188 :         enclNum = dl->daylightControl(daylightCtrlNum).enclIndex;
    2870    26784984 :     } else if (CalledFrom == CalledFor::MapPoint) {
    2871    26784984 :         assert(MapNum > 0);
    2872    26784984 :         zoneNum = dl->illumMaps(MapNum).zoneIndex;
    2873    26784984 :         enclNum = dl->illumMaps(MapNum).enclIndex;
    2874             :     }
    2875    33061172 :     if (state.dataSurface->SurfWinWindowModelType(IWin) != WindowModel::BSDF) {
    2876    33059684 :         if (LSHCAL == 1) DayltgInterReflectedIllum(state, ISunPos, iHour, enclNum, IWin2);
    2877             :     } else {
    2878        1488 :         if (LSHCAL == 1) DayltgInterReflectedIllumComplexFenestration(state, IWin2, WinEl, iHour, daylightCtrlNum, iRefPoint, CalledFrom, MapNum);
    2879        1488 :         if (COSB <= 0.0) return;
    2880        1488 :         DayltgDirectIllumComplexFenestration(state, IWin, WinEl, iHour, iRefPoint, CalledFrom, MapNum);
    2881             :         // Call direct sun component only once since calculation is done for entire window
    2882        1488 :         if (WinEl == (NWX * NWY)) {
    2883         186 :             DayltgDirectSunDiskComplexFenestration(state, IWin2, iHour, iRefPoint, WinEl, AZVIEW, CalledFrom, MapNum);
    2884             :         }
    2885        1488 :         return;
    2886             :     }
    2887             : 
    2888             :     // Daylighting shelf simplification:  The shelf completely blocks all view of the window,
    2889             :     // only interrelflected illumination is allowed (see DayltgInterReflectedIllum above).
    2890             :     // Everything else in this loop has to do with direct luminance from the window.
    2891    33059684 :     if (InShelfSurf > 0) return;
    2892             : 
    2893    32669324 :     if (COSB <= 0.0) return;
    2894             : 
    2895    32669324 :     auto &surfWin = state.dataSurface->SurfaceWindow(IWin);
    2896             : 
    2897    32669324 :     Illums XDirIllum;
    2898    32669324 :     Illums XAvgWinLum;
    2899    32669324 :     Real64 const Ray_3 = Ray.z;
    2900    32669324 :     Real64 const DOMEGA_Ray_3 = DOMEGA * Ray_3;
    2901             : 
    2902             :     // Add contribution of this window element to glare and to
    2903             :     // direct illuminance at reference point
    2904             : 
    2905             :     // The I,J,K indices for sky and sun components of direct illuminance
    2906             :     // (EDIRSK, EDIRSU) and average window luminance (AVWLSK, AVWLSU) are:
    2907             :     // I=1 for clear sky, =2 Clear turbid, =3 Intermediate, =4 Overcast;
    2908             :     // J=1 for bare window, =2 for window with shade or fixed slat-angle blind;
    2909             :     //  = 2,3,...,MaxSlatAngs+1 for window with variable slat-angle blind;
    2910             :     // K = sun position index.
    2911             : 
    2912             :     // ----- CASE I -- BARE WINDOW (no shading device)
    2913             : 
    2914             :     // Beam solar and sky solar reflected from nearest obstruction.
    2915             :     // In the following hitIntObs == false  ==> no interior obstructions hit, and
    2916             :     //                  hitExtObs == true  ==> one or more exterior obstructions hit.
    2917    32669324 :     if (state.dataSurface->CalcSolRefl && !hitIntObs && hitExtObs) {
    2918             :         int NearestHitSurfNum;        // Surface number of nearest obstruction
    2919           0 :         Vector3<Real64> NearestHitPt; // Hit point of ray on nearest obstruction
    2920             :         // One or more exterior obstructions was hit; get contribution of reflection
    2921             :         // from nearest obstruction.
    2922             :         // Find obstruction whose hit point is closest to this ray's window element
    2923           0 :         DayltgClosestObstruction(state, RWIN2, Ray, NearestHitSurfNum, NearestHitPt);
    2924           0 :         if (NearestHitSurfNum > 0) {
    2925             : 
    2926             :             // Beam solar reflected from nearest obstruction
    2927             : 
    2928           0 :             LumAtHitPtFrSun = DayltgSurfaceLumFromSun(state, iHour, Ray, NearestHitSurfNum, NearestHitPt);
    2929           0 :             dl->avgWinLum(iHour, 1).sun += LumAtHitPtFrSun * TVISB;
    2930           0 :             if (PHRAY >= 0.0) dl->dirIllum(iHour, 1).sun += LumAtHitPtFrSun * DOMEGA_Ray_3 * TVISB;
    2931             : 
    2932             :             // Sky solar reflected from nearest obstruction
    2933             : 
    2934           0 :             int const ObsConstrNum = state.dataSurface->SurfActiveConstruction(NearestHitSurfNum);
    2935           0 :             if (ObsConstrNum > 0) {
    2936             :                 // Exterior building surface is nearest hit
    2937           0 :                 if (!state.dataConstruction->Construct(ObsConstrNum).TypeIsWindow) {
    2938             :                     // Obstruction is not a window, i.e., is an opaque surface
    2939           0 :                     ObsVisRefl = 1.0 - dynamic_cast<const Material::MaterialChild *>(
    2940           0 :                                            state.dataMaterial->Material(state.dataConstruction->Construct(ObsConstrNum).LayerPoint(1)))
    2941           0 :                                            ->AbsorpVisible;
    2942             :                 } else {
    2943             :                     // Obstruction is a window; assume it is bare
    2944           0 :                     ObsVisRefl = state.dataConstruction->Construct(ObsConstrNum).ReflectVisDiffFront;
    2945             :                 }
    2946             :             } else {
    2947             :                 // Shadowing surface is nearest hit
    2948           0 :                 if (state.dataSurface->SurfDaylightingShelfInd(NearestHitSurfNum) > 0) {
    2949             :                     // This is a daylighting shelf, for which reflection is separately calculated
    2950           0 :                     ObsVisRefl = 0.0;
    2951             :                 } else {
    2952           0 :                     ObsVisRefl = state.dataSurface->SurfShadowDiffuseVisRefl(NearestHitSurfNum);
    2953           0 :                     if (state.dataSurface->SurfShadowGlazingConstruct(NearestHitSurfNum) > 0)
    2954           0 :                         ObsVisRefl +=
    2955           0 :                             state.dataSurface->SurfShadowGlazingFrac(NearestHitSurfNum) *
    2956           0 :                             state.dataConstruction->Construct(state.dataSurface->SurfShadowGlazingConstruct(NearestHitSurfNum)).ReflectVisDiffFront;
    2957             :                 }
    2958             :             }
    2959             :             // Surface number to use when obstruction is a shadowing surface
    2960           0 :             int NearestHitSurfNumX = NearestHitSurfNum;
    2961             :             // Each shadowing surface has a "mirror" duplicate surface facing in the opposite direction.
    2962             :             // The following gets the correct side of a shadowing surface for reflection.
    2963           0 :             if (state.dataSurface->Surface(NearestHitSurfNum).IsShadowing) {
    2964           0 :                 if (dot(Ray, state.dataSurface->Surface(NearestHitSurfNum).OutNormVec) > 0.0) NearestHitSurfNumX = NearestHitSurfNum + 1;
    2965             :             }
    2966           0 :             if (!state.dataSysVars->DetailedSkyDiffuseAlgorithm || !state.dataSurface->ShadingTransmittanceVaries ||
    2967           0 :                 state.dataHeatBal->SolarDistribution == DataHeatBalance::Shadowing::Minimal) {
    2968           0 :                 SkyReflVisLum = ObsVisRefl * state.dataSurface->Surface(NearestHitSurfNumX).ViewFactorSky *
    2969           0 :                                 state.dataSolarShading->SurfDifShdgRatioIsoSky(NearestHitSurfNumX) / Constant::Pi;
    2970             :             } else {
    2971           0 :                 SkyReflVisLum = ObsVisRefl * state.dataSurface->Surface(NearestHitSurfNumX).ViewFactorSky *
    2972           0 :                                 state.dataSolarShading->SurfDifShdgRatioIsoSkyHRTS(1, iHour, NearestHitSurfNumX) / Constant::Pi;
    2973             :             }
    2974           0 :             assert(equal_dimensions(dl->avgWinLum, dl->dirIllum));
    2975           0 :             auto &gilsk = dl->horIllum[iHour];
    2976           0 :             auto &avwlsk = dl->avgWinLum(iHour, 1);
    2977           0 :             auto &edirsk = dl->dirIllum(iHour, 1);
    2978             : 
    2979           0 :             for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
    2980           0 :                 XAvgWinLum.sky[iSky] = gilsk.sky[iSky] * SkyReflVisLum;
    2981           0 :                 avwlsk.sky[iSky] += XAvgWinLum.sky[iSky] * TVISB;
    2982           0 :                 if (PHRAY >= 0.0) {
    2983           0 :                     XDirIllum.sky[iSky] = gilsk.sky[iSky] * SkyReflVisLum * DOMEGA_Ray_3;
    2984           0 :                     edirsk.sky[iSky] += XDirIllum.sky[iSky] * TVISB;
    2985             :                 }
    2986             :             }
    2987             :         }
    2988           0 :     } // End of check if solar reflection calculation is in effect
    2989             : 
    2990    32669324 :     if (ObTrans > 1.e-6) {
    2991             :         // Ray did not hit an obstruction or the transmittance product of hit obstructions is non-zero.
    2992             :         // Contribution of sky or ground luminance in cd/m2
    2993    31751288 :         if (state.dataSurface->Surface(IWin).OriginalClass == SurfaceClass::TDD_Diffuser) {
    2994             :             // Make all transmitted light diffuse for a TDD with a bare diffuser
    2995        2448 :             assert(equal_dimensions(dl->avgWinLum, dl->winLum));
    2996        2448 :             assert(equal_dimensions(dl->avgWinLum, dl->dirIllum));
    2997        2448 :             auto &avwlsk = dl->avgWinLum(iHour, 1);
    2998        2448 :             auto &edirsk = dl->dirIllum(iHour, 1);
    2999        2448 :             auto &wlumsk = dl->winLum(iHour, 1);
    3000       12240 :             for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
    3001        9792 :                 avwlsk.sky[iSky] += wlumsk.sky[iSky];
    3002        9792 :                 if (PHRAY > 0.0) edirsk.sky[iSky] += wlumsk.sky[iSky] * DOMEGA_Ray_3;
    3003             :             }
    3004             : 
    3005        2448 :             dl->avgWinLum(iHour, 1).sun += dl->winLum(iHour, 1).sun;
    3006        2448 :             dl->avgWinLum(iHour, 1).sunDisk += dl->winLum(iHour, 1).sunDisk;
    3007             : 
    3008        2448 :             if (PHRAY > 0.0) dl->dirIllum(iHour, 1).sun += dl->winLum(iHour, 1).sun * DOMEGA_Ray_3;
    3009             :         } else {                         // Bare window
    3010    31748840 :             Vector3<Real64> GroundHitPt; // Coordinates of point that ray hits ground (m)
    3011             :             // Tuned Hoisted operations out of loop and linear indexing
    3012    31748840 :             if (state.dataSurface->CalcSolRefl) { // Coordinates of ground point hit by the ray
    3013        7264 :                 Real64 Alfa = std::acos(-Ray_3);
    3014        7264 :                 Real64 const Ray_1(Ray.x);
    3015        7264 :                 Real64 const Ray_2(Ray.y);
    3016             :                 //                    Beta = std::atan2( Ray_2, Ray_1 ); //Unused Tuning below eliminated use
    3017             :                 // Distance between ground hit point and proj'n of center of window element onto ground (m)
    3018        7264 :                 Real64 HorDis = (RWIN2.z - state.dataSurface->GroundLevelZ) * std::tan(Alfa);
    3019        7264 :                 GroundHitPt.z = state.dataSurface->GroundLevelZ;
    3020             :                 // Tuned Replaced by below: sqrt is faster than sincos
    3021             :                 //                    GroundHitPt( 1 ) = RWIN2( 1 ) + HorDis * std::cos( Beta );
    3022             :                 //                    GroundHitPt( 2 ) = RWIN2( 2 ) + HorDis * std::sin( Beta );
    3023        7264 :                 Real64 const Ray_r(std::sqrt(square(Ray_1) + square(Ray_2)));
    3024        7264 :                 if (Ray_r > 0.0) {
    3025        7264 :                     HorDis /= Ray_r;
    3026        7264 :                     GroundHitPt.x = RWIN2.x + HorDis * Ray_1;
    3027        7264 :                     GroundHitPt.y = RWIN2.y + HorDis * Ray_2;
    3028             :                 } else { // Treat as angle==0
    3029           0 :                     GroundHitPt.x = RWIN2.x + HorDis;
    3030           0 :                     GroundHitPt.y = RWIN2.y;
    3031             :                 }
    3032             :             }
    3033    31748840 :             Real64 const GILSK_mult((state.dataEnvrn->GndReflectanceForDayltg / Constant::Pi) * ObTrans * SkyObstructionMult);
    3034    31748840 :             Real64 const TVISB_ObTrans(TVISB * ObTrans);
    3035    31748840 :             Real64 const AVWLSU_add(TVISB_ObTrans * dl->horIllum[iHour].sun * (state.dataEnvrn->GndReflectanceForDayltg / Constant::Pi));
    3036    31748840 :             Vector3<Real64> const SUNCOS_iHour(state.dataSurface->SurfSunCosHourly(iHour));
    3037    31748840 :             assert(equal_dimensions(dl->dirIllum, dl->avgWinLum));
    3038    31748840 :             auto &edirsk = dl->dirIllum(iHour, 1);
    3039    31748840 :             auto &avwlsk = dl->avgWinLum(iHour, 1);
    3040             : 
    3041   158744200 :             for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
    3042   126995360 :                 if (PHRAY > 0.0) {                                                                     // Ray heads upward to sky
    3043   125938612 :                     Real64 ELUM = DayltgSkyLuminance(state, static_cast<SkyType>(iSky), THRAY, PHRAY); // Sky or ground luminance (cd/m2)
    3044   125938612 :                     XDirIllum.sky[iSky] = ELUM * DOMEGA_Ray_3;
    3045   125938612 :                     Real64 DEDIR = XDirIllum.sky[iSky] * TVISB; // Illuminance contribution at reference point from window element (lux)
    3046   125938612 :                     edirsk.sky[iSky] += DEDIR * ObTrans;
    3047   125938612 :                     avwlsk.sky[iSky] += ELUM * TVISB_ObTrans;
    3048   125938612 :                     XAvgWinLum.sky[iSky] = ELUM * ObTrans;
    3049             :                 } else { // PHRAY <= 0.
    3050             :                     // Ray heads downward to ground.
    3051             :                     // Contribution from sky diffuse reflected from ground
    3052     1056748 :                     XAvgWinLum.sky[iSky] = dl->horIllum[iHour].sky[iSky] * GILSK_mult;
    3053     1056748 :                     avwlsk.sky[iSky] += TVISB * XAvgWinLum.sky[iSky];
    3054             :                     // Contribution from beam solar reflected from ground (beam reaching ground point
    3055             :                     // can be obstructed [SunObstructionMult < 1.0] if CalcSolRefl = .TRUE.)
    3056             :                 } // End of check if ray is going up or down
    3057             :             }     // for (iSky)
    3058             : 
    3059    31748840 :             if (PHRAY <= 0.0) {
    3060             :                 // SunObstructionMult = 1.0; //Tuned
    3061      264187 :                 if (state.dataSurface->CalcSolRefl) { // Coordinates of ground point hit by the ray
    3062             :                     // Sun reaches ground point if vector from this point to the sun is unobstructed
    3063        2270 :                     hitObs = false;
    3064        2270 :                     Vector3<Real64> ObsHitPt; // Coordinates of hit point on an obstruction (m)
    3065       10957 :                     for (int ObsSurfNum : state.dataSurface->AllShadowPossObstrSurfaceList) {
    3066        8912 :                         hitObs = PierceSurface(state, ObsSurfNum, GroundHitPt, SUNCOS_iHour, ObsHitPt);
    3067        8912 :                         if (hitObs) break;
    3068        2270 :                     }
    3069             :                     // if ( hitObs ) SunObstructionMult = 0.0;
    3070        2270 :                     if (!hitObs) dl->avgWinLum(iHour, 1).sun += AVWLSU_add;
    3071        2270 :                 } else {
    3072      261917 :                     dl->avgWinLum(iHour, 1).sun += AVWLSU_add;
    3073             :                 }
    3074             :             } // (PHRAY <= 0.0)
    3075    31748840 :         }
    3076             :     } // End of check if bare window or TDD:DIFFUSER
    3077             : 
    3078             :     // Illuminance from beam solar (without interior reflection)
    3079             :     // Just run this once on the last pass
    3080    32669324 :     if (iXelement == NWX && iYelement == NWY) { // Last pass
    3081             : 
    3082             :         // Beam solar reaching reference point directly without exterior reflection
    3083             : 
    3084             :         // Unit vector from ref. pt. to sun
    3085      322356 :         Vector3<Real64> RAYCOS;
    3086      322356 :         RAYCOS.x = dl->sunAngles.cosPhi * std::cos(dl->sunAngles.theta);
    3087      322356 :         RAYCOS.y = dl->sunAngles.cosPhi * std::sin(dl->sunAngles.theta);
    3088      322356 :         RAYCOS.z = dl->sunAngles.sinPhi;
    3089             : 
    3090             :         // Is sun on front side of exterior window?
    3091      322356 :         Real64 COSI = dot(WNORM2, RAYCOS); // Cosine of angle between direct sun and window outward normal
    3092             :         bool hit;                          // True if ray from ref point thru window element hits an obstruction
    3093             :         bool hitWin;                       // True if ray passes thru window
    3094      322356 :         Vector3<Real64> HP;
    3095      322356 :         if (COSI > 0.0) {
    3096             : 
    3097             :             // Does RAYCOS pass thru exterior window? HP is point that RAYCOS intersects window plane.
    3098      243067 :             hitWin = PierceSurface(state, IWin2, RREF2, RAYCOS, HP);
    3099             :             // True if ray from ref pt to sun hits an interior obstruction
    3100      243067 :             if (hitWin) {
    3101        8571 :                 bool hitIntObsDisk = false;
    3102        8571 :                 if (extWinType == ExtWinType::InZone) {
    3103             :                     // Check for interior obstructions between reference point and HP.
    3104        8345 :                     hitIntObsDisk = DayltgHitInteriorObstruction(state, IWin2, RREF2, HP);
    3105             :                 }
    3106        8571 :                 ObTransDisk = 0.0; // Init value
    3107             :                 // Init flag for vector from RP to sun passing through interior window
    3108        8571 :                 bool hitIntWinDisk = false;
    3109        8571 :                 if (extWinType == ExtWinType::AdjZone) { // This block is for RPs in zones with interior windows
    3110             :                     // adjacent to zones with exterior windows
    3111             :                     // Does RAYCOS pass through interior window in zone containing RP?
    3112             :                     // Loop over zone surfaces looking for interior windows between reference point and sun
    3113             :                     // Surface number of int window intersected by ray betw ref pt and sun
    3114             :                     int IntWinDiskHitNum;
    3115             :                     // Intersection point on an interior window for ray from ref pt to sun (m)
    3116         226 :                     Vector3<Real64> HitPtIntWinDisk;
    3117         226 :                     auto const &thisZone = state.dataHeatBal->Zone(zoneNum);
    3118         452 :                     for (int const spaceNum : thisZone.spaceIndexes) {
    3119         226 :                         auto const &thisSpace = state.dataHeatBal->space(spaceNum);
    3120         452 :                         for (int IntWinDisk = thisSpace.WindowSurfaceFirst, IntWinDisk_end = thisSpace.WindowSurfaceLast;
    3121         452 :                              IntWinDisk <= IntWinDisk_end;
    3122             :                              ++IntWinDisk) {
    3123         226 :                             auto const &surfIntWinDisk = state.dataSurface->Surface(IntWinDisk);
    3124         226 :                             if (surfIntWinDisk.ExtBoundCond < 1) continue;
    3125             : 
    3126         226 :                             if (state.dataSurface->Surface(surfIntWinDisk.ExtBoundCond).Zone != state.dataSurface->Surface(IWin2).Zone) continue;
    3127             : 
    3128         226 :                             hitIntWinDisk = PierceSurface(state, IntWinDisk, RREF, RAYCOS, HitPtIntWinDisk);
    3129         226 :                             if (!hitIntWinDisk) continue;
    3130             : 
    3131           0 :                             IntWinDiskHitNum = IntWinDisk;
    3132           0 :                             COSBIntWin = dot(surfIntWinDisk.OutNormVec, RAYCOS);
    3133           0 :                             if (COSBIntWin <= 0.0) {
    3134           0 :                                 hitIntWinDisk = false;
    3135           0 :                                 IntWinDiskHitNum = 0;
    3136           0 :                                 continue;
    3137             :                             }
    3138           0 :                             TVISIntWinDisk =
    3139           0 :                                 General::POLYF(COSBIntWin, state.dataConstruction->Construct(surfIntWinDisk.Construction).TransVisBeamCoef);
    3140           0 :                             break;
    3141             :                         } // for (IntWinDisk)
    3142         226 :                     }     // for (spaceNum)
    3143             : 
    3144         226 :                     if (!hitIntWinDisk) { // Vector from RP to sun does not pass through interior window
    3145         226 :                         ObTransDisk = 0.0;
    3146         226 :                         hit = true; //! fcw Is this needed?
    3147             :                     }
    3148             : 
    3149             :                     // Check for interior obstructions between ref point and interior window
    3150         226 :                     hitIntObsDisk = false;
    3151         226 :                     if (hitIntWinDisk) {
    3152           0 :                         hitIntObsDisk = DayltgHitInteriorObstruction(state, IntWinDiskHitNum, RREF, HitPtIntWinDisk);
    3153             :                         // If no obstruction between RP and hit int win, check for obstruction
    3154             :                         // between int win and ext win
    3155           0 :                         if (!hitIntObsDisk) {
    3156           0 :                             hitIntObsDisk = DayltgHitBetWinObstruction(state, IntWinDiskHitNum, IWin2, HitPtIntWinDisk, HP);
    3157             :                         }
    3158             :                     }
    3159         226 :                     if (hitIntObsDisk) ObTransDisk = 0.0;
    3160         226 :                 } // case where RP is in zone with interior window adjacent to zone with exterior window
    3161             : 
    3162             :                 //                    hitExtObsDisk = false; //Unused Set but never used
    3163             :                 // RJH 08-25-07 hitIntWinDisk should not be reset to false here, and should be tested below.
    3164             :                 // This is to correct logic flaw causing direct solar to reach adjacent zone refpt
    3165             :                 // when vector to sun does not pass through interior window
    3166             :                 // hitIntWinDisk = false
    3167        8571 :                 if (!hitIntObsDisk) { // No interior obstruction was hit
    3168             :                     // Net transmittance of exterior obstructions encountered by RAYCOS
    3169             :                     // ObTransDisk = 1.0 will be returned if no exterior obstructions are hit.
    3170        8567 :                     ObTransDisk = DayltgHitObstruction(state, iHour, IWin2, RREF2, RAYCOS);
    3171             :                     //                        if ( ObTransDisk < 1.0 ) hitExtObsDisk = true; //Unused Set but never used
    3172             :                     // RJH 08-26-07 However, if this is a case of interior window
    3173             :                     // and vector to sun does not pass through interior window
    3174             :                     // then reset ObTransDisk to 0.0 since it is the key test for adding
    3175             :                     // contribution of sun to RP below.
    3176        8567 :                     if ((extWinType == ExtWinType::AdjZone) && (!hitIntWinDisk)) {
    3177         226 :                         ObTransDisk = 0.0;
    3178             :                     }
    3179             :                 }
    3180             : 
    3181             :                 // PETER: need side wall mounted TDD to test this
    3182             :                 // PETER: probably need to replace RREF2 with RWIN2
    3183             :                 // PETER: need to check for interior obstructions too.
    3184             : 
    3185        8571 :                 if (ObTransDisk > 1.e-6) {
    3186             : 
    3187             :                     // Sun reaches reference point;  increment illuminance.
    3188             :                     // Direct normal illuminance is normalized to 1.0
    3189             : 
    3190        7969 :                     if (state.dataSurface->Surface(IWin).OriginalClass == SurfaceClass::TDD_Diffuser) {
    3191             :                         // No beam is transmitted.  Takes care of TDD with a bare diffuser and all types of blinds.
    3192           0 :                         TVISS = 0.0;
    3193             :                     } else {
    3194             :                         // Beam transmittance for bare window and all types of blinds
    3195        7969 :                         TVISS = General::POLYF(COSI, state.dataConstruction->Construct(IConst).TransVisBeamCoef) * surfWin.glazedFrac *
    3196        7969 :                                 surfWin.lightWellEff;
    3197        7969 :                         if (extWinType == ExtWinType::AdjZone && hitIntWinDisk) TVISS *= TVISIntWinDisk;
    3198             :                     }
    3199             : 
    3200        7969 :                     dl->dirIllum(iHour, 1).sunDisk = RAYCOS.z * TVISS * ObTransDisk; // Bare window
    3201             : 
    3202             :                     std::array<Real64, (int)Material::MaxSlatAngs + 1> transBmBmMult;
    3203        7969 :                     std::fill(transBmBmMult.begin(), transBmBmMult.end(), 0.0);
    3204             : 
    3205        7969 :                     if (ANY_BLIND(ShType)) {
    3206           0 :                         auto const &blind = state.dataMaterial->Blind(BlNum);
    3207             : 
    3208           0 :                         Real64 ProfAng = ProfileAngle(state, IWin, RAYCOS, blind.SlatOrientation);
    3209             :                         // Contribution of beam passing through slats and reaching reference point
    3210           0 :                         for (int JB = 1; JB <= Material::MaxSlatAngs; ++JB) {
    3211             :                             // IF (.NOT.SurfaceWindow(IWin)%MovableSlats .AND. JB > 1) EXIT
    3212           0 :                             Real64 SlatAng = (state.dataSurface->SurfWinMovableSlats(IWin)) ? ((JB - 1) * Constant::Pi / (Material::MaxSlatAngs - 1))
    3213           0 :                                                                                             : (blind.SlatAngle * Constant::DegToRadians);
    3214           0 :                             transBmBmMult[JB] =
    3215           0 :                                 Window::BlindBeamBeamTrans(ProfAng, SlatAng, blind.SlatWidth, blind.SlatSeparation, blind.SlatThickness);
    3216           0 :                             dl->dirIllum(iHour, JB + 1).sunDisk = RAYCOS.z * TVISS * transBmBmMult[JB] * ObTransDisk;
    3217             : 
    3218             :                             // do this only once for fixed slat blinds
    3219           0 :                             if (!state.dataSurface->SurfWinMovableSlats(IWin)) break;
    3220             :                         }
    3221        7969 :                     } else if (ShType == WinShadingType::ExtScreen) {
    3222             :                         //                          pass angle from sun to window normal here using PHSUN and THSUN from above and surface angles
    3223             :                         //                          SunAltitudeToWindowNormalAngle = PHSUN - SurfaceWindow(IWin)%Phi
    3224             :                         //                          SunAzimuthToWindowNormalAngle = THSUN - SurfaceWindow(IWin)%Theta
    3225           0 :                         auto const *screen = dynamic_cast<Material::MaterialScreen *>(state.dataMaterial->Material(surfWin.screenNum));
    3226           0 :                         assert(screen != nullptr);
    3227             : 
    3228           0 :                         Real64 phi = std::abs(dl->sunAngles.phi - surfWin.phi);
    3229           0 :                         Real64 theta = std::abs(dl->sunAngles.theta - surfWin.theta);
    3230             :                         int ip1, ip2, it1, it2;
    3231             :                         General::BilinearInterpCoeffs coeffs;
    3232           0 :                         Material::NormalizePhiTheta(phi, theta);
    3233           0 :                         Material::GetPhiThetaIndices(phi, theta, screen->dPhi, screen->dTheta, ip1, ip2, it1, it2);
    3234           0 :                         GetBilinearInterpCoeffs(
    3235           0 :                             phi, theta, ip1 * screen->dPhi, ip2 * screen->dPhi, it1 * screen->dTheta, it2 * screen->dTheta, coeffs);
    3236           0 :                         transBmBmMult[1] = BilinearInterp(screen->btars[ip1][it1].BmTrans,
    3237           0 :                                                           screen->btars[ip1][it2].BmTrans,
    3238           0 :                                                           screen->btars[ip2][it1].BmTrans,
    3239           0 :                                                           screen->btars[ip2][it2].BmTrans,
    3240             :                                                           coeffs);
    3241             : 
    3242           0 :                         dl->dirIllum(iHour, 2).sunDisk = RAYCOS.z * TVISS * transBmBmMult[1] * ObTransDisk;
    3243             :                     }
    3244             : 
    3245        7969 :                     if (CalledFrom == CalledFor::RefPoint) {
    3246             :                         // Glare from solar disk
    3247             : 
    3248             :                         // Position factor for sun (note that AZVIEW is wrt y-axis and THSUN is wrt
    3249             :                         // x-axis of absolute coordinate system.
    3250        5981 :                         Real64 XR = std::tan(std::abs(Constant::PiOvr2 - AZVIEW - dl->sunAngles.theta) + 0.001);
    3251        5981 :                         Real64 YR = std::tan(dl->sunAngles.phi + 0.001);
    3252             :                         Real64 POSFAC =
    3253        5981 :                             DayltgGlarePositionFactor(XR, YR); // Position factor for a window element / ref point / view vector combination
    3254             : 
    3255        5981 :                         WindowSolidAngleDaylightPoint = state.dataSurface->SurfaceWindow(IWin).refPts(iRefPoint).solidAngWtd;
    3256             : 
    3257        5981 :                         if (POSFAC != 0.0 && WindowSolidAngleDaylightPoint > 0.000001) {
    3258             :                             // Increment window luminance.  Luminance of solar disk (cd/m2)
    3259             :                             // is 1.47*10^4*(direct normal solar illuminance) for direct normal solar
    3260             :                             // illuminance in lux (lumens/m2). For purposes of calculating daylight factors
    3261             :                             // direct normal solar illuminance = 1.0.
    3262             :                             // Solid angle subtended by sun is 0.000068 steradians
    3263             : 
    3264        3799 :                             XAVWL = 14700.0 * std::sqrt(0.000068 * POSFAC) * double(NWX * NWY) / std::pow(WindowSolidAngleDaylightPoint, 0.8);
    3265        3799 :                             dl->avgWinLum(iHour, 1).sunDisk = XAVWL * TVISS * ObTransDisk; // Bare window
    3266             : 
    3267        3799 :                             if (ANY_BLIND(ShType)) {
    3268           0 :                                 for (int JB = 1; JB <= Material::MaxSlatAngs; ++JB) {
    3269             :                                     // IF (.NOT. SurfaceWindow(IWin)%MovableSlats .AND. JB > 1) EXIT
    3270           0 :                                     dl->avgWinLum(iHour, JB + 1).sunDisk = XAVWL * TVISS * transBmBmMult[JB] * ObTransDisk;
    3271           0 :                                     if (!state.dataSurface->SurfWinMovableSlats(IWin)) break;
    3272             :                                 }
    3273        3799 :                             } else if (ShType == WinShadingType::ExtScreen) {
    3274           0 :                                 dl->avgWinLum(iHour, 2).sunDisk = XAVWL * TVISS * transBmBmMult[1] * ObTransDisk;
    3275             :                             }
    3276             :                         } // Position Factor
    3277             :                     }     // if (calledFrom == RefPt)
    3278             :                 }         // if (ObTransDisk > 1e-6) // Beam avoids all obstructions
    3279             :             }             // if (hitWin)
    3280             :         }                 // if (COSI > 0.0) // Sun on front side
    3281             : 
    3282             :         // Beam solar reaching reference point after beam-beam (specular) reflection from
    3283             :         // an exterior surface
    3284             : 
    3285      322356 :         if (state.dataSurface->CalcSolRefl) {
    3286             :             // Receiving surface number corresponding this window
    3287         908 :             int RecSurfNum = state.dataSurface->SurfShadowRecSurfNum(IWin2);
    3288         908 :             if (RecSurfNum > 0) { // interior windows do not apply
    3289         908 :                 if (state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).NumPossibleObs > 0) {
    3290             :                     bool hitRefl;              // True iff ray hits reflecting surface
    3291         908 :                     Vector3<Real64> HitPtRefl; // Point that ray hits reflecting surface
    3292         908 :                     Vector3<Real64> SunVecMir; // Sun ray mirrored in reflecting surface
    3293         908 :                     Vector3<Real64> ReflNorm;  // Normal vector to reflecting surface
    3294         908 :                     Vector3<Real64> TransBmBmMultRefl;
    3295             :                     // This window has associated obstructions that could reflect beam onto the window
    3296        2724 :                     for (int loop = 1, loop_end = state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).NumPossibleObs; loop <= loop_end;
    3297             :                          ++loop) {
    3298        1816 :                         int ReflSurfNum = state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).PossibleObsSurfNums(loop);
    3299        1816 :                         int ReflSurfNumX = ReflSurfNum;
    3300             :                         // Each shadowing surface has a "mirror" duplicate surface facing in the opposite direction.
    3301             :                         // The following gets the correct side of a shadowing surface for reflection.
    3302        1816 :                         if (state.dataSurface->Surface(ReflSurfNum).IsShadowing) {
    3303        1816 :                             if (dot(RAYCOS, state.dataSurface->Surface(ReflSurfNum).OutNormVec) < 0.0) ReflSurfNumX = ReflSurfNum + 1;
    3304             :                         }
    3305             :                         // Require that the surface can have specular reflection
    3306        3632 :                         if (state.dataSurface->Surface(ReflSurfNum).Class == SurfaceClass::Window ||
    3307        1816 :                             state.dataSurface->SurfShadowGlazingFrac(ReflSurfNum) > 0.0) {
    3308           0 :                             ReflNorm = state.dataSurface->Surface(ReflSurfNumX).OutNormVec;
    3309             :                             // Vector to sun that is mirrored in obstruction
    3310           0 :                             SunVecMir = RAYCOS - 2.0 * dot(RAYCOS, ReflNorm) * ReflNorm;
    3311             :                             // Skip if reflecting surface is not sunlit
    3312           0 :                             if (state.dataHeatBal->SurfSunlitFrac(iHour, 1, ReflSurfNumX) < 0.01) continue;
    3313             :                             // Skip if altitude angle of mirrored sun is negative since reflected sun cannot
    3314             :                             // reach reference point in this case
    3315           0 :                             if (SunVecMir.z <= 0.0) continue;
    3316             :                             // Cosine of incidence angle of reflected beam on window
    3317           0 :                             Real64 CosIncAngRec = dot(state.dataSurface->Surface(IWin2).OutNormVec, SunVecMir);
    3318           0 :                             if (CosIncAngRec <= 0.0) continue;
    3319             :                             // Does ray from ref. pt. along SunVecMir pass through window?
    3320           0 :                             hitWin = PierceSurface(state, IWin2, RREF2, SunVecMir, HP);
    3321           0 :                             if (!hitWin) continue; // Ray did not pass through window
    3322             :                             // Check if this ray hits interior obstructions
    3323           0 :                             hit = DayltgHitInteriorObstruction(state, IWin2, RREF2, HP);
    3324           0 :                             if (hit) continue; // Interior obstruction was hit
    3325             :                             // Does ray hit this reflecting surface?
    3326           0 :                             hitRefl = PierceSurface(state, ReflSurfNum, RREF2, SunVecMir, HitPtRefl);
    3327           0 :                             if (!hitRefl) continue; // Ray did not hit this reflecting surface
    3328           0 :                             Real64 ReflDistanceSq = distance_squared(HitPtRefl, RREF2);
    3329           0 :                             Real64 ReflDistance = std::sqrt(ReflDistanceSq);
    3330             :                             // Is ray from ref. pt. to reflection point (HitPtRefl) obstructed?
    3331           0 :                             bool hitObsRefl = false;
    3332           0 :                             Vector3<Real64> HitPtObs; // Hit point on obstruction
    3333           0 :                             for (int loop2 = 1, loop2_end = state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).NumPossibleObs;
    3334           0 :                                  loop2 <= loop2_end;
    3335             :                                  ++loop2) {
    3336           0 :                                 int const ObsSurfNum = state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).PossibleObsSurfNums(loop2);
    3337           0 :                                 if (ObsSurfNum == ReflSurfNum || ObsSurfNum == state.dataSurface->Surface(ReflSurfNum).BaseSurf) continue;
    3338           0 :                                 hitObs = PierceSurface(state, ObsSurfNum, RREF2, SunVecMir, ReflDistance, HitPtObs); // ReflDistance cutoff added
    3339           0 :                                 if (hitObs) { // => Could skip distance check (unless < vs <= ReflDistance really matters)
    3340           0 :                                     if (distance_squared(HitPtObs, RREF2) < ReflDistanceSq) { // Distance squared from ref pt to reflection point
    3341           0 :                                         hitObsRefl = true;
    3342           0 :                                         break;
    3343             :                                     }
    3344             :                                 }
    3345             :                             }
    3346           0 :                             if (hitObsRefl) continue; // Obstruction closer than reflection pt. was hit; go to next obstruction
    3347             :                             // There is no obstruction for this ray between ref pt and hit pt on reflecting surface.
    3348             :                             // See if ray from hit pt on reflecting surface to original (unmirrored) sun position is obstructed
    3349           0 :                             hitObs = false;
    3350           0 :                             if (state.dataSurface->Surface(ReflSurfNum).Class == SurfaceClass::Window) {
    3351             :                                 // Reflecting surface is a window.
    3352             :                                 // Receiving surface number for this reflecting window.
    3353           0 :                                 int ReflSurfRecNum = state.dataSurface->SurfShadowRecSurfNum(ReflSurfNum);
    3354           0 :                                 if (ReflSurfRecNum > 0) {
    3355             :                                     // Loop over possible obstructions for this reflecting window
    3356           0 :                                     for (int loop2 = 1, loop2_end = state.dataSolarReflectionManager->SolReflRecSurf(ReflSurfRecNum).NumPossibleObs;
    3357           0 :                                          loop2 <= loop2_end;
    3358             :                                          ++loop2) {
    3359             :                                         int const ObsSurfNum =
    3360           0 :                                             state.dataSolarReflectionManager->SolReflRecSurf(ReflSurfRecNum).PossibleObsSurfNums(loop2);
    3361           0 :                                         hitObs = PierceSurface(state, ObsSurfNum, HitPtRefl, RAYCOS, HitPtObs);
    3362           0 :                                         if (hitObs) break;
    3363             :                                     }
    3364             :                                 }
    3365             :                             } else {
    3366             :                                 // Reflecting surface is a building shade
    3367           0 :                                 for (int ObsSurfNum : state.dataSurface->AllShadowPossObstrSurfaceList) {
    3368           0 :                                     if (ObsSurfNum == ReflSurfNum) continue;
    3369           0 :                                     hitObs = PierceSurface(state, ObsSurfNum, HitPtRefl, RAYCOS, HitPtObs);
    3370           0 :                                     if (hitObs) break;
    3371           0 :                                 }
    3372             :                             } // End of check if reflector is a window or shadowing surface
    3373             : 
    3374           0 :                             if (hitObs) continue; // Obstruction hit between reflection hit point and sun; go to next obstruction
    3375             : 
    3376             :                             // No obstructions. Calculate reflected beam illuminance at ref. pt. from this reflecting surface.
    3377           0 :                             SpecReflectance = 0.0;
    3378           0 :                             Real64 CosIncAngRefl = std::abs(dot(RAYCOS, ReflNorm)); // Cos of angle of incidence of beam on reflecting surface
    3379           0 :                             if (state.dataSurface->Surface(ReflSurfNum).Class == SurfaceClass::Window) {
    3380           0 :                                 int const ConstrNumRefl = state.dataSurface->SurfActiveConstruction(ReflSurfNum);
    3381             :                                 SpecReflectance =
    3382           0 :                                     General::POLYF(std::abs(CosIncAngRefl), state.dataConstruction->Construct(ConstrNumRefl).ReflSolBeamFrontCoef);
    3383             :                             }
    3384           0 :                             if (state.dataSurface->Surface(ReflSurfNum).IsShadowing && state.dataSurface->SurfShadowGlazingConstruct(ReflSurfNum) > 0)
    3385           0 :                                 SpecReflectance =
    3386           0 :                                     state.dataSurface->SurfShadowGlazingFrac(ReflSurfNum) *
    3387           0 :                                     General::POLYF(std::abs(CosIncAngRefl),
    3388           0 :                                                    state.dataConstruction->Construct(state.dataSurface->SurfShadowGlazingConstruct(ReflSurfNum))
    3389           0 :                                                        .ReflSolBeamFrontCoef);
    3390           0 :                             TVisRefl = General::POLYF(CosIncAngRec, state.dataConstruction->Construct(IConst).TransVisBeamCoef) * surfWin.glazedFrac *
    3391           0 :                                        surfWin.lightWellEff;
    3392           0 :                             dl->dirIllum(iHour, 1).sunDisk += SunVecMir.z * SpecReflectance * TVisRefl; // Bare window
    3393             : 
    3394           0 :                             TransBmBmMultRefl = 0.0;
    3395           0 :                             if (ANY_BLIND(ShType)) {
    3396           0 :                                 auto const &blind = state.dataMaterial->Blind(BlNum);
    3397           0 :                                 Real64 ProfAng = ProfileAngle(state, IWin, SunVecMir, blind.SlatOrientation);
    3398             :                                 // Contribution of reflected beam passing through slats and reaching reference point
    3399           0 :                                 Real64 const Pi_SlatAng_fac(Constant::Pi / (Material::MaxSlatAngs - 1));
    3400           0 :                                 for (int JB = 1; JB <= Material::MaxSlatAngs; ++JB) {
    3401             :                                     // IF (.NOT.SurfaceWindow(IWin)%MovableSlats .AND. JB > 1) EXIT
    3402           0 :                                     Real64 SlatAng = (state.dataSurface->SurfWinMovableSlats(IWin)) ? (double(JB - 1) * Pi_SlatAng_fac)
    3403           0 :                                                                                                     : (blind.SlatAngle * Constant::DegToRadians);
    3404             : 
    3405           0 :                                     TransBmBmMultRefl(JB) =
    3406           0 :                                         Window::BlindBeamBeamTrans(ProfAng, SlatAng, blind.SlatWidth, blind.SlatSeparation, blind.SlatThickness);
    3407           0 :                                     dl->dirIllum(iHour, JB + 1).sunDisk += SunVecMir.z * SpecReflectance * TVisRefl * TransBmBmMultRefl(JB);
    3408           0 :                                     if (!state.dataSurface->SurfWinMovableSlats(IWin)) break;
    3409             :                                 }
    3410           0 :                             } else if (ShType == WinShadingType::ExtScreen) {
    3411             :                                 // pass angle from sun to window normal here using PHSUN and THSUN from above and
    3412             :                                 // surface angles SunAltitudeToWindowNormalAngle = PHSUN - SurfaceWindow(IWin)%Phi
    3413             :                                 // SunAzimuthToWindowNormalAngle = THSUN - SurfaceWindow(IWin)%Theta
    3414           0 :                                 auto const *screen = dynamic_cast<Material::MaterialScreen const *>(state.dataMaterial->Material(surfWin.screenNum));
    3415           0 :                                 assert(screen != nullptr);
    3416             : 
    3417           0 :                                 Real64 phi = std::abs(dl->sunAngles.phi - surfWin.phi);
    3418           0 :                                 Real64 theta = std::abs(dl->sunAngles.theta - surfWin.theta);
    3419             :                                 int ip1, ip2, it1, it2; // lo/hi phi/theta interpolation map indices
    3420             :                                 General::BilinearInterpCoeffs coeffs;
    3421           0 :                                 Material::NormalizePhiTheta(phi, theta);
    3422           0 :                                 Material::GetPhiThetaIndices(phi, theta, screen->dPhi, screen->dTheta, ip1, ip2, it1, it2);
    3423           0 :                                 General::GetBilinearInterpCoeffs(
    3424           0 :                                     phi, theta, ip1 * screen->dPhi, ip2 * screen->dPhi, it1 * screen->dTheta, it2 * screen->dTheta, coeffs);
    3425             : 
    3426           0 :                                 TransBmBmMultRefl(1) = General::BilinearInterp(screen->btars[ip1][it1].BmTrans,
    3427           0 :                                                                                screen->btars[ip1][it2].BmTrans,
    3428           0 :                                                                                screen->btars[ip2][it1].BmTrans,
    3429           0 :                                                                                screen->btars[ip2][it2].BmTrans,
    3430             :                                                                                coeffs);
    3431           0 :                                 dl->dirIllum(iHour, 2).sunDisk += SunVecMir.z * SpecReflectance * TVisRefl * TransBmBmMultRefl(1);
    3432             :                             } // End of check if window has a blind or screen
    3433             : 
    3434             :                             // Glare from reflected solar disk
    3435             : 
    3436           0 :                             PHSUNrefl = SunVecMir.z;
    3437           0 :                             THSUNrefl = std::atan2(SunVecMir.y, SunVecMir.x);
    3438           0 :                             Real64 XR = std::tan(std::abs(Constant::PiOvr2 - AZVIEW - THSUNrefl) + 0.001);
    3439           0 :                             Real64 YR = std::tan(PHSUNrefl + 0.001);
    3440           0 :                             Real64 POSFAC = DayltgGlarePositionFactor(XR, YR);
    3441           0 :                             if (POSFAC != 0.0 && state.dataSurface->SurfaceWindow(IWin).refPts(iRefPoint).solidAngWtd > 0.000001) {
    3442           0 :                                 XAVWL = 14700.0 * std::sqrt(0.000068 * POSFAC) * double(NWX * NWY) /
    3443           0 :                                         std::pow(state.dataSurface->SurfaceWindow(IWin).refPts(iRefPoint).solidAngWtd, 0.8);
    3444           0 :                                 dl->avgWinLum(iHour, 1).sunDisk += XAVWL * TVisRefl * SpecReflectance; // Bare window
    3445           0 :                                 if (ANY_BLIND(ShType)) {
    3446           0 :                                     for (int JB = 1; JB <= Material::MaxSlatAngs; ++JB) {
    3447             :                                         // IF(.NOT. SurfaceWindow(IWin)%MovableSlats .AND. JB > 1) EXIT
    3448           0 :                                         dl->avgWinLum(iHour, JB + 1).sunDisk += XAVWL * TVisRefl * SpecReflectance * TransBmBmMultRefl(JB);
    3449           0 :                                         if (!state.dataSurface->SurfWinMovableSlats(IWin)) break;
    3450             :                                     }
    3451           0 :                                 } else if (ShType == WinShadingType::ExtScreen) {
    3452           0 :                                     dl->avgWinLum(iHour, 2).sunDisk += XAVWL * TVisRefl * SpecReflectance * TransBmBmMultRefl(1);
    3453             :                                 }
    3454             :                             }
    3455           0 :                         } // End of check that obstruction can specularly reflect
    3456             :                     }     // End of loop over obstructions associated with this window
    3457             : 
    3458         908 :                 } // End of check if this window has associated obstructions
    3459             :             }     // End of check to see if this is exterior type window
    3460             :         }         // End of check if exterior reflection calculation is in effect
    3461             : 
    3462      322356 :     } // Last pass
    3463             : 
    3464    32669324 :     if ((ICtrl > 0 && (ANY_BLIND(ShType) || ANY_SHADE_SCREEN(ShType))) || state.dataSurface->SurfWinSolarDiffusing(IWin)) {
    3465             : 
    3466             :         // ----- CASE II -- WINDOW WITH SCREEN, SHADE, BLIND, OR DIFFUSING WINDOW
    3467             : 
    3468             :         // Interior window visible transmittance multiplier for exterior window in adjacent zone
    3469       12496 :         TVisIntWinMult = 1.0;
    3470       12496 :         TVisIntWinDiskMult = 1.0;
    3471       12496 :         if (state.dataSurface->Surface(IWin).SolarEnclIndex != dl->daylightControl(daylightCtrlNum).enclIndex) {
    3472           0 :             TVisIntWinMult = TVISIntWin;
    3473           0 :             TVisIntWinDiskMult = TVISIntWinDisk;
    3474             :         }
    3475             : 
    3476       12496 :         Real64 const DOMEGA_Ray_3_TVisIntWinMult(DOMEGA_Ray_3 * TVisIntWinMult);
    3477       24992 :         for (int JB = 1; JB <= Material::MaxSlatAngs; ++JB) {
    3478             :             // Sometimes this is JB > 2 and sometimes it's JB > 1, what gives?
    3479       24992 :             if (!state.dataSurface->SurfWinMovableSlats(IWin) && JB > 1) break;
    3480             : 
    3481       12496 :             auto &wlumsk = dl->winLum(iHour, JB + 1);
    3482       12496 :             auto &edirsk = dl->dirIllum(iHour, JB + 1);
    3483       12496 :             auto &avwlsk = dl->avgWinLum(iHour, JB + 1);
    3484       62480 :             for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
    3485             :                 // IF (.NOT.SurfaceWindow(IWin)%MovableSlats .AND. JB > 1) EXIT
    3486       49984 :                 avwlsk.sky[iSky] += wlumsk.sky[iSky] * TVisIntWinMult;
    3487       49984 :                 if (PHRAY > 0.0) edirsk.sky[iSky] += wlumsk.sky[iSky] * DOMEGA_Ray_3_TVisIntWinMult;
    3488             :             } // for (iSky)
    3489             : 
    3490       12496 :             dl->avgWinLum(iHour, JB + 1).sun += dl->winLum(iHour, JB + 1).sun * TVisIntWinMult;
    3491       12496 :             dl->avgWinLum(iHour, JB + 1).sunDisk += dl->winLum(iHour, JB + 1).sunDisk * TVisIntWinDiskMult;
    3492             : 
    3493       12496 :             if (PHRAY > 0.0) {
    3494        9914 :                 dl->dirIllum(iHour, JB + 1).sun += dl->winLum(iHour, JB + 1).sun * DOMEGA_Ray_3_TVisIntWinMult;
    3495             :             }
    3496             :         } // for (JB)
    3497             :     }
    3498    33061172 : } // FigureDayltgCoeffsAtPointsForSunPosition()
    3499             : 
    3500      363744 : void FigureRefPointDayltgFactorsToAddIllums(EnergyPlusData &state,
    3501             :                                             int const daylightCtrlNum, // Current daylighting control number
    3502             :                                             int const iRefPoint,
    3503             :                                             int const iHour,
    3504             :                                             int &ISunPos,
    3505             :                                             int const IWin,
    3506             :                                             int const loopwin,
    3507             :                                             int const NWX,  // Number of window elements in x direction for dayltg calc
    3508             :                                             int const NWY,  // Number of window elements in y direction for dayltg calc
    3509             :                                             int const ICtrl // Window control counter
    3510             : )
    3511             : {
    3512             : 
    3513             :     // SUBROUTINE INFORMATION:
    3514             :     //       AUTHOR         B. Griffith, Oct 2012, derived from legacy code by Fred Winkelmann
    3515             :     //       DATE WRITTEN   Oct. 2012
    3516             : 
    3517             :     // PURPOSE OF THIS SUBROUTINE:
    3518             :     // calculation worker routine to fill daylighting coefficients
    3519             : 
    3520             :     // METHODOLOGY EMPLOYED:
    3521             :     // this version is just for reference points.
    3522             : 
    3523             :     // SUBROUTINE PARAMETER DEFINITIONS:
    3524      363744 :     Real64 constexpr tmpDFCalc(0.05); // cut off illuminance (lux) for exterior horizontal in calculating the daylighting and glare factors
    3525             : 
    3526             :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    3527      363744 :     auto &dl = state.dataDayltg;
    3528             : 
    3529      363744 :     if (state.dataSurface->SurfSunCosHourly(iHour).z < DataEnvironment::SunIsUpValue) return;
    3530             : 
    3531      160590 :     ++ISunPos;
    3532             : 
    3533             :     // Altitude of sun (degrees)
    3534      160590 :     dl->sunAngles = dl->sunAnglesHr[iHour];
    3535             : 
    3536      160590 :     auto &thisDayltgCtrl = dl->daylightControl(daylightCtrlNum);
    3537      160590 :     int const enclNum = state.dataSurface->Surface(IWin).SolarEnclIndex;
    3538             : 
    3539             :     // Loop over shading index (1=bare window; 2=diffusing glazing, shade, screen or fixed slat-angle blind;
    3540             :     // 2 to Material::MaxSlatAngs+1 for variable slat-angle blind)
    3541             : 
    3542             :     // TH. 9/22/2009. CR 7625 - daylight illuminance spikes during some sunset hours due to the calculated sky and sun
    3543             :     //  related daylight factors > 1, which theoretically can occur when sun is perpendicular to the window
    3544             :     //  and interior surfaces with high visible reflectance.
    3545             :     // Added tmpDFCalc (default to 0.05 lux) as the cap for GILSK and GILSU in calculating the daylight factors
    3546             :     //  the assumption behind it is if exterior horizontal surface does not get daylight, spaces do not get daylight.
    3547             : 
    3548      160590 :     auto &daylFacHr = thisDayltgCtrl.daylFac[iHour];
    3549             : 
    3550      481770 :     for (int JSH = 1; JSH <= Material::MaxSlatAngs + 1; ++JSH) {
    3551      481770 :         if (!state.dataSurface->SurfWinMovableSlats(IWin) && JSH > 2) break;
    3552             : 
    3553      321180 :         auto const &gilsk = dl->horIllum[iHour];
    3554      321180 :         auto const &edirsk = dl->dirIllum(iHour, JSH);
    3555      321180 :         auto const &eintsk = dl->reflIllum(iHour, JSH);
    3556      321180 :         auto const &avwlsk = dl->avgWinLum(iHour, JSH);
    3557             : 
    3558      321180 :         auto &daylFac = daylFacHr(loopwin, iRefPoint, JSH);
    3559      321180 :         auto &illFac = daylFac[iLum_Illum];
    3560      321180 :         auto &sourceFac = daylFac[iLum_Source];
    3561      321180 :         auto &backFac = daylFac[iLum_Back];
    3562             : 
    3563     1605900 :         for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) { // Loop over sky types
    3564             : 
    3565     1284720 :             if (gilsk.sky[iSky] > tmpDFCalc) {
    3566     1284720 :                 illFac.sky[iSky] = (edirsk.sky[iSky] + eintsk.sky[iSky]) / gilsk.sky[iSky];
    3567     1284720 :                 sourceFac.sky[iSky] = avwlsk.sky[iSky] / (NWX * NWY * gilsk.sky[iSky]);
    3568     1284720 :                 backFac.sky[iSky] = eintsk.sky[iSky] * dl->enclDaylight(enclNum).aveVisDiffReflect / (Constant::Pi * gilsk.sky[iSky]);
    3569             :             } else {
    3570           0 :                 illFac.sky[iSky] = 0.0;
    3571           0 :                 sourceFac.sky[iSky] = 0.0;
    3572           0 :                 backFac.sky[iSky] = 0.0;
    3573             :             }
    3574             : 
    3575             :         } // for (iSky)
    3576             : 
    3577      321180 :         if (dl->horIllum[iHour].sun > tmpDFCalc) {
    3578      303974 :             auto &daylFac = daylFacHr(loopwin, iRefPoint, JSH);
    3579      303974 :             daylFac[iLum_Illum].sun = (dl->dirIllum(iHour, JSH).sun + dl->reflIllum(iHour, JSH).sun) / (dl->horIllum[iHour].sun + 0.0001);
    3580      303974 :             daylFac[iLum_Illum].sunDisk = (dl->dirIllum(iHour, JSH).sunDisk + dl->reflIllum(iHour, JSH).sunDisk) / (dl->horIllum[iHour].sun + 0.0001);
    3581      303974 :             daylFac[iLum_Source].sun = dl->avgWinLum(iHour, JSH).sun / (NWX * NWY * (dl->horIllum[iHour].sun + 0.0001));
    3582      303974 :             daylFac[iLum_Source].sunDisk = dl->avgWinLum(iHour, JSH).sunDisk / (NWX * NWY * (dl->horIllum[iHour].sun + 0.0001));
    3583      607948 :             daylFac[iLum_Back].sun =
    3584      303974 :                 dl->reflIllum(iHour, JSH).sun * dl->enclDaylight(enclNum).aveVisDiffReflect / (Constant::Pi * (dl->horIllum[iHour].sun + 0.0001));
    3585      303974 :             daylFac[iLum_Back].sunDisk =
    3586      303974 :                 dl->reflIllum(iHour, JSH).sunDisk * dl->enclDaylight(enclNum).aveVisDiffReflect / (Constant::Pi * (dl->horIllum[iHour].sun + 0.0001));
    3587             :         } else {
    3588       17206 :             daylFac[iLum_Illum].sun = 0.0;
    3589       17206 :             daylFac[iLum_Illum].sunDisk = 0.0;
    3590             : 
    3591       17206 :             daylFac[iLum_Source].sun = 0.0;
    3592       17206 :             daylFac[iLum_Source].sunDisk = 0.0;
    3593             : 
    3594       17206 :             daylFac[iLum_Back].sun = 0.0;
    3595       17206 :             daylFac[iLum_Back].sunDisk = 0.0;
    3596             :         }
    3597             :     } // for (jSH)
    3598             : 
    3599             :     // For switchable glazing put daylighting factors for switched (dark) state in IS=2 location
    3600      160590 :     if (ICtrl > 0 && state.dataSurface->WindowShadingControl(ICtrl).ShadingType == WinShadingType::SwitchableGlazing) {
    3601             : 
    3602        2090 :         Real64 VTR = state.dataSurface->SurfWinVisTransRatio(IWin); // Ratio of Tvis of fully-switched state to that of the unswitched state
    3603        2090 :         auto &daylFac2 = daylFacHr(loopwin, iRefPoint, 2);
    3604        2090 :         auto const &daylFac1 = daylFacHr(loopwin, iRefPoint, 1);
    3605             : 
    3606       10450 :         for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
    3607        8360 :             daylFac2[iLum_Illum].sky[iSky] = daylFac1[iLum_Illum].sky[iSky] * VTR;
    3608        8360 :             daylFac2[iLum_Source].sky[iSky] = daylFac1[iLum_Source].sky[iSky] * VTR;
    3609        8360 :             daylFac2[iLum_Back].sky[iSky] = daylFac1[iLum_Back].sky[iSky] * VTR;
    3610             :         } // for (iSky)
    3611             : 
    3612        2090 :         daylFac2[iLum_Illum].sun = daylFac1[iLum_Illum].sun * VTR;
    3613        2090 :         daylFac2[iLum_Source].sun = daylFac1[iLum_Source].sun * VTR;
    3614        2090 :         daylFac2[iLum_Back].sun = daylFac1[iLum_Back].sun * VTR;
    3615        2090 :         daylFac2[iLum_Illum].sunDisk = daylFac1[iLum_Illum].sunDisk * VTR;
    3616        2090 :         daylFac2[iLum_Source].sunDisk = daylFac1[iLum_Source].sunDisk * VTR;
    3617        2090 :         daylFac2[iLum_Back].sunDisk = daylFac1[iLum_Back].sunDisk * VTR;
    3618             :     } // ICtrl > 0
    3619             : } // FigureRefPointDayltgFactorsToAddIllums()
    3620             : 
    3621      328800 : void FigureMapPointDayltgFactorsToAddIllums(EnergyPlusData &state,
    3622             :                                             int const MapNum,
    3623             :                                             int const iMapPoint,
    3624             :                                             int const iHour,
    3625             :                                             int const IWin,
    3626             :                                             int const loopwin,
    3627             :                                             int const ICtrl // Window control counter
    3628             : )
    3629             : {
    3630             : 
    3631             :     // SUBROUTINE INFORMATION:
    3632             :     //       AUTHOR         B. Griffith, Oct 2012, derived from legacy code by Fred Winkelmann, Peter Ellis, Linda Lawrie
    3633             :     //       DATE WRITTEN   Nov. 2012
    3634             : 
    3635             :     // PURPOSE OF THIS SUBROUTINE:
    3636             :     // calculation worker routine to fill daylighting coefficients
    3637             : 
    3638             :     // METHODOLOGY EMPLOYED:
    3639             :     // this version is just for map points.
    3640             : 
    3641             :     // SUBROUTINE PARAMETER DEFINITIONS:
    3642      328800 :     Real64 constexpr tmpDFCalc(0.05); // cut off illuminance (lux) for exterior horizontal in calculating
    3643             :     // the daylighting and glare factors
    3644             : 
    3645             :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    3646      328800 :     auto &dl = state.dataDayltg;
    3647             : 
    3648      328800 :     if (state.dataSurface->SurfSunCosHourly(iHour).z < DataEnvironment::SunIsUpValue) return;
    3649             : 
    3650             :     // Loop over shading index (1=bare window; 2=diffusing glazing, shade, screen or fixed slat-angle blind;
    3651             :     // 2 to Material::MaxSlatAngs+1 for variable slat-angle blind)
    3652             : 
    3653             :     // TH. 9/22/2009. CR 7625 - daylight illuminance spikes during some sunset hours due to the calculated sky and sun
    3654             :     //  related daylight factors > 1, which theoretically can occur when sun is perpendicular to the window
    3655             :     //  and interior surfaces with high visible reflectance.
    3656             :     // Added tmpDFCalc (default to 0.05 lux) as the cap for GILSK and GILSU in calculating the daylight factors
    3657             :     //  the assumption behind it is if exterior horizontal surface does not get daylight, spaces do not get daylight.
    3658             : 
    3659      164400 :     auto &illumMap = dl->illumMaps(MapNum);
    3660      164400 :     auto &daylFacHr = illumMap.daylFac[iHour];
    3661      493200 :     for (int JSH = 1; JSH <= Material::MaxSlatAngs + 1; ++JSH) {
    3662      493200 :         if (!state.dataSurface->SurfWinMovableSlats(IWin) && JSH > 2) break;
    3663             : 
    3664      328800 :         auto const &gilsk = dl->horIllum[iHour];
    3665      328800 :         auto const &edirsk = dl->dirIllum(iHour, JSH);
    3666      328800 :         auto const &eintsk = dl->reflIllum(iHour, JSH);
    3667      328800 :         auto &illSky = daylFacHr(loopwin, iMapPoint, JSH);
    3668             : 
    3669     1644000 :         for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) { // Loop over sky types
    3670     1315200 :             illSky.sky[iSky] = (gilsk.sky[iSky] > tmpDFCalc) ? ((edirsk.sky[iSky] + eintsk.sky[iSky]) / gilsk.sky[iSky]) : 0.0;
    3671             :         } // for (iSky)
    3672             : 
    3673      328800 :         if (dl->horIllum[iHour].sun > tmpDFCalc) {
    3674      630200 :             daylFacHr(loopwin, iMapPoint, JSH).sun =
    3675      315100 :                 (dl->dirIllum(iHour, JSH).sun + dl->reflIllum(iHour, JSH).sun) / (dl->horIllum[iHour].sun + 0.0001);
    3676      315100 :             daylFacHr(loopwin, iMapPoint, JSH).sunDisk =
    3677      315100 :                 (dl->dirIllum(iHour, JSH).sunDisk + dl->reflIllum(iHour, JSH).sunDisk) / (dl->horIllum[iHour].sun + 0.0001);
    3678             :         } else {
    3679       13700 :             daylFacHr(loopwin, iMapPoint, JSH).sun = 0.0;
    3680       13700 :             daylFacHr(loopwin, iMapPoint, JSH).sunDisk = 0.0;
    3681             :         }
    3682             :     } // for (jSH)
    3683             : 
    3684             :     // For switchable glazing put daylighting factors for switched (dark) state in IS=2 location
    3685      164400 :     if (ICtrl > 0 && state.dataSurface->WindowShadingControl(ICtrl).ShadingType == WinShadingType::SwitchableGlazing) {
    3686      146400 :         Real64 VTR = state.dataSurface->SurfWinVisTransRatio(IWin); // ratio of Tvis of switched to unswitched state
    3687      146400 :         auto &illSky2 = daylFacHr(loopwin, iMapPoint, 2);
    3688      146400 :         auto const &illSky1 = daylFacHr(loopwin, iMapPoint, 1);
    3689      732000 :         for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
    3690      585600 :             illSky2.sky[iSky] = illSky1.sky[iSky] * VTR;
    3691             :         }
    3692             : 
    3693      146400 :         daylFacHr(loopwin, iMapPoint, 2).sun = daylFacHr(loopwin, iMapPoint, 1).sun * VTR;
    3694      146400 :         daylFacHr(loopwin, iMapPoint, 2).sunDisk = daylFacHr(loopwin, iMapPoint, 1).sunDisk * VTR;
    3695             :     } // ICtrl > 0
    3696             : }
    3697             : 
    3698         796 : void GetDaylightingParametersInput(EnergyPlusData &state)
    3699             : {
    3700             : 
    3701             :     // SUBROUTINE INFORMATION:
    3702             :     //       AUTHOR         Linda Lawrie
    3703             :     //       DATE WRITTEN   Oct 2004
    3704             : 
    3705             :     // PURPOSE OF THIS SUBROUTINE:
    3706             :     // This subroutine provides a simple structure to get all daylighting
    3707             :     // parameters.
    3708         796 :     auto &dl = state.dataDayltg;
    3709             : 
    3710         796 :     if (!dl->getDaylightingParametersInputFlag) return;
    3711         796 :     dl->getDaylightingParametersInputFlag = false;
    3712             : 
    3713         796 :     auto const &ipsc = state.dataIPShortCut;
    3714         796 :     ipsc->cCurrentModuleObject = "Daylighting:Controls";
    3715         796 :     bool ErrorsFound = false;
    3716         796 :     int TotDaylightingControls = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, ipsc->cCurrentModuleObject);
    3717         796 :     if (TotDaylightingControls > 0) {
    3718          64 :         dl->enclDaylight.allocate(state.dataViewFactor->NumOfSolarEnclosures);
    3719          64 :         GetInputDayliteRefPt(state, ErrorsFound);
    3720          64 :         GetDaylightingControls(state, ErrorsFound);
    3721          64 :         GeometryTransformForDaylighting(state);
    3722          64 :         GetInputIlluminanceMap(state, ErrorsFound);
    3723          64 :         GetLightWellData(state, ErrorsFound);
    3724          64 :         if (ErrorsFound) ShowFatalError(state, "Program terminated for above reasons, related to DAYLIGHTING");
    3725          64 :         DayltgSetupAdjZoneListsAndPointers(state);
    3726             :     }
    3727             : 
    3728         796 :     dl->maxNumRefPtInAnyDaylCtrl = 0;
    3729         796 :     dl->maxNumRefPtInAnyEncl = 0;
    3730             :     // Loop through all daylighting controls to find total reference points in each enclosure
    3731        1086 :     for (int daylightCtrlNum = 1; daylightCtrlNum <= (int)dl->daylightControl.size(); ++daylightCtrlNum) {
    3732         290 :         int numRefPoints = dl->daylightControl(daylightCtrlNum).TotalDaylRefPoints;
    3733         290 :         dl->maxNumRefPtInAnyDaylCtrl = max(numRefPoints, dl->maxNumRefPtInAnyDaylCtrl);
    3734             :     }
    3735        5851 :     for (int enclNum = 1; enclNum <= state.dataViewFactor->NumOfSolarEnclosures; ++enclNum) {
    3736        5055 :         dl->maxNumRefPtInAnyEncl = max(state.dataViewFactor->EnclSolInfo(enclNum).TotalEnclosureDaylRefPoints, dl->maxNumRefPtInAnyEncl);
    3737             :     }
    3738             : 
    3739         796 :     dl->maxEnclSubSurfaces = max(maxval(state.dataHeatBal->Zone, &DataHeatBalance::ZoneData::NumSubSurfaces),
    3740         796 :                                  maxval(dl->enclDaylight, &EnclDaylightCalc::NumOfDayltgExtWins));
    3741             : 
    3742        7013 :     for (int SurfNum : state.dataSurface->AllHTWindowSurfaceList) {
    3743        6217 :         auto const &surf = state.dataSurface->Surface(SurfNum);
    3744        6217 :         int const surfEnclNum = surf.SolarEnclIndex;
    3745        6217 :         int const numEnclRefPoints = state.dataViewFactor->EnclSolInfo(surfEnclNum).TotalEnclosureDaylRefPoints;
    3746        6217 :         auto &surfWin = state.dataSurface->SurfaceWindow(SurfNum);
    3747        6217 :         if (numEnclRefPoints > 0) {
    3748         848 :             if (!state.dataSurface->SurfWinSurfDayLightInit(SurfNum)) {
    3749         848 :                 surfWin.refPts.allocate(numEnclRefPoints);
    3750        2343 :                 for (auto &refPt : surfWin.refPts) {
    3751        1495 :                     new (&refPt) SurfaceWindowRefPt();
    3752             :                 }
    3753             : 
    3754         848 :                 state.dataSurface->SurfWinSurfDayLightInit(SurfNum) = true;
    3755             :             }
    3756             :         } else {
    3757        5369 :             int SurfNumAdj = surf.ExtBoundCond;
    3758        5369 :             if (SurfNumAdj > 0) {
    3759          13 :                 int const adjSurfEnclNum = state.dataSurface->Surface(SurfNumAdj).SolarEnclIndex;
    3760          13 :                 int const numAdjEnclRefPoints = state.dataViewFactor->EnclSolInfo(adjSurfEnclNum).TotalEnclosureDaylRefPoints;
    3761          13 :                 if (numAdjEnclRefPoints > 0) {
    3762           1 :                     if (!state.dataSurface->SurfWinSurfDayLightInit(SurfNum)) {
    3763           1 :                         surfWin.refPts.allocate(numAdjEnclRefPoints);
    3764           2 :                         for (auto &refPt : surfWin.refPts) {
    3765           1 :                             new (&refPt) SurfaceWindowRefPt();
    3766             :                         }
    3767           1 :                         state.dataSurface->SurfWinSurfDayLightInit(SurfNum) = true;
    3768             :                     }
    3769             :                 }
    3770             :             }
    3771             :         }
    3772             : 
    3773        6217 :         if (surf.ExtBoundCond != ExternalEnvironment) continue;
    3774             : 
    3775        6203 :         if (!surf.HasShadeControl) continue;
    3776             : 
    3777         151 :         auto &thisSurfEnclosure(state.dataViewFactor->EnclSolInfo(surf.SolarEnclIndex));
    3778         151 :         if (state.dataSurface->WindowShadingControl(surf.activeWindowShadingControl).GlareControlIsActive) {
    3779             :             // Error if GlareControlIsActive but window is not in a Daylighting:Detailed zone
    3780          30 :             if (thisSurfEnclosure.TotalEnclosureDaylRefPoints == 0) {
    3781           0 :                 ShowSevereError(state, format("Window={} has Window Shading Control with", surf.Name));
    3782           0 :                 ShowContinueError(state, "GlareControlIsActive = Yes but it is not in a Daylighting zone or enclosure.");
    3783           0 :                 ShowContinueError(state, format("Zone or enclosure indicated={}", state.dataViewFactor->EnclSolInfo(surf.SolarEnclIndex).Name));
    3784           0 :                 ErrorsFound = true;
    3785             :             }
    3786             :             // Error if GlareControlIsActive and window is in a Daylighting:Detailed zone/enclosure with
    3787             :             // an interior window adjacent to another Daylighting:Detailed zone/enclosure
    3788          30 :             if (thisSurfEnclosure.TotalEnclosureDaylRefPoints > 0) {
    3789         357 :                 for (int const intWin : thisSurfEnclosure.SurfacePtr) {
    3790         327 :                     int const SurfNumAdj = state.dataSurface->Surface(intWin).ExtBoundCond;
    3791         327 :                     if (state.dataSurface->Surface(intWin).Class == SurfaceClass::Window && SurfNumAdj > 0) {
    3792           0 :                         auto &adjSurfEnclosure(state.dataViewFactor->EnclSolInfo(state.dataSurface->Surface(SurfNumAdj).SolarEnclIndex));
    3793           0 :                         if (adjSurfEnclosure.TotalEnclosureDaylRefPoints > 0) {
    3794           0 :                             ShowSevereError(state, format("Window={} has Window Shading Control with", surf.Name));
    3795           0 :                             ShowContinueError(state, "GlareControlIsActive = Yes and is in a Daylighting zone or enclosure");
    3796           0 :                             ShowContinueError(state, "that shares an interior window with another Daylighting zone or enclosure");
    3797           0 :                             ShowContinueError(state, format("Adjacent Zone or Enclosure indicated={}", adjSurfEnclosure.Name));
    3798           0 :                             ErrorsFound = true;
    3799             :                         }
    3800             :                     }
    3801             :                 }
    3802             :             }
    3803             :         }
    3804             : 
    3805         151 :         if (state.dataSurface->WindowShadingControl(surf.activeWindowShadingControl).shadingControlType != WindowShadingControlType::MeetDaylIlumSetp)
    3806         149 :             continue;
    3807             : 
    3808             :         // Error if window has shadingControlType = MeetDaylightingIlluminanceSetpoint &
    3809             :         // but is not in a Daylighting:Detailed zone
    3810           2 :         if (thisSurfEnclosure.TotalEnclosureDaylRefPoints == 0) {
    3811           0 :             ShowSevereError(state, format("Window={} has Window Shading Control with", surf.Name));
    3812           0 :             ShowContinueError(state, "MeetDaylightingIlluminanceSetpoint but it is not in a Daylighting zone or enclosure.");
    3813           0 :             ShowContinueError(state, format("Zone or enclosure indicated={}", thisSurfEnclosure.Name));
    3814           0 :             ErrorsFound = true;
    3815           0 :             continue;
    3816             :         }
    3817             : 
    3818             :         // Error if window has shadingControlType = MeetDaylightIlluminanceSetpoint and is in a &
    3819             :         // Daylighting:Detailed zone with an interior window adjacent to another Daylighting:Detailed zone
    3820          22 :         for (int const intWin : thisSurfEnclosure.SurfacePtr) {
    3821          20 :             int const SurfNumAdj = state.dataSurface->Surface(intWin).ExtBoundCond;
    3822          20 :             if (state.dataSurface->Surface(intWin).Class == SurfaceClass::Window && SurfNumAdj > 0) {
    3823           0 :                 auto &adjSurfEnclosure(state.dataViewFactor->EnclSolInfo(state.dataSurface->Surface(SurfNumAdj).SolarEnclIndex));
    3824           0 :                 if (adjSurfEnclosure.TotalEnclosureDaylRefPoints > 0) {
    3825           0 :                     ShowSevereError(state, format("Window={} has Window Shading Control with", surf.Name));
    3826           0 :                     ShowContinueError(state, "MeetDaylightIlluminanceSetpoint and is in a Daylighting zone or enclosure");
    3827           0 :                     ShowContinueError(state, "that shares an interior window with another Daylighting zone or enclosure");
    3828           0 :                     ShowContinueError(state, format("Adjacent Zone or enclosure indicated={}", adjSurfEnclosure.Name));
    3829           0 :                     ErrorsFound = true;
    3830             :                 }
    3831             :             }
    3832             :         }
    3833         796 :     } // for (SurfNum)
    3834             : 
    3835         796 :     if (!state.dataHeatBal->AnyAirBoundary) {
    3836       46524 :         for (int SurfLoop = 1; SurfLoop <= state.dataSurface->TotSurfaces; ++SurfLoop) {
    3837       45733 :             auto const &surf = state.dataSurface->Surface(SurfLoop);
    3838       45733 :             if (surf.Class != SurfaceClass::Window || !surf.ExtSolar) continue;
    3839             : 
    3840        6171 :             int const enclOfSurf = surf.SolarEnclIndex;
    3841        6171 :             auto const &enclSol = state.dataViewFactor->EnclSolInfo(enclOfSurf);
    3842        6171 :             if (enclSol.TotalEnclosureDaylRefPoints == 0 || enclSol.HasInterZoneWindow || !dl->enclDaylight(enclOfSurf).hasSplitFluxDaylighting)
    3843        5336 :                 continue;
    3844             : 
    3845         835 :             auto &surfWin = state.dataSurface->SurfaceWindow(SurfLoop);
    3846        2309 :             for (int refPtNum = 1; refPtNum <= enclSol.TotalEnclosureDaylRefPoints; ++refPtNum) {
    3847        1474 :                 auto &refPt = surfWin.refPts(refPtNum);
    3848        4422 :                 SetupOutputVariable(state,
    3849        2948 :                                     format("Daylighting Window Reference Point {} Illuminance", refPtNum),
    3850             :                                     Constant::Units::lux,
    3851        1474 :                                     refPt.illumFromWinRep,
    3852             :                                     OutputProcessor::TimeStepType::Zone,
    3853             :                                     OutputProcessor::StoreType::Average,
    3854        1474 :                                     surf.Name);
    3855        4422 :                 SetupOutputVariable(state,
    3856        2948 :                                     format("Daylighting Window Reference Point {} View Luminance", refPtNum),
    3857             :                                     Constant::Units::cd_m2,
    3858        1474 :                                     refPt.lumWinRep,
    3859             :                                     OutputProcessor::TimeStepType::Zone,
    3860             :                                     OutputProcessor::StoreType::Average,
    3861        1474 :                                     surf.Name);
    3862             :             }
    3863             :         }
    3864             :     } else {
    3865          34 :         for (int enclNum = 1; enclNum <= state.dataViewFactor->NumOfSolarEnclosures; ++enclNum) {
    3866          29 :             auto const &enclSol = state.dataViewFactor->EnclSolInfo(enclNum);
    3867         302 :             for (int const enclSurfNum : enclSol.SurfacePtr) {
    3868         273 :                 auto const &surf = state.dataSurface->Surface(enclSurfNum);
    3869         273 :                 auto &surfWindow = state.dataSurface->SurfaceWindow(enclSurfNum);
    3870             : 
    3871         273 :                 if (surf.Class != SurfaceClass::Window || !surf.ExtSolar) continue;
    3872             : 
    3873          30 :                 if (enclSol.TotalEnclosureDaylRefPoints == 0 || enclSol.HasInterZoneWindow) continue;
    3874             : 
    3875           7 :                 auto const &enclDayltg = dl->enclDaylight(enclNum);
    3876           7 :                 if (!enclDayltg.hasSplitFluxDaylighting) continue;
    3877             : 
    3878           7 :                 int refPtCount = 0;
    3879          17 :                 for (int controlNum : enclDayltg.daylightControlIndexes) {
    3880          10 :                     auto const &control = dl->daylightControl(controlNum);
    3881          20 :                     for (int refPtNum = 1; refPtNum <= control.TotalDaylRefPoints; ++refPtNum) {
    3882          10 :                         ++refPtCount; // Count reference points across each daylighting control in the same enclosure
    3883          10 :                         auto &refPt = surfWindow.refPts(refPtCount);
    3884          10 :                         std::string varKey = format("{} to {}", surf.Name, state.dataDayltg->DaylRefPt(control.refPts(refPtNum).num).Name);
    3885          20 :                         SetupOutputVariable(state,
    3886             :                                             "Daylighting Window Reference Point Illuminance",
    3887             :                                             Constant::Units::lux,
    3888          10 :                                             refPt.illumFromWinRep,
    3889             :                                             OutputProcessor::TimeStepType::Zone,
    3890             :                                             OutputProcessor::StoreType::Average,
    3891             :                                             varKey);
    3892          20 :                         SetupOutputVariable(state,
    3893             :                                             "Daylighting Window Reference Point View Luminance",
    3894             :                                             Constant::Units::cd_m2,
    3895          10 :                                             refPt.lumWinRep,
    3896             :                                             OutputProcessor::TimeStepType::Zone,
    3897             :                                             OutputProcessor::StoreType::Average,
    3898             :                                             varKey);
    3899          10 :                     }
    3900           7 :                 } // for (controlNum)
    3901             :             }     // for (enclSurfNum)
    3902             :         }         // for (enclNum)
    3903             :     }
    3904             : 
    3905             :     // RJH DElight Modification Begin - Calls to DElight preprocessing subroutines
    3906         796 :     if (doesDayLightingUseDElight(state)) {
    3907           3 :         Real64 dLatitude = state.dataEnvrn->Latitude;
    3908           3 :         DisplayString(state, "Calculating DElight Daylighting Factors");
    3909           3 :         DElightManagerF::DElightInputGenerator(state);
    3910             :         // Init Error Flag to 0 (no Warnings or Errors)
    3911           3 :         DisplayString(state, "ReturnFrom DElightInputGenerator");
    3912           3 :         int iErrorFlag = 0;
    3913           3 :         DisplayString(state, "Calculating DElight DaylightCoefficients");
    3914           3 :         DElightManagerF::GenerateDElightDaylightCoefficients(dLatitude, iErrorFlag);
    3915             :         // Check Error Flag for Warnings or Errors returning from DElight
    3916             :         // RJH 2008-03-07: open file for READWRITE and DELETE file after processing
    3917           3 :         DisplayString(state, "ReturnFrom DElight DaylightCoefficients Calc");
    3918           3 :         if (iErrorFlag != 0) {
    3919             :             // Open DElight Daylight Factors Error File for reading
    3920           0 :             auto iDElightErrorFile = state.files.outputDelightDfdmpFilePath.try_open(state.files.outputControl.delightdfdmp); // (THIS_AUTO_OK)
    3921             : 
    3922             :             // Sequentially read lines in DElight Daylight Factors Error File
    3923             :             // and process them using standard EPlus warning/error handling calls
    3924             :             // Process all error/warning messages first
    3925             :             // Then, if any error has occurred, ShowFatalError to terminate processing
    3926           0 :             bool bEndofErrFile = !iDElightErrorFile.good();
    3927           0 :             std::string cErrorMsg; // Each DElight Error Message can be up to 200 characters long
    3928           0 :             while (!bEndofErrFile) {
    3929           0 :                 auto cErrorLine = iDElightErrorFile.readLine(); // (THIS_AUTO_OK)
    3930           0 :                 if (cErrorLine.eof) {
    3931           0 :                     bEndofErrFile = true;
    3932           0 :                     continue;
    3933             :                 }
    3934             :                 // Is the current line a Warning message?
    3935           0 :                 if (has_prefix(cErrorLine.data, "WARNING: ")) {
    3936           0 :                     cErrorMsg = cErrorLine.data.substr(9);
    3937           0 :                     ShowWarningError(state, cErrorMsg);
    3938             :                 }
    3939             :                 // Is the current line an Error message?
    3940           0 :                 if (has_prefix(cErrorLine.data, "ERROR: ")) {
    3941           0 :                     cErrorMsg = cErrorLine.data.substr(7);
    3942           0 :                     ShowSevereError(state, cErrorMsg);
    3943           0 :                     iErrorFlag = 1;
    3944             :                 }
    3945           0 :             }
    3946             : 
    3947             :             // Close and Delete DElight Error File
    3948           0 :             if (iDElightErrorFile.is_open()) {
    3949           0 :                 iDElightErrorFile.close();
    3950           0 :                 FileSystem::removeFile(iDElightErrorFile.filePath);
    3951             :             }
    3952             : 
    3953             :             // If any DElight Error occurred then ShowFatalError to terminate
    3954           0 :             if (iErrorFlag > 0) {
    3955           0 :                 ErrorsFound = true;
    3956             :             }
    3957           0 :         } else {
    3958           3 :             if (FileSystem::fileExists(state.files.outputDelightDfdmpFilePath.filePath)) {
    3959           3 :                 FileSystem::removeFile(state.files.outputDelightDfdmpFilePath.filePath);
    3960             :             }
    3961             :         }
    3962             :     }
    3963             :     // RJH DElight Modification End - Calls to DElight preprocessing subroutines
    3964             : 
    3965             :     // TH 6/3/2010, added to report daylight factors
    3966         796 :     ipsc->cCurrentModuleObject = "Output:DaylightFactors";
    3967         796 :     int NumReports = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, ipsc->cCurrentModuleObject);
    3968         796 :     if (NumReports > 0) {
    3969             :         int NumNames;
    3970             :         int NumNumbers;
    3971             :         int IOStat;
    3972           2 :         state.dataInputProcessing->inputProcessor->getObjectItem(state,
    3973           1 :                                                                  ipsc->cCurrentModuleObject,
    3974             :                                                                  1,
    3975           1 :                                                                  ipsc->cAlphaArgs,
    3976             :                                                                  NumNames,
    3977           1 :                                                                  ipsc->rNumericArgs,
    3978             :                                                                  NumNumbers,
    3979             :                                                                  IOStat,
    3980           1 :                                                                  ipsc->lNumericFieldBlanks,
    3981           1 :                                                                  ipsc->lAlphaFieldBlanks,
    3982           1 :                                                                  ipsc->cAlphaFieldNames,
    3983           1 :                                                                  ipsc->cNumericFieldNames);
    3984           1 :         if (has_prefix(ipsc->cAlphaArgs(1), "SIZINGDAYS")) {
    3985           1 :             dl->DFSReportSizingDays = true;
    3986           0 :         } else if (has_prefix(ipsc->cAlphaArgs(1), "ALLSHADOWCALCULATIONDAYS")) {
    3987           0 :             dl->DFSReportAllShadowCalculationDays = true;
    3988             :         }
    3989             :     }
    3990             : 
    3991         796 :     if (ErrorsFound) ShowFatalError(state, "Program terminated for above reasons");
    3992             : } // FigureMapPointDayltgFactorsToAddIllums()
    3993             : 
    3994          64 : void GetInputIlluminanceMap(EnergyPlusData &state, bool &ErrorsFound)
    3995             : {
    3996             :     // Perform the GetInput function for the Output:IlluminanceMap
    3997             :     // Glazer - June 2016 (moved from GetDaylightingControls)
    3998          64 :     auto &dl = state.dataDayltg;
    3999             : 
    4000          64 :     Array1D_bool ZoneMsgDone;
    4001             : 
    4002          64 :     Real64 CosBldgRelNorth = std::cos(-(state.dataHeatBal->BuildingAzimuth + state.dataHeatBal->BuildingRotationAppendixG) * Constant::DegToRadians);
    4003          64 :     Real64 SinBldgRelNorth = std::sin(-(state.dataHeatBal->BuildingAzimuth + state.dataHeatBal->BuildingRotationAppendixG) * Constant::DegToRadians);
    4004             :     // these are only for Building Rotation for Appendix G when using world coordinate system
    4005          64 :     Real64 CosBldgRotAppGonly = std::cos(-state.dataHeatBal->BuildingRotationAppendixG * Constant::DegToRadians);
    4006          64 :     Real64 SinBldgRotAppGonly = std::sin(-state.dataHeatBal->BuildingRotationAppendixG * Constant::DegToRadians);
    4007             : 
    4008          64 :     bool doTransform = false;
    4009          64 :     Real64 OldAspectRatio = 1.0;
    4010          64 :     Real64 NewAspectRatio = 1.0;
    4011             : 
    4012          64 :     CheckForGeometricTransform(state, doTransform, OldAspectRatio, NewAspectRatio);
    4013             : 
    4014          64 :     auto &ip = state.dataInputProcessing->inputProcessor;
    4015          64 :     auto const &ipsc = state.dataIPShortCut;
    4016          64 :     ipsc->cCurrentModuleObject = "Output:IlluminanceMap";
    4017          64 :     int TotIllumMaps = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, ipsc->cCurrentModuleObject);
    4018             : 
    4019          64 :     dl->illumMaps.allocate(TotIllumMaps);
    4020             : 
    4021          64 :     if (TotIllumMaps > 0) {
    4022             :         int IOStat;
    4023             :         int NumAlpha;
    4024             :         int NumNumber;
    4025          15 :         for (int MapNum = 1; MapNum <= TotIllumMaps; ++MapNum) {
    4026          18 :             ip->getObjectItem(state,
    4027           9 :                               ipsc->cCurrentModuleObject,
    4028             :                               MapNum,
    4029           9 :                               ipsc->cAlphaArgs,
    4030             :                               NumAlpha,
    4031           9 :                               ipsc->rNumericArgs,
    4032             :                               NumNumber,
    4033             :                               IOStat,
    4034           9 :                               ipsc->lNumericFieldBlanks,
    4035           9 :                               ipsc->lAlphaFieldBlanks,
    4036           9 :                               ipsc->cAlphaFieldNames,
    4037           9 :                               ipsc->cNumericFieldNames);
    4038             : 
    4039           9 :             auto &illumMap = dl->illumMaps(MapNum);
    4040           9 :             illumMap.Name = ipsc->cAlphaArgs(1);
    4041           9 :             int const zoneNum = Util::FindItemInList(ipsc->cAlphaArgs(2), state.dataHeatBal->Zone);
    4042           9 :             if (zoneNum > 0) {
    4043           8 :                 illumMap.zoneIndex = zoneNum;
    4044             :                 // set enclosure index for first space in zone
    4045           8 :                 int enclNum = state.dataHeatBal->space(state.dataHeatBal->Zone(zoneNum).spaceIndexes(1)).solarEnclosureNum;
    4046           8 :                 illumMap.enclIndex = enclNum;
    4047             :                 // check that all spaces in the zone are in the same enclosure
    4048           8 :                 for (int spaceCounter = 2; spaceCounter <= state.dataHeatBal->Zone(zoneNum).numSpaces; ++spaceCounter) {
    4049           0 :                     int spaceNum = state.dataHeatBal->Zone(zoneNum).spaceIndexes(spaceCounter);
    4050           0 :                     if (enclNum != state.dataHeatBal->space(spaceNum).solarEnclosureNum) {
    4051           0 :                         ShowSevereError(state,
    4052           0 :                                         format("{}=\"{}\" All spaces in the zone must be in the same enclosure for daylighting illuminance maps.",
    4053           0 :                                                ipsc->cCurrentModuleObject,
    4054           0 :                                                ipsc->cAlphaArgs(1)));
    4055           0 :                         ShowContinueError(
    4056           0 :                             state, format("Zone=\"{}\" spans multiple enclosures. Use a Space Name instead.", state.dataHeatBal->Zone(zoneNum).Name));
    4057           0 :                         ErrorsFound = true;
    4058           0 :                         break;
    4059             :                     }
    4060             :                 }
    4061             :             } else {
    4062           1 :                 int const spaceNum = Util::FindItemInList(state.dataIPShortCut->cAlphaArgs(2), state.dataHeatBal->space);
    4063           1 :                 if (spaceNum == 0) {
    4064           0 :                     ShowSevereError(state,
    4065           0 :                                     format("{}=\"{}\", invalid {}=\"{}\".",
    4066           0 :                                            ipsc->cCurrentModuleObject,
    4067           0 :                                            ipsc->cAlphaArgs(1),
    4068           0 :                                            ipsc->cAlphaFieldNames(2),
    4069           0 :                                            ipsc->cAlphaArgs(2)));
    4070           0 :                     ErrorsFound = true;
    4071             :                 } else {
    4072           1 :                     illumMap.spaceIndex = spaceNum;
    4073           1 :                     illumMap.zoneIndex = state.dataHeatBal->space(spaceNum).zoneNum;
    4074           1 :                     illumMap.enclIndex = state.dataHeatBal->space(spaceNum).solarEnclosureNum;
    4075           1 :                     assert(illumMap.enclIndex > 0);
    4076             :                 }
    4077             :             }
    4078             : 
    4079           9 :             illumMap.Z = ipsc->rNumericArgs(1);
    4080           9 :             illumMap.Xmin = ipsc->rNumericArgs(2);
    4081           9 :             illumMap.Xmax = ipsc->rNumericArgs(3);
    4082           9 :             if (ipsc->rNumericArgs(2) > ipsc->rNumericArgs(3)) {
    4083           0 :                 ShowSevereError(state, format("{}=\"{}\", invalid entry.", ipsc->cCurrentModuleObject, ipsc->cAlphaArgs(1)));
    4084           0 :                 ShowContinueError(state,
    4085           0 :                                   format("...{} {:.2R} must be <= {} {:.2R}.",
    4086           0 :                                          ipsc->cNumericFieldNames(2),
    4087           0 :                                          ipsc->rNumericArgs(2),
    4088           0 :                                          ipsc->cNumericFieldNames(3),
    4089           0 :                                          ipsc->rNumericArgs(3)));
    4090           0 :                 ErrorsFound = true;
    4091             :             }
    4092           9 :             illumMap.Xnum = ipsc->rNumericArgs(4);
    4093           9 :             illumMap.Xinc = (illumMap.Xnum != 1) ? ((illumMap.Xmax - illumMap.Xmin) / (illumMap.Xnum - 1)) : 0.0;
    4094             : 
    4095           9 :             illumMap.Ymin = ipsc->rNumericArgs(5);
    4096           9 :             illumMap.Ymax = ipsc->rNumericArgs(6);
    4097           9 :             if (ipsc->rNumericArgs(5) > ipsc->rNumericArgs(6)) {
    4098           0 :                 ShowSevereError(state, format("{}=\"{}\", invalid entry.", ipsc->cCurrentModuleObject, ipsc->cAlphaArgs(1)));
    4099           0 :                 ShowContinueError(state,
    4100           0 :                                   format("...{} {:.2R} must be <= {} {:.2R}.",
    4101           0 :                                          ipsc->cNumericFieldNames(5),
    4102           0 :                                          ipsc->rNumericArgs(5),
    4103           0 :                                          ipsc->cNumericFieldNames(6),
    4104           0 :                                          ipsc->rNumericArgs(6)));
    4105           0 :                 ErrorsFound = true;
    4106             :             }
    4107           9 :             illumMap.Ynum = ipsc->rNumericArgs(7);
    4108           9 :             illumMap.Yinc = (illumMap.Ynum != 1) ? ((illumMap.Ymax - illumMap.Ymin) / (illumMap.Ynum - 1)) : 0.0;
    4109             : 
    4110           9 :             if (illumMap.Xnum * illumMap.Ynum > MaxMapRefPoints) {
    4111           0 :                 ShowSevereError(state, format("{}=\"{}\", too many map points specified.", ipsc->cCurrentModuleObject, ipsc->cAlphaArgs(1)));
    4112           0 :                 ShowContinueError(state,
    4113           0 :                                   format("...{}[{}] * {}[{}].= [{}] must be <= [{}].",
    4114           0 :                                          ipsc->cNumericFieldNames(4),
    4115           0 :                                          illumMap.Xnum,
    4116           0 :                                          ipsc->cNumericFieldNames(7),
    4117           0 :                                          illumMap.Ynum,
    4118           0 :                                          illumMap.Xnum * illumMap.Ynum,
    4119             :                                          MaxMapRefPoints));
    4120           0 :                 ErrorsFound = true;
    4121             :             }
    4122             :         } // MapNum
    4123           6 :         ipsc->cCurrentModuleObject = "OutputControl:IlluminanceMap:Style";
    4124           6 :         int MapStyleIn = ip->getNumObjectsFound(state, ipsc->cCurrentModuleObject);
    4125             : 
    4126           6 :         if (MapStyleIn == 0) {
    4127           5 :             ipsc->cAlphaArgs(1) = "COMMA";
    4128           5 :             dl->MapColSep = DataStringGlobals::CharComma; // comma
    4129           1 :         } else if (MapStyleIn == 1) {
    4130           2 :             ip->getObjectItem(state,
    4131           1 :                               ipsc->cCurrentModuleObject,
    4132             :                               1,
    4133           1 :                               ipsc->cAlphaArgs,
    4134             :                               NumAlpha,
    4135           1 :                               ipsc->rNumericArgs,
    4136             :                               NumNumber,
    4137             :                               IOStat,
    4138           1 :                               ipsc->lNumericFieldBlanks,
    4139           1 :                               ipsc->lAlphaFieldBlanks,
    4140           1 :                               ipsc->cAlphaFieldNames,
    4141           1 :                               ipsc->cNumericFieldNames);
    4142           1 :             if (ipsc->cAlphaArgs(1) == "COMMA") {
    4143           1 :                 dl->MapColSep = DataStringGlobals::CharComma; // comma
    4144           0 :             } else if (ipsc->cAlphaArgs(1) == "TAB") {
    4145           0 :                 dl->MapColSep = DataStringGlobals::CharTab; // tab
    4146           0 :             } else if (ipsc->cAlphaArgs(1) == "FIXED" || ipsc->cAlphaArgs(1) == "SPACE") {
    4147           0 :                 dl->MapColSep = DataStringGlobals::CharSpace; // space
    4148             :             } else {
    4149           0 :                 dl->MapColSep = DataStringGlobals::CharComma; // comma
    4150           0 :                 ShowWarningError(state,
    4151           0 :                                  format("{}: invalid {}=\"{}\", Commas will be used to separate fields.",
    4152           0 :                                         ipsc->cCurrentModuleObject,
    4153           0 :                                         ipsc->cAlphaFieldNames(1),
    4154           0 :                                         ipsc->cAlphaArgs(1)));
    4155           0 :                 ipsc->cAlphaArgs(1) = "COMMA";
    4156             :             }
    4157             :         }
    4158           6 :         print(state.files.eio, "! <Daylighting:Illuminance Maps>,#Maps,Style\n");
    4159           6 :         ConvertCaseToLower(ipsc->cAlphaArgs(1), ipsc->cAlphaArgs(2));
    4160           6 :         ipsc->cAlphaArgs(1).erase(1);
    4161           6 :         ipsc->cAlphaArgs(1) += ipsc->cAlphaArgs(2).substr(1);
    4162           6 :         print(state.files.eio, "Daylighting:Illuminance Maps,{},{}\n", TotIllumMaps, ipsc->cAlphaArgs(1));
    4163             :     }
    4164             : 
    4165             :     // Check for illuminance maps associated with this zone
    4166          73 :     for (auto &illumMap : dl->illumMaps) {
    4167             : 
    4168           9 :         if (illumMap.zoneIndex == 0) continue;
    4169             : 
    4170           9 :         auto &zone = state.dataHeatBal->Zone(illumMap.zoneIndex);
    4171             :         // Calc cos and sin of Zone Relative North values for later use in transforming Reference Point coordinates
    4172           9 :         Real64 CosZoneRelNorth = std::cos(-zone.RelNorth * Constant::DegToRadians);
    4173           9 :         Real64 SinZoneRelNorth = std::sin(-zone.RelNorth * Constant::DegToRadians);
    4174             : 
    4175           9 :         if (illumMap.Xnum * illumMap.Ynum == 0) continue;
    4176             : 
    4177             :         // Add additional daylighting reference points for map
    4178           9 :         illumMap.TotalMapRefPoints = illumMap.Xnum * illumMap.Ynum;
    4179           9 :         illumMap.refPts.allocate(illumMap.TotalMapRefPoints);
    4180         634 :         for (auto &refPt : illumMap.refPts) {
    4181         625 :             new (&refPt) DaylMapPt();
    4182             :         }
    4183             : 
    4184           9 :         if (illumMap.TotalMapRefPoints > MaxMapRefPoints) {
    4185           0 :             ShowSevereError(state, "GetDaylighting Parameters: Total Map Reference points entered is greater than maximum allowed.");
    4186           0 :             ShowContinueError(state, format("Occurs in Zone={}", zone.Name));
    4187           0 :             ShowContinueError(state,
    4188           0 :                               format("Maximum reference points allowed={}, entered amount ( when error first occurred )={}",
    4189             :                                      MaxMapRefPoints,
    4190           0 :                                      illumMap.TotalMapRefPoints));
    4191           0 :             ErrorsFound = true;
    4192           0 :             break;
    4193             :         }
    4194             : 
    4195             :         // Calc cos and sin of Zone Relative North values for later use in transforming Map Point coordinates
    4196             :         // CosZoneRelNorth = std::cos( -zone.RelNorth * DegToRadians ); //Tuned These should not be changing
    4197             :         // SinZoneRelNorth = std::sin( -zone.RelNorth * DegToRadians );
    4198           9 :         illumMap.Xinc = (illumMap.Xnum != 1) ? ((illumMap.Xmax - illumMap.Xmin) / (illumMap.Xnum - 1)) : 0.0;
    4199           9 :         illumMap.Yinc = (illumMap.Ynum != 1) ? ((illumMap.Ymax - illumMap.Ymin) / (illumMap.Ynum - 1)) : 0.0;
    4200             : 
    4201             :         // Map points and increments are stored in AbsCoord and then that is operated on if relative coords entered.
    4202          94 :         for (int Y = 1; Y <= illumMap.Ynum; ++Y) {
    4203         710 :             for (int X = 1; X <= illumMap.Xnum; ++X) {
    4204         625 :                 int iRefPt = (Y - 1) * illumMap.Xnum + X;
    4205         625 :                 auto &refPt = illumMap.refPts(iRefPt);
    4206         625 :                 refPt.absCoords = {illumMap.Xmin + (X - 1) * illumMap.Xinc, illumMap.Ymin + (Y - 1) * illumMap.Yinc, illumMap.Z};
    4207             :             }
    4208             :         }
    4209             : 
    4210          94 :         for (int Y = 1; Y <= illumMap.Ynum; ++Y) {
    4211         710 :             for (int X = 1; X <= illumMap.Xnum; ++X) {
    4212         625 :                 int iRefPt = (Y - 1) * illumMap.Xnum + X;
    4213         625 :                 auto &refPt = illumMap.refPts(iRefPt);
    4214             : 
    4215         625 :                 if (!state.dataSurface->DaylRefWorldCoordSystem) {
    4216         425 :                     Real64 Xb = refPt.absCoords.x * CosZoneRelNorth - refPt.absCoords.y * SinZoneRelNorth + zone.OriginX;
    4217         425 :                     Real64 Yb = refPt.absCoords.x * SinZoneRelNorth + refPt.absCoords.y * CosZoneRelNorth + zone.OriginY;
    4218         425 :                     refPt.absCoords.x = Xb * CosBldgRelNorth - Yb * SinBldgRelNorth;
    4219         425 :                     refPt.absCoords.y = Xb * SinBldgRelNorth + Yb * CosBldgRelNorth;
    4220         425 :                     refPt.absCoords.z += zone.OriginZ;
    4221         425 :                     if (doTransform) {
    4222           0 :                         Real64 Xo = refPt.absCoords.x; // world coordinates.... shifted by relative north angle...
    4223           0 :                         Real64 Yo = refPt.absCoords.y;
    4224             :                         // next derotate the building
    4225           0 :                         Real64 XnoRot = Xo * CosBldgRelNorth + Yo * SinBldgRelNorth;
    4226           0 :                         Real64 YnoRot = Yo * CosBldgRelNorth - Xo * SinBldgRelNorth;
    4227             :                         // translate
    4228           0 :                         Real64 Xtrans = XnoRot * std::sqrt(NewAspectRatio / OldAspectRatio);
    4229           0 :                         Real64 Ytrans = YnoRot * std::sqrt(OldAspectRatio / NewAspectRatio);
    4230             :                         // rerotate
    4231           0 :                         refPt.absCoords.x = Xtrans * CosBldgRelNorth - Ytrans * SinBldgRelNorth;
    4232             : 
    4233           0 :                         refPt.absCoords.y = Xtrans * SinBldgRelNorth + Ytrans * CosBldgRelNorth;
    4234             :                     }
    4235             :                 } else {
    4236         200 :                     Real64 Xb = refPt.absCoords.x;
    4237         200 :                     Real64 Yb = refPt.absCoords.y;
    4238         200 :                     refPt.absCoords.x = Xb * CosBldgRotAppGonly - Yb * SinBldgRotAppGonly;
    4239         200 :                     refPt.absCoords.y = Xb * SinBldgRotAppGonly + Yb * CosBldgRotAppGonly;
    4240             :                 }
    4241         625 :                 if (iRefPt == 1) {
    4242           9 :                     illumMap.Xmin = refPt.absCoords.x;
    4243           9 :                     illumMap.Ymin = refPt.absCoords.y;
    4244           9 :                     illumMap.Xmax = refPt.absCoords.x;
    4245           9 :                     illumMap.Ymax = refPt.absCoords.y;
    4246           9 :                     illumMap.Z = refPt.absCoords.z;
    4247             :                 }
    4248         625 :                 illumMap.Xmin = min(illumMap.Xmin, refPt.absCoords.x);
    4249         625 :                 illumMap.Ymin = min(illumMap.Ymin, refPt.absCoords.y);
    4250         625 :                 illumMap.Xmax = max(illumMap.Xmax, refPt.absCoords.x);
    4251         625 :                 illumMap.Ymax = max(illumMap.Ymax, refPt.absCoords.y);
    4252         625 :                 if ((refPt.absCoords.x < zone.MinimumX && (zone.MinimumX - refPt.absCoords.x) > 0.001) ||
    4253         625 :                     (refPt.absCoords.x > zone.MaximumX && (refPt.absCoords.x - zone.MaximumX) > 0.001) ||
    4254         625 :                     (refPt.absCoords.y < zone.MinimumY && (zone.MinimumY - refPt.absCoords.y) > 0.001) ||
    4255         625 :                     (refPt.absCoords.y > zone.MaximumY && (refPt.absCoords.y - zone.MaximumY) > 0.001) ||
    4256         625 :                     (refPt.absCoords.z < zone.MinimumZ && (zone.MinimumZ - refPt.absCoords.z) > 0.001) ||
    4257         625 :                     (refPt.absCoords.z > zone.MaximumZ && (refPt.absCoords.z - zone.MaximumZ) > 0.001)) {
    4258           0 :                     refPt.inBounds = false;
    4259             :                 }
    4260             : 
    4261             :                 // Test extremes of Map Points against Zone Min/Max
    4262         643 :                 if (iRefPt != 1 && iRefPt != illumMap.TotalMapRefPoints) continue;
    4263             : 
    4264          18 :                 if (refPt.inBounds) continue;
    4265             : 
    4266           0 :                 if (refPt.absCoords.x < zone.MinimumX || refPt.absCoords.x > zone.MaximumX) {
    4267           0 :                     ShowWarningError(
    4268             :                         state,
    4269           0 :                         format("GetInputIlluminanceMap: Reference Map point #[{}], X Value outside Zone Min/Max X, Zone={}", iRefPt, zone.Name));
    4270           0 :                     ShowContinueError(state,
    4271           0 :                                       format("...X Reference Point= {:.2R}, Zone Minimum X= {:.2R}, Zone Maximum X= {:.2R}",
    4272           0 :                                              refPt.absCoords.x,
    4273           0 :                                              zone.MinimumX,
    4274           0 :                                              zone.MaximumX));
    4275           0 :                     ShowContinueError(
    4276             :                         state,
    4277           0 :                         format("...X Reference Distance Outside MinimumX= {:.4R} m.",
    4278           0 :                                (refPt.absCoords.x < zone.MinimumX) ? (zone.MinimumX - refPt.absCoords.x) : (refPt.absCoords.x - zone.MaximumX)));
    4279             :                 }
    4280           0 :                 if (refPt.absCoords.y < zone.MinimumY || refPt.absCoords.y > zone.MaximumY) {
    4281           0 :                     ShowWarningError(
    4282             :                         state,
    4283           0 :                         format("GetInputIlluminanceMap: Reference Map point #[{}], Y Value outside Zone Min/Max Y, Zone={}", iRefPt, zone.Name));
    4284           0 :                     ShowContinueError(state,
    4285           0 :                                       format("...Y Reference Point= {:.2R}, Zone Minimum Y= {:.2R}, Zone Maximum Y= {:.2R}",
    4286           0 :                                              refPt.absCoords.y,
    4287           0 :                                              zone.MinimumY,
    4288           0 :                                              zone.MaximumY));
    4289           0 :                     ShowContinueError(
    4290             :                         state,
    4291           0 :                         format("...Y Reference Distance Outside MinimumY= {:.4R} m.",
    4292           0 :                                (refPt.absCoords.y < zone.MinimumY) ? (zone.MinimumY - refPt.absCoords.y) : (refPt.absCoords.y - zone.MaximumY)));
    4293             :                 }
    4294           0 :                 if (refPt.absCoords.z < zone.MinimumZ || refPt.absCoords.z > zone.MaximumZ) {
    4295           0 :                     ShowWarningError(
    4296             :                         state,
    4297           0 :                         format("GetInputIlluminanceMap: Reference Map point #[{}], Z Value outside Zone Min/Max Z, Zone={}", iRefPt, zone.Name));
    4298           0 :                     ShowContinueError(state,
    4299           0 :                                       format("...Z Reference Point= {:.2R}, Zone Minimum Z= {:.2R}, Zone Maximum Z= {:.2R}",
    4300           0 :                                              refPt.absCoords.z,
    4301           0 :                                              zone.MinimumZ,
    4302           0 :                                              zone.MaximumZ));
    4303           0 :                     ShowContinueError(
    4304             :                         state,
    4305           0 :                         format("...Z Reference Distance Outside MinimumZ= {:.4R} m.",
    4306           0 :                                (refPt.absCoords.z < zone.MinimumZ) ? (zone.MinimumZ - refPt.absCoords.z) : (refPt.absCoords.z - zone.MaximumZ)));
    4307             :                 }
    4308             :             } // for (X)
    4309             :         }     // for (Y)
    4310             :     }         // for (MapNum)
    4311             : 
    4312          64 :     ZoneMsgDone.dimension(state.dataGlobal->NumOfZones, false);
    4313          73 :     for (auto const &illumMap : dl->illumMaps) {
    4314           9 :         if (illumMap.zoneIndex == 0) continue;
    4315           9 :         int enclNum = illumMap.enclIndex;
    4316           9 :         if (!dl->enclDaylight(enclNum).hasSplitFluxDaylighting && !ZoneMsgDone(illumMap.zoneIndex)) {
    4317           0 :             ShowSevereError(state,
    4318           0 :                             format("Zone Name in Output:IlluminanceMap is not used for Daylighting:Controls={}",
    4319           0 :                                    state.dataHeatBal->Zone(illumMap.zoneIndex).Name));
    4320           0 :             ErrorsFound = true;
    4321             :         }
    4322             :     }
    4323          64 :     ZoneMsgDone.deallocate();
    4324          64 :     if (ErrorsFound) return;
    4325             : 
    4326          64 :     if (TotIllumMaps > 0) {
    4327           6 :         print(state.files.eio,
    4328             :               "! <Daylighting:Illuminance Maps:Detail>,Name,Zone,XMin {{m}},XMax {{m}},Xinc {{m}},#X Points,YMin "
    4329             :               "{{m}},YMax {{m}},Yinc {{m}},#Y Points,Z {{m}}\n");
    4330             :     }
    4331          73 :     for (auto const &illumMap : dl->illumMaps) {
    4332           9 :         print(state.files.eio,
    4333             :               "Daylighting:Illuminance Maps:Detail,{},{},{:.2R},{:.2R},{:.2R},{},{:.2R},{:.2R},{:.2R},{},{:.2R}\n",
    4334           9 :               illumMap.Name,
    4335           9 :               state.dataHeatBal->Zone(illumMap.zoneIndex).Name,
    4336           9 :               illumMap.Xmin,
    4337           9 :               illumMap.Xmax,
    4338           9 :               illumMap.Xinc,
    4339           9 :               illumMap.Xnum,
    4340           9 :               illumMap.Ymin,
    4341           9 :               illumMap.Ymax,
    4342           9 :               illumMap.Yinc,
    4343           9 :               illumMap.Ynum,
    4344           9 :               illumMap.Z);
    4345             :     }
    4346             : 
    4347          64 : } // GetInputIlluminanceMap()
    4348             : 
    4349          64 : void GetDaylightingControls(EnergyPlusData &state, bool &ErrorsFound)
    4350             : {
    4351             :     //       AUTHOR         Fred Winkelmann
    4352             :     //       DATE WRITTEN   March 2002
    4353             :     //       MODIFIED       Glazer - July 2016 - Move geometry transformation portion, rearrange input, allow more than three reference points
    4354             :     // Obtain the user input data for Daylighting:Controls object in the input file.
    4355             : 
    4356          64 :     auto &dl = state.dataDayltg;
    4357             : 
    4358             :     int IOStat;
    4359             :     int NumAlpha;
    4360             :     int NumNumber;
    4361             : 
    4362             :     // Smallest deviation from unity for the sum of all fractions
    4363             :     // Accept approx 4 to 8 ULP error (technically abs(1.0 + sumFracs) should be close to 2)
    4364             :     //   constexpr Real64 FractionTolerance(4 * std::numeric_limits<Real64>::epsilon());
    4365             :     // Instead, we use a 0.001 = 0.1% tolerance
    4366          64 :     constexpr Real64 FractionTolerance(0.001);
    4367             : 
    4368          64 :     auto &ip = state.dataInputProcessing->inputProcessor;
    4369          64 :     auto const &ipsc = state.dataIPShortCut;
    4370          64 :     ipsc->cCurrentModuleObject = "Daylighting:Controls";
    4371          64 :     int totDaylightingControls = ip->getNumObjectsFound(state, ipsc->cCurrentModuleObject);
    4372          64 :     dl->daylightControl.allocate(totDaylightingControls);
    4373          64 :     Array1D<bool> spaceHasDaylightingControl;
    4374          64 :     spaceHasDaylightingControl.dimension(state.dataGlobal->numSpaces, false);
    4375             :     // Reset to zero in case this is called more than once in unit tests
    4376         785 :     for (int enclNum = 1; enclNum <= state.dataViewFactor->NumOfSolarEnclosures; ++enclNum) {
    4377         721 :         state.dataViewFactor->EnclSolInfo(enclNum).TotalEnclosureDaylRefPoints = 0;
    4378             :     }
    4379         354 :     for (int controlNum = 1; controlNum <= totDaylightingControls; ++controlNum) {
    4380         290 :         ipsc->cAlphaArgs = "";
    4381         290 :         ipsc->rNumericArgs = 0.0;
    4382         580 :         ip->getObjectItem(state,
    4383         290 :                           ipsc->cCurrentModuleObject,
    4384             :                           controlNum,
    4385         290 :                           ipsc->cAlphaArgs,
    4386             :                           NumAlpha,
    4387         290 :                           ipsc->rNumericArgs,
    4388             :                           NumNumber,
    4389             :                           IOStat,
    4390         290 :                           ipsc->lNumericFieldBlanks,
    4391         290 :                           ipsc->lAlphaFieldBlanks,
    4392         290 :                           ipsc->cAlphaFieldNames,
    4393         290 :                           ipsc->cNumericFieldNames);
    4394         290 :         auto &daylightControl = dl->daylightControl(controlNum);
    4395         290 :         daylightControl.Name = ipsc->cAlphaArgs(1);
    4396             : 
    4397             :         // Is it a zone or space name?
    4398         290 :         int const zoneNum = Util::FindItemInList(state.dataIPShortCut->cAlphaArgs(2), state.dataHeatBal->Zone);
    4399         290 :         if (zoneNum > 0) {
    4400         289 :             daylightControl.zoneIndex = zoneNum;
    4401             :             // set enclosure index for first space in zone
    4402         289 :             int enclNum = state.dataHeatBal->space(state.dataHeatBal->Zone(zoneNum).spaceIndexes(1)).solarEnclosureNum;
    4403         289 :             daylightControl.enclIndex = enclNum;
    4404             :             // check that all spaces in the zone are in the same enclosure
    4405         289 :             for (int spaceCounter = 2; spaceCounter <= state.dataHeatBal->Zone(zoneNum).numSpaces; ++spaceCounter) {
    4406           0 :                 int zoneSpaceNum = state.dataHeatBal->Zone(zoneNum).spaceIndexes(spaceCounter);
    4407           0 :                 if (daylightControl.enclIndex != state.dataHeatBal->space(zoneSpaceNum).solarEnclosureNum) {
    4408           0 :                     ShowSevereError(state,
    4409           0 :                                     format("{}: invalid {}=\"{}\" All spaces in the zone must be in the same enclosure for daylighting.",
    4410           0 :                                            ipsc->cCurrentModuleObject,
    4411           0 :                                            ipsc->cAlphaFieldNames(2),
    4412           0 :                                            ipsc->cAlphaArgs(2)));
    4413           0 :                     ErrorsFound = true;
    4414           0 :                     break;
    4415             :                 }
    4416             :             }
    4417         578 :             for (int zoneSpaceNum : state.dataHeatBal->Zone(zoneNum).spaceIndexes) {
    4418             :                 // Check if this is a duplicate
    4419         289 :                 if (spaceHasDaylightingControl(zoneSpaceNum)) {
    4420           0 :                     ShowWarningError(state,
    4421           0 :                                      format("{}=\"{}\" Space=\"{}\" already has a {} object assigned to it.",
    4422           0 :                                             ipsc->cCurrentModuleObject,
    4423           0 :                                             daylightControl.Name,
    4424           0 :                                             state.dataHeatBal->space(zoneSpaceNum).Name,
    4425           0 :                                             ipsc->cCurrentModuleObject));
    4426           0 :                     ShowContinueError(state, "This control will override the lighting power factor for this space.");
    4427             :                 }
    4428         289 :                 spaceHasDaylightingControl(zoneSpaceNum) = true;
    4429         289 :             }
    4430             :         } else {
    4431           1 :             int const spaceNum = Util::FindItemInList(state.dataIPShortCut->cAlphaArgs(2), state.dataHeatBal->space);
    4432           1 :             if (spaceNum == 0) {
    4433           0 :                 ShowSevereError(state, format("{}: invalid {}=\"{}\".", ipsc->cCurrentModuleObject, ipsc->cAlphaFieldNames(2), ipsc->cAlphaArgs(2)));
    4434           0 :                 ErrorsFound = true;
    4435           0 :                 continue;
    4436             :             } else {
    4437           1 :                 daylightControl.spaceIndex = spaceNum;
    4438           1 :                 daylightControl.zoneIndex = state.dataHeatBal->space(spaceNum).zoneNum;
    4439           1 :                 daylightControl.enclIndex = state.dataHeatBal->space(spaceNum).solarEnclosureNum;
    4440             :                 // Check if this is a duplicate
    4441           1 :                 if (spaceHasDaylightingControl(spaceNum)) {
    4442           0 :                     ShowWarningError(state,
    4443           0 :                                      format("{}=\"{}\" Space=\"{}\" already has a {} object assigned to it.",
    4444           0 :                                             ipsc->cCurrentModuleObject,
    4445           0 :                                             daylightControl.Name,
    4446           0 :                                             state.dataHeatBal->space(spaceNum).Name,
    4447           0 :                                             ipsc->cCurrentModuleObject));
    4448           0 :                     ShowContinueError(state, "This control will override the lighting power factor for this space.");
    4449             :                 }
    4450           1 :                 spaceHasDaylightingControl(spaceNum) = true;
    4451             :             }
    4452             :         }
    4453             : 
    4454         290 :         dl->enclDaylight(daylightControl.enclIndex).daylightControlIndexes.emplace_back(controlNum);
    4455         290 :         daylightControl.ZoneName = state.dataHeatBal->Zone(daylightControl.zoneIndex).Name;
    4456             : 
    4457         290 :         if (ipsc->lAlphaFieldBlanks(3)) {
    4458           0 :             daylightControl.DaylightMethod = DaylightingMethod::SplitFlux;
    4459             :         } else {
    4460         290 :             daylightControl.DaylightMethod =
    4461         290 :                 static_cast<DaylightingMethod>(getEnumValue(DaylightingMethodNamesUC, Util::makeUPPER(ipsc->cAlphaArgs(3))));
    4462             : 
    4463         290 :             if (daylightControl.DaylightMethod == DaylightingMethod::Invalid) {
    4464           0 :                 daylightControl.DaylightMethod = DaylightingMethod::SplitFlux;
    4465           0 :                 ShowWarningError(state,
    4466           0 :                                  format("Invalid {} = {}, occurs in {}object for {}=\"{}",
    4467           0 :                                         ipsc->cAlphaFieldNames(3),
    4468           0 :                                         ipsc->cAlphaArgs(3),
    4469           0 :                                         ipsc->cCurrentModuleObject,
    4470           0 :                                         ipsc->cCurrentModuleObject,
    4471           0 :                                         ipsc->cAlphaArgs(1)));
    4472           0 :                 ShowContinueError(state, "SplitFlux assumed, and the simulation continues.");
    4473             :             }
    4474             :         }
    4475         290 :         dl->enclDaylight(daylightControl.enclIndex).hasSplitFluxDaylighting |= (daylightControl.DaylightMethod == DaylightingMethod::SplitFlux);
    4476             : 
    4477         290 :         if (!ipsc->lAlphaFieldBlanks(4)) { // Field: Availability Schedule Name
    4478           6 :             daylightControl.AvailSchedNum = ScheduleManager::GetScheduleIndex(state, ipsc->cAlphaArgs(4));
    4479           6 :             if (daylightControl.AvailSchedNum == 0) {
    4480           0 :                 ShowWarningError(state,
    4481           0 :                                  format("Invalid {} = {}, occurs in {}object for {}=\"{}",
    4482           0 :                                         ipsc->cAlphaFieldNames(4),
    4483           0 :                                         ipsc->cAlphaArgs(4),
    4484           0 :                                         ipsc->cCurrentModuleObject,
    4485           0 :                                         ipsc->cCurrentModuleObject,
    4486           0 :                                         ipsc->cAlphaArgs(1)));
    4487           0 :                 ShowContinueError(state, "Schedule was not found so controls will always be available, and the simulation continues.");
    4488           0 :                 daylightControl.AvailSchedNum = ScheduleManager::ScheduleAlwaysOn;
    4489             :             }
    4490             :         } else {
    4491         284 :             daylightControl.AvailSchedNum = ScheduleManager::ScheduleAlwaysOn;
    4492             :         }
    4493             : 
    4494         290 :         daylightControl.LightControlType = static_cast<LtgCtrlType>(getEnumValue(LtgCtrlTypeNamesUC, Util::makeUPPER(ipsc->cAlphaArgs(5))));
    4495         290 :         if (daylightControl.LightControlType == LtgCtrlType::Invalid) {
    4496           0 :             ShowWarningError(state,
    4497           0 :                              format("Invalid {} = {}, occurs in {}object for {}=\"{}",
    4498           0 :                                     ipsc->cAlphaFieldNames(5),
    4499           0 :                                     ipsc->cAlphaArgs(5),
    4500           0 :                                     ipsc->cCurrentModuleObject,
    4501           0 :                                     ipsc->cCurrentModuleObject,
    4502           0 :                                     ipsc->cAlphaArgs(1)));
    4503           0 :             ShowContinueError(state, "Continuous assumed, and the simulation continues.");
    4504             :         }
    4505             : 
    4506         290 :         daylightControl.MinPowerFraction = ipsc->rNumericArgs(1);  // Field: Minimum Input Power Fraction for Continuous Dimming Control
    4507         290 :         daylightControl.MinLightFraction = ipsc->rNumericArgs(2);  // Field: Minimum Light Output Fraction for Continuous Dimming Control
    4508         290 :         daylightControl.LightControlSteps = ipsc->rNumericArgs(3); // Field: Number of Stepped Control Steps
    4509         290 :         daylightControl.LightControlProbability =
    4510         290 :             ipsc->rNumericArgs(4); // Field: Probability Lighting will be Reset When Needed in Manual Stepped Control
    4511             : 
    4512         290 :         if (!ipsc->lAlphaFieldBlanks(6)) { // Field: Glare Calculation Daylighting Reference Point Name
    4513         286 :             daylightControl.glareRefPtNumber = Util::FindItemInList(ipsc->cAlphaArgs(6),
    4514         286 :                                                                     dl->DaylRefPt,
    4515             :                                                                     &RefPointData::Name); // Field: Glare Calculation Daylighting Reference Point Name
    4516         286 :             if (daylightControl.glareRefPtNumber == 0) {
    4517           0 :                 ShowSevereError(state,
    4518           0 :                                 format("{}: invalid {}=\"{}\" for object named: {}",
    4519           0 :                                        ipsc->cCurrentModuleObject,
    4520           0 :                                        ipsc->cAlphaFieldNames(6),
    4521           0 :                                        ipsc->cAlphaArgs(6),
    4522           0 :                                        ipsc->cAlphaArgs(1)));
    4523           0 :                 ErrorsFound = true;
    4524           0 :                 continue;
    4525             :             }
    4526           4 :         } else if (daylightControl.DaylightMethod == DaylightingMethod::SplitFlux) {
    4527           1 :             ShowWarningError(state, format("No {} provided for object named: {}", ipsc->cAlphaFieldNames(6), ipsc->cAlphaArgs(1)));
    4528           1 :             ShowContinueError(state, "No glare calculation performed, and the simulation continues.");
    4529             :         }
    4530             : 
    4531             :         // Field: Glare Calculation Azimuth Angle of View Direction Clockwise from Zone y-Axis
    4532         290 :         daylightControl.ViewAzimuthForGlare = !ipsc->lNumericFieldBlanks(5) ? ipsc->rNumericArgs(5) : 0.0;
    4533             : 
    4534         290 :         daylightControl.MaxGlareallowed = ipsc->rNumericArgs(6);           // Field: Maximum Allowable Discomfort Glare Index
    4535         290 :         daylightControl.DElightGriddingResolution = ipsc->rNumericArgs(7); // Field: DElight Gridding Resolution
    4536             : 
    4537         290 :         int curTotalDaylRefPts = NumAlpha - 6; // first six alpha fields are not part of extensible group
    4538         290 :         daylightControl.TotalDaylRefPoints = curTotalDaylRefPts;
    4539         290 :         state.dataViewFactor->EnclSolInfo(daylightControl.enclIndex).TotalEnclosureDaylRefPoints += curTotalDaylRefPts;
    4540         290 :         dl->ZoneDaylight(daylightControl.zoneIndex).totRefPts += curTotalDaylRefPts;
    4541         290 :         dl->maxControlRefPoints = max(dl->maxControlRefPoints, curTotalDaylRefPts);
    4542         290 :         if ((NumNumber - 7) / 2 != daylightControl.TotalDaylRefPoints) {
    4543           0 :             ShowSevereError(state,
    4544           0 :                             format("{}The number of extensible numeric fields and alpha fields is inconsistent for: {}",
    4545           0 :                                    ipsc->cCurrentModuleObject,
    4546           0 :                                    ipsc->cAlphaArgs(1)));
    4547           0 :             ShowContinueError(state,
    4548           0 :                               format("For each field: {} there needs to be the following fields: Fraction Controlled by Reference Point and "
    4549             :                                      "Illuminance Setpoint at Reference Point",
    4550           0 :                                      ipsc->cAlphaFieldNames(NumAlpha)));
    4551           0 :             ErrorsFound = true;
    4552             :         }
    4553             : 
    4554         290 :         daylightControl.refPts.allocate(curTotalDaylRefPts);
    4555             : 
    4556         759 :         for (auto &refPt : daylightControl.refPts) {
    4557         469 :             refPt = DaylRefPt();
    4558             :         }
    4559             : 
    4560         290 :         int countRefPts = 0;
    4561         759 :         for (int refPtNum = 1; refPtNum <= curTotalDaylRefPts; ++refPtNum) {
    4562         469 :             auto &refPt = daylightControl.refPts(refPtNum);
    4563         469 :             refPt.num =
    4564         469 :                 Util::FindItemInList(ipsc->cAlphaArgs(6 + refPtNum), dl->DaylRefPt, &RefPointData::Name); // Field: Daylighting Reference Point Name
    4565         469 :             if (refPt.num == 0) {
    4566           0 :                 ShowSevereError(state,
    4567           0 :                                 format("{}: invalid {}=\"{}\" for object named: {}",
    4568           0 :                                        ipsc->cCurrentModuleObject,
    4569           0 :                                        ipsc->cAlphaFieldNames(6 + refPtNum),
    4570           0 :                                        ipsc->cAlphaArgs(6 + refPtNum),
    4571           0 :                                        ipsc->cAlphaArgs(1)));
    4572           0 :                 ErrorsFound = true;
    4573           0 :                 continue;
    4574             :             } else {
    4575         469 :                 ++countRefPts;
    4576             :             }
    4577         469 :             refPt.fracZoneDaylit = ipsc->rNumericArgs(6 + refPtNum * 2); // Field: Fraction Controlled by Reference Point
    4578         469 :             refPt.illumSetPoint = ipsc->rNumericArgs(7 + refPtNum * 2);  // Field: Illuminance Setpoint at Reference Point
    4579             : 
    4580         469 :             if (daylightControl.DaylightMethod == DaylightingMethod::SplitFlux) {
    4581        1389 :                 SetupOutputVariable(state,
    4582         926 :                                     format("Daylighting Reference Point {} Illuminance", refPtNum),
    4583             :                                     Constant::Units::lux,
    4584         463 :                                     refPt.lums[iLum_Illum],
    4585             :                                     OutputProcessor::TimeStepType::Zone,
    4586             :                                     OutputProcessor::StoreType::Average,
    4587         463 :                                     daylightControl.Name);
    4588        1389 :                 SetupOutputVariable(state,
    4589         926 :                                     format("Daylighting Reference Point {} Daylight Illuminance Setpoint Exceeded Time", refPtNum),
    4590             :                                     Constant::Units::hr,
    4591         463 :                                     refPt.timeExceedingDaylightIlluminanceSetPoint,
    4592             :                                     OutputProcessor::TimeStepType::Zone,
    4593             :                                     OutputProcessor::StoreType::Sum,
    4594         463 :                                     daylightControl.Name);
    4595        1389 :                 SetupOutputVariable(state,
    4596         926 :                                     format("Daylighting Reference Point {} Glare Index", refPtNum),
    4597             :                                     Constant::Units::None,
    4598         463 :                                     refPt.glareIndex,
    4599             :                                     OutputProcessor::TimeStepType::Zone,
    4600             :                                     OutputProcessor::StoreType::Average,
    4601         463 :                                     daylightControl.Name);
    4602        1389 :                 SetupOutputVariable(state,
    4603         926 :                                     format("Daylighting Reference Point {} Glare Index Setpoint Exceeded Time", refPtNum),
    4604             :                                     Constant::Units::hr,
    4605         463 :                                     refPt.timeExceedingGlareIndexSetPoint,
    4606             :                                     OutputProcessor::TimeStepType::Zone,
    4607             :                                     OutputProcessor::StoreType::Sum,
    4608         463 :                                     daylightControl.Name);
    4609             :             } // if (DaylightMethod == SplitFlux)
    4610             :         }     // for (RefPtNum)
    4611             : 
    4612             :         // Register Error if 0 DElight RefPts have been input for valid DElight object
    4613         290 :         if (countRefPts < 1) {
    4614           0 :             ShowSevereError(state, format("No Reference Points input for {} zone ={}", ipsc->cCurrentModuleObject, daylightControl.ZoneName));
    4615           0 :             ErrorsFound = true;
    4616             :         }
    4617             : 
    4618         290 :         Real64 sumFracs = 0.0;
    4619         759 :         for (auto const &refPt : daylightControl.refPts)
    4620         469 :             sumFracs += refPt.fracZoneDaylit;
    4621             : 
    4622         290 :         daylightControl.sumFracLights = sumFracs;
    4623         290 :         if ((1.0 - sumFracs) > FractionTolerance) {
    4624         166 :             ShowWarningError(state, "GetDaylightingControls: Fraction of zone or space controlled by the Daylighting reference points is < 1.0.");
    4625         332 :             ShowContinueError(state,
    4626         332 :                               format("..discovered in {}=\"{}\", only {:.3R} of the zone or space is controlled.",
    4627         166 :                                      ipsc->cCurrentModuleObject,
    4628         166 :                                      daylightControl.Name,
    4629             :                                      sumFracs));
    4630         124 :         } else if ((sumFracs - 1.0) > FractionTolerance) {
    4631           0 :             ShowSevereError(state, "GetDaylightingControls: Fraction of zone or space controlled by the Daylighting reference points is > 1.0.");
    4632           0 :             ShowContinueError(state,
    4633           0 :                               format("..discovered in {}=\"{}\", trying to control {:.3R} of the zone or space.",
    4634           0 :                                      ipsc->cCurrentModuleObject,
    4635           0 :                                      daylightControl.Name,
    4636             :                                      sumFracs));
    4637           0 :             ErrorsFound = true;
    4638             :         }
    4639             : 
    4640         290 :         if (daylightControl.LightControlType == LtgCtrlType::Stepped && daylightControl.LightControlSteps <= 0) {
    4641           0 :             ShowWarningError(state, "GetDaylightingControls: For Stepped Control, the number of steps must be > 0");
    4642           0 :             ShowContinueError(state, format("..discovered in \"{}\" for Zone=\"{}\", will use 1", ipsc->cCurrentModuleObject, ipsc->cAlphaArgs(2)));
    4643           0 :             daylightControl.LightControlSteps = 1;
    4644             :         }
    4645         580 :         SetupOutputVariable(state,
    4646             :                             "Daylighting Lighting Power Multiplier",
    4647             :                             Constant::Units::None,
    4648         290 :                             daylightControl.PowerReductionFactor,
    4649             :                             OutputProcessor::TimeStepType::Zone,
    4650             :                             OutputProcessor::StoreType::Average,
    4651         290 :                             daylightControl.Name);
    4652             :     } // for (controlNum)
    4653          64 : } // GetDaylightingControls()
    4654             : 
    4655          64 : void GeometryTransformForDaylighting(EnergyPlusData &state)
    4656             : {
    4657             :     //       AUTHOR         Fred Winkelmann
    4658             :     //       DATE WRITTEN   March 2002
    4659             :     //       MODIFIED       Glazer - July 2016 - separated this from GetInput function
    4660             :     // For splitflux daylighting, transform the geometry
    4661          64 :     auto &dl = state.dataDayltg;
    4662             : 
    4663             :     // Calc cos and sin of Building Relative North values for later use in transforming Reference Point coordinates
    4664          64 :     Real64 CosBldgRelNorth = std::cos(-(state.dataHeatBal->BuildingAzimuth + state.dataHeatBal->BuildingRotationAppendixG) * Constant::DegToRadians);
    4665          64 :     Real64 SinBldgRelNorth = std::sin(-(state.dataHeatBal->BuildingAzimuth + state.dataHeatBal->BuildingRotationAppendixG) * Constant::DegToRadians);
    4666             :     // these are only for Building Rotation for Appendix G when using world coordinate system
    4667          64 :     Real64 CosBldgRotAppGonly = std::cos(-state.dataHeatBal->BuildingRotationAppendixG * Constant::DegToRadians);
    4668          64 :     Real64 SinBldgRotAppGonly = std::sin(-state.dataHeatBal->BuildingRotationAppendixG * Constant::DegToRadians);
    4669             : 
    4670          64 :     bool doTransform = false;
    4671          64 :     Real64 OldAspectRatio = 1.0;
    4672          64 :     Real64 NewAspectRatio = 1.0;
    4673             : 
    4674          64 :     CheckForGeometricTransform(state, doTransform, OldAspectRatio, NewAspectRatio);
    4675         354 :     for (auto &daylCntrl : dl->daylightControl) {
    4676         290 :         auto &zone = state.dataHeatBal->Zone(daylCntrl.zoneIndex);
    4677             : 
    4678             :         // Calc cos and sin of Zone Relative North values for later use in transforming Reference Point coordinates
    4679         290 :         Real64 CosZoneRelNorth = std::cos(-zone.RelNorth * Constant::DegToRadians);
    4680         290 :         Real64 SinZoneRelNorth = std::sin(-zone.RelNorth * Constant::DegToRadians);
    4681             : 
    4682         290 :         Real64 rLightLevel = InternalHeatGains::GetDesignLightingLevelForZone(state, daylCntrl.zoneIndex);
    4683         290 :         InternalHeatGains::CheckLightsReplaceableMinMaxForZone(state, daylCntrl.zoneIndex);
    4684             : 
    4685         759 :         for (int refPtNum = 1; refPtNum <= daylCntrl.TotalDaylRefPoints; ++refPtNum) {
    4686         469 :             auto &refPt = daylCntrl.refPts(refPtNum);
    4687         469 :             auto &curRefPt = dl->DaylRefPt(refPt.num); // get the active daylighting:referencepoint
    4688         469 :             curRefPt.indexToFracAndIllum = refPtNum;   // back reference to the index to the ZoneDaylight structure arrays related to reference points
    4689         469 :             if (state.dataSurface->DaylRefWorldCoordSystem) {
    4690             :                 // transform only by appendix G rotation
    4691           7 :                 refPt.absCoords.x = curRefPt.coords.x * CosBldgRotAppGonly - curRefPt.coords.y * SinBldgRotAppGonly;
    4692           7 :                 refPt.absCoords.y = curRefPt.coords.x * SinBldgRotAppGonly + curRefPt.coords.y * CosBldgRotAppGonly;
    4693           7 :                 refPt.absCoords.z = curRefPt.coords.z;
    4694             :             } else {
    4695             :                 // Transform reference point coordinates into building coordinate system
    4696         462 :                 Real64 Xb = curRefPt.coords.x * CosZoneRelNorth - curRefPt.coords.y * SinZoneRelNorth + zone.OriginX;
    4697         462 :                 Real64 Yb = curRefPt.coords.x * SinZoneRelNorth + curRefPt.coords.y * CosZoneRelNorth + zone.OriginY;
    4698             :                 // Transform into World Coordinate System
    4699         462 :                 refPt.absCoords.x = Xb * CosBldgRelNorth - Yb * SinBldgRelNorth;
    4700         462 :                 refPt.absCoords.y = Xb * SinBldgRelNorth + Yb * CosBldgRelNorth;
    4701         462 :                 refPt.absCoords.z = curRefPt.coords.z + zone.OriginZ;
    4702         462 :                 if (doTransform) {
    4703           0 :                     Real64 Xo = refPt.absCoords.x; // world coordinates.... shifted by relative north angle...
    4704           0 :                     Real64 Yo = refPt.absCoords.y;
    4705             :                     // next derotate the building
    4706           0 :                     Real64 XnoRot = Xo * CosBldgRelNorth + Yo * SinBldgRelNorth;
    4707           0 :                     Real64 YnoRot = Yo * CosBldgRelNorth - Xo * SinBldgRelNorth;
    4708             :                     // translate
    4709           0 :                     Real64 Xtrans = XnoRot * std::sqrt(NewAspectRatio / OldAspectRatio);
    4710           0 :                     Real64 Ytrans = YnoRot * std::sqrt(OldAspectRatio / NewAspectRatio);
    4711             :                     // rerotate
    4712           0 :                     refPt.absCoords.x = Xtrans * CosBldgRelNorth - Ytrans * SinBldgRelNorth;
    4713           0 :                     refPt.absCoords.y = Xtrans * SinBldgRelNorth + Ytrans * CosBldgRelNorth;
    4714             :                 }
    4715             :             }
    4716             : 
    4717         469 :             auto &orp = state.dataOutRptPredefined;
    4718         469 :             OutputReportPredefined::PreDefTableEntry(state, orp->pdchDyLtZone, curRefPt.Name, daylCntrl.ZoneName);
    4719         469 :             OutputReportPredefined::PreDefTableEntry(state, orp->pdchDyLtCtrlName, curRefPt.Name, daylCntrl.Name);
    4720         469 :             if (daylCntrl.DaylightMethod == DaylightingMethod::SplitFlux) {
    4721         463 :                 OutputReportPredefined::PreDefTableEntry(state, orp->pdchDyLtKind, curRefPt.Name, "SplitFlux");
    4722             :             } else {
    4723           6 :                 OutputReportPredefined::PreDefTableEntry(state, orp->pdchDyLtKind, curRefPt.Name, "DElight");
    4724             :             }
    4725             :             // ( 1=continuous, 2=stepped, 3=continuous/off )
    4726         469 :             if (daylCntrl.LightControlType == LtgCtrlType::Continuous) {
    4727          60 :                 OutputReportPredefined::PreDefTableEntry(state, orp->pdchDyLtCtrlType, curRefPt.Name, "Continuous");
    4728         409 :             } else if (daylCntrl.LightControlType == LtgCtrlType::Stepped) {
    4729         131 :                 OutputReportPredefined::PreDefTableEntry(state, orp->pdchDyLtCtrlType, curRefPt.Name, "Stepped");
    4730         278 :             } else if (daylCntrl.LightControlType == LtgCtrlType::ContinuousOff) {
    4731         278 :                 OutputReportPredefined::PreDefTableEntry(state, orp->pdchDyLtCtrlType, curRefPt.Name, "Continuous/Off");
    4732             :             }
    4733         469 :             OutputReportPredefined::PreDefTableEntry(state, orp->pdchDyLtFrac, curRefPt.Name, refPt.fracZoneDaylit);
    4734         469 :             OutputReportPredefined::PreDefTableEntry(state, orp->pdchDyLtWInst, curRefPt.Name, rLightLevel);
    4735         469 :             OutputReportPredefined::PreDefTableEntry(state, orp->pdchDyLtWCtrl, curRefPt.Name, rLightLevel * refPt.fracZoneDaylit);
    4736             : 
    4737         469 :             if (refPt.absCoords.x < zone.MinimumX || refPt.absCoords.x > zone.MaximumX) {
    4738           0 :                 refPt.inBounds = false;
    4739           0 :                 ShowWarningError(state,
    4740           0 :                                  format("GeometryTransformForDaylighting: Reference point X Value outside Zone Min/Max X, Zone={}", zone.Name));
    4741           0 :                 ShowContinueError(state,
    4742           0 :                                   format("...X Reference Point= {:.2R}, Zone Minimum X= {:.2R}, Zone Maximum X= {:.2R}",
    4743           0 :                                          refPt.absCoords.x,
    4744           0 :                                          zone.MinimumX,
    4745           0 :                                          zone.MaximumX));
    4746           0 :                 ShowContinueError(
    4747             :                     state,
    4748           0 :                     format("...X Reference Distance Outside MinimumX= {:.4R} m.",
    4749           0 :                            (refPt.absCoords.x < zone.MinimumX) ? (zone.MinimumX - refPt.absCoords.x) : (refPt.absCoords.x - zone.MaximumX)));
    4750             :             }
    4751         469 :             if (refPt.absCoords.y < zone.MinimumY || refPt.absCoords.y > zone.MaximumY) {
    4752           0 :                 refPt.inBounds = false;
    4753           0 :                 ShowWarningError(state,
    4754           0 :                                  format("GeometryTransformForDaylighting: Reference point Y Value outside Zone Min/Max Y, Zone={}", zone.Name));
    4755           0 :                 ShowContinueError(state,
    4756           0 :                                   format("...Y Reference Point= {:.2R}, Zone Minimum Y= {:.2R}, Zone Maximum Y= {:.2R}",
    4757           0 :                                          refPt.absCoords.x,
    4758           0 :                                          zone.MinimumY,
    4759           0 :                                          zone.MaximumY));
    4760           0 :                 ShowContinueError(
    4761             :                     state,
    4762           0 :                     format("...Y Reference Distance Outside MinimumY= {:.4R} m.",
    4763           0 :                            (refPt.absCoords.y < zone.MinimumY) ? (zone.MinimumY - refPt.absCoords.y) : (refPt.absCoords.y - zone.MaximumY)));
    4764             :             }
    4765         469 :             if (refPt.absCoords.z < zone.MinimumZ || refPt.absCoords.z > zone.MaximumZ) {
    4766           0 :                 refPt.inBounds = false;
    4767           0 :                 ShowWarningError(state,
    4768           0 :                                  format("GeometryTransformForDaylighting: Reference point Z Value outside Zone Min/Max Z, Zone={}", zone.Name));
    4769           0 :                 ShowContinueError(state,
    4770           0 :                                   format("...Z Reference Point= {:.2R}, Zone Minimum Z= {:.2R}, Zone Maximum Z= {:.2R}",
    4771           0 :                                          refPt.absCoords.z,
    4772           0 :                                          zone.MinimumZ,
    4773           0 :                                          zone.MaximumZ));
    4774           0 :                 ShowContinueError(
    4775             :                     state,
    4776           0 :                     format("...Z Reference Distance Outside MinimumZ= {:.4R} m.",
    4777           0 :                            (refPt.absCoords.z < zone.MinimumZ) ? (zone.MinimumZ - refPt.absCoords.z) : (refPt.absCoords.z - zone.MaximumZ)));
    4778             :             }
    4779             :         } // for (refPt)
    4780             :     }     // for (daylightCtrl)
    4781          64 : } // GeometryTransformForDaylighting()
    4782             : 
    4783          64 : void GetInputDayliteRefPt(EnergyPlusData &state, bool &ErrorsFound)
    4784             : {
    4785             :     // Perform GetInput function for the Daylighting:ReferencePoint object
    4786             :     // Glazer - July 2016
    4787          64 :     auto &dl = state.dataDayltg;
    4788          64 :     auto &ip = state.dataInputProcessing->inputProcessor;
    4789          64 :     auto const &ipsc = state.dataIPShortCut;
    4790          64 :     ipsc->cCurrentModuleObject = "Daylighting:ReferencePoint";
    4791             : 
    4792          64 :     int RefPtNum = 0;
    4793             :     int IOStat;
    4794             :     int NumAlpha;
    4795             :     int NumNumber;
    4796             : 
    4797          64 :     int TotRefPoints = ip->getNumObjectsFound(state, ipsc->cCurrentModuleObject);
    4798             : 
    4799          64 :     dl->DaylRefPt.allocate(TotRefPoints);
    4800         533 :     for (auto &pt : dl->DaylRefPt) {
    4801         938 :         ip->getObjectItem(state,
    4802         469 :                           ipsc->cCurrentModuleObject,
    4803             :                           ++RefPtNum,
    4804         469 :                           ipsc->cAlphaArgs,
    4805             :                           NumAlpha,
    4806         469 :                           ipsc->rNumericArgs,
    4807             :                           NumNumber,
    4808             :                           IOStat,
    4809         469 :                           ipsc->lNumericFieldBlanks,
    4810         469 :                           ipsc->lAlphaFieldBlanks,
    4811         469 :                           ipsc->cAlphaFieldNames,
    4812         469 :                           ipsc->cNumericFieldNames);
    4813         469 :         pt.Name = ipsc->cAlphaArgs(1);
    4814         469 :         pt.ZoneNum = Util::FindItemInList(ipsc->cAlphaArgs(2), state.dataHeatBal->Zone);
    4815         469 :         if (pt.ZoneNum == 0) {
    4816           1 :             int spaceNum = Util::FindItemInList(ipsc->cAlphaArgs(2), state.dataHeatBal->space);
    4817           1 :             if (spaceNum == 0) {
    4818           0 :                 ShowSevereError(state,
    4819           0 :                                 format("{}=\"{}\", invalid {}=\"{}\".",
    4820           0 :                                        ipsc->cCurrentModuleObject,
    4821           0 :                                        ipsc->cAlphaArgs(1),
    4822           0 :                                        ipsc->cAlphaFieldNames(2),
    4823           0 :                                        ipsc->cAlphaArgs(2)));
    4824           0 :                 ErrorsFound = true;
    4825             :             } else {
    4826           1 :                 pt.ZoneNum = state.dataHeatBal->space(spaceNum).zoneNum;
    4827             :             }
    4828             :         }
    4829         469 :         pt.coords = {ipsc->rNumericArgs(1), ipsc->rNumericArgs(2), ipsc->rNumericArgs(3)};
    4830             :     }
    4831          64 : }
    4832             : 
    4833        1063 : bool doesDayLightingUseDElight(EnergyPlusData &state)
    4834             : {
    4835        1063 :     auto &dl = state.dataDayltg;
    4836        1347 :     for (auto const &znDayl : dl->daylightControl) {
    4837         287 :         if (znDayl.DaylightMethod == DaylightingMethod::DElight) {
    4838           3 :             return true;
    4839             :         }
    4840             :     }
    4841        1060 :     return false;
    4842             : }
    4843             : 
    4844         796 : void CheckTDDsAndLightShelvesInDaylitZones(EnergyPlusData &state)
    4845             : {
    4846             :     // SUBROUTINE INFORMATION:
    4847             :     //       AUTHOR         Brent Griffith
    4848             :     //       DATE WRITTEN   Dec 2007
    4849             : 
    4850             :     // PURPOSE OF THIS SUBROUTINE:
    4851             :     // This subroutine checks daylighting input for TDDs and light shelfs
    4852             :     //  which need to be checked after daylighting input has been read in (CR 7145)
    4853             :     //  (eventually this should be changed once/if implementations change to decouple from daylighting calcs so that
    4854             :     //  these devices can be used in models without daylighting controls
    4855             :     // CR 7145 was for TDDs, but also implenting check for light shelves, the other "daylighting device"
    4856             : 
    4857             :     // METHODOLOGY EMPLOYED:
    4858             :     // loop thru daylighting devices and check that their zones have daylight controls
    4859             : 
    4860             :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    4861         796 :     bool ErrorsFound = false;
    4862             : 
    4863         798 :     for (auto const &pipe : state.dataDaylightingDevicesData->TDDPipe) {
    4864           2 :         int SurfNum = pipe.Diffuser;
    4865           2 :         if (SurfNum > 0) {
    4866           2 :             int const pipeEnclNum = state.dataSurface->Surface(SurfNum).SolarEnclIndex;
    4867           2 :             if (state.dataViewFactor->EnclSolInfo(pipeEnclNum).TotalEnclosureDaylRefPoints == 0) {
    4868           0 :                 ShowWarningError(state,
    4869           0 :                                  format("DaylightingDevice:Tubular = {}:  is not connected to a Zone that has Daylighting, no visible transmittance "
    4870             :                                         "will be modeled through the daylighting device.",
    4871           0 :                                         pipe.Name));
    4872             :             }
    4873             :         } else { // SurfNum == 0
    4874             :             // should not come here (would have already been caught in TDD get input), but is an error
    4875           0 :             ShowSevereError(state, format("DaylightingDevice:Tubular = {}:  Diffuser surface not found ", pipe.Name));
    4876           0 :             ErrorsFound = true;
    4877             :         }
    4878             :     } // for (pipe)
    4879             : 
    4880         797 :     for (auto const &shelf : state.dataDaylightingDevicesData->Shelf) {
    4881           1 :         if (shelf.Window == 0) {
    4882             :             // should not come here (would have already been caught in shelf get input), but is an error
    4883           0 :             ShowSevereError(state, format("DaylightingDevice:Shelf = {}:  window not found ", shelf.Name));
    4884           0 :             ErrorsFound = true;
    4885             :         }
    4886             :     } // for (shelf)
    4887             : 
    4888         796 :     if (ErrorsFound) ShowFatalError(state, "CheckTDDsAndLightShelvesInDaylitZones: Errors in DAYLIGHTING input.");
    4889         796 : }
    4890             : 
    4891         796 : void AssociateWindowShadingControlWithDaylighting(EnergyPlusData &state)
    4892             : {
    4893         796 :     auto &dl = state.dataDayltg;
    4894         869 :     for (auto &winShadeControl : state.dataSurface->WindowShadingControl) {
    4895          73 :         if (winShadeControl.DaylightingControlName.empty()) continue;
    4896          25 :         int found = -1;
    4897          27 :         for (int daylightCtrlNum = 1; daylightCtrlNum <= (int)dl->daylightControl.size(); ++daylightCtrlNum) {
    4898          27 :             if (Util::SameString(winShadeControl.DaylightingControlName, dl->daylightControl(daylightCtrlNum).Name)) {
    4899          25 :                 found = daylightCtrlNum;
    4900          25 :                 break;
    4901             :             }
    4902             :         }
    4903          25 :         if (found > 0) {
    4904          25 :             winShadeControl.DaylightControlIndex = found;
    4905             :         } else {
    4906           0 :             ShowWarningError(state, "AssociateWindowShadingControlWithDaylighting: Daylighting object name used in WindowShadingControl not found.");
    4907           0 :             ShowContinueError(state,
    4908           0 :                               format("..The WindowShadingControl object=\"{}\" and referenes an object named: \"{}\"",
    4909           0 :                                      winShadeControl.Name,
    4910           0 :                                      winShadeControl.DaylightingControlName));
    4911             :         }
    4912         796 :     }
    4913         796 : } // AssociateWindowShadingControlWithDaylighting()
    4914             : 
    4915          64 : void GetLightWellData(EnergyPlusData &state, bool &ErrorsFound) // If errors found in input
    4916             : {
    4917             : 
    4918             :     // SUBROUTINE INFORMATION:
    4919             :     //       AUTHOR         Fred Winkelmann
    4920             :     //       DATE WRITTEN   Apr 2004
    4921             : 
    4922             :     // PURPOSE OF THIS SUBROUTINE:
    4923             :     // Gets data for a light well associated with a rectangular exterior window.
    4924             :     // Calculates light well efficiency, defined as the ratio of the amount of visible
    4925             :     // solar radiation leaving a well to the amount entering the well.
    4926             : 
    4927             :     // METHODOLOGY EMPLOYED:
    4928             :     // Based on fit to Fig. 8-21, "Efficiency factors for various depths of light wells
    4929             :     // based on well-interreflectance values," Lighting Handbook, 8th Edition, Illuminating
    4930             :     // Engineering Society of North America, 1993.
    4931             : 
    4932             :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    4933             : 
    4934             :     int IOStat;        // IO Status when calling get input subroutine
    4935             :     int NumAlpha;      // Number of alpha names being passed
    4936             :     int NumProp;       // Number of properties being passed
    4937             :     int TotLightWells; // Total Light Well objects
    4938             : 
    4939          64 :     auto &ip = state.dataInputProcessing->inputProcessor;
    4940          64 :     auto const &ipsc = state.dataIPShortCut;
    4941             : 
    4942             :     // Get the total number of Light Well objects
    4943          64 :     ipsc->cCurrentModuleObject = "DaylightingDevice:LightWell";
    4944          64 :     TotLightWells = ip->getNumObjectsFound(state, ipsc->cCurrentModuleObject);
    4945          64 :     if (TotLightWells == 0) return;
    4946             : 
    4947           3 :     for (int loop = 1; loop <= TotLightWells; ++loop) {
    4948             : 
    4949           4 :         ip->getObjectItem(state,
    4950           2 :                           ipsc->cCurrentModuleObject,
    4951             :                           loop,
    4952           2 :                           ipsc->cAlphaArgs,
    4953             :                           NumAlpha,
    4954           2 :                           ipsc->rNumericArgs,
    4955             :                           NumProp,
    4956             :                           IOStat,
    4957           2 :                           ipsc->lNumericFieldBlanks,
    4958           2 :                           ipsc->lAlphaFieldBlanks,
    4959           2 :                           ipsc->cAlphaFieldNames,
    4960           2 :                           ipsc->cNumericFieldNames);
    4961             : 
    4962           2 :         int SurfNum = Util::FindItemInList(ipsc->cAlphaArgs(1), state.dataSurface->Surface);
    4963           2 :         if (SurfNum == 0) {
    4964           0 :             ShowSevereError(state,
    4965           0 :                             format("{}: invalid {}=\"{}\" not found.", ipsc->cCurrentModuleObject, ipsc->cAlphaFieldNames(1), ipsc->cAlphaArgs(1)));
    4966           0 :             ErrorsFound = true;
    4967           0 :             continue;
    4968             :         }
    4969             : 
    4970           2 :         auto const &surf = state.dataSurface->Surface(SurfNum);
    4971           2 :         auto &surfWin = state.dataSurface->SurfaceWindow(SurfNum);
    4972             :         // Check that associated surface is an exterior window
    4973             :         // True if associated surface is not an exterior window
    4974           2 :         if (surf.Class != SurfaceClass::Window && surf.ExtBoundCond != ExternalEnvironment) {
    4975           0 :             ShowSevereError(
    4976             :                 state,
    4977           0 :                 format(
    4978           0 :                     "{}: invalid {}=\"{}\" - not an exterior window.", ipsc->cCurrentModuleObject, ipsc->cAlphaFieldNames(1), ipsc->cAlphaArgs(1)));
    4979           0 :             ErrorsFound = true;
    4980           0 :             continue;
    4981             :         }
    4982             : 
    4983             :         // Associated surface is an exterior window; calculate light well efficiency.
    4984           2 :         surfWin.lightWellEff = 1.0;
    4985           2 :         Real64 HeightWell = ipsc->rNumericArgs(1);  // Well height (from window to bottom of well) (m)
    4986           2 :         Real64 PerimWell = ipsc->rNumericArgs(2);   // Well perimeter (at bottom of well) (m)
    4987           2 :         Real64 AreaWell = ipsc->rNumericArgs(3);    // Well area (at bottom of well) (m2)
    4988           2 :         Real64 VisReflWell = ipsc->rNumericArgs(4); // Area-weighted visible reflectance of well walls
    4989             : 
    4990             :         // Warning if light well area is less than window area
    4991           2 :         if (AreaWell < (surf.Area + state.dataSurface->SurfWinDividerArea(SurfNum) - 0.1)) {
    4992           0 :             ShowSevereError(state,
    4993           0 :                             format("{}: invalid {}=\"{}\" - Areas.", ipsc->cCurrentModuleObject, ipsc->cAlphaFieldNames(1), ipsc->cAlphaArgs(1)));
    4994           0 :             ShowContinueError(state, format("has Area of Bottom of Well={:.1R} that is less than window area={:.1R}", surf.Area, AreaWell));
    4995             :         }
    4996             : 
    4997           2 :         if (HeightWell >= 0.0 && PerimWell > 0.0 && AreaWell > 0.0) {
    4998           2 :             Real64 WellCavRatio = 2.5 * HeightWell * PerimWell / AreaWell;
    4999           2 :             surfWin.lightWellEff = std::exp(-WellCavRatio * (0.16368 - 0.14467 * VisReflWell));
    5000             :         }
    5001             :     } // End of loop over light well objects
    5002             : } // GetLightWellData()
    5003             : 
    5004     4102827 : inline int findWinShadingStatus(EnergyPlusData &state, int const IWin)
    5005             : {
    5006             :     // Return the window shading status, 1=unshaded, 2=shaded
    5007     4102827 :     bool WinShadedNoGlareControl = IS_SHADED_NO_GLARE_CTRL(state.dataSurface->SurfWinShadingFlag(IWin));
    5008             : 
    5009     8200222 :     return ((state.dataSurface->SurfWinWindowModelType(IWin) != WindowModel::BSDF) &&
    5010     3828233 :             (WinShadedNoGlareControl || state.dataSurface->SurfWinSolarDiffusing(IWin)))
    5011     8200222 :                ? 2
    5012     4102827 :                : 1;
    5013             : }
    5014             : 
    5015     1018666 : Real64 DayltgGlare(EnergyPlusData &state,
    5016             :                    int IL,                   // Reference point index: 1=first ref pt, 2=second ref pt
    5017             :                    Real64 BLUM,              // Window background (surround) luminance (cd/m2)
    5018             :                    int const daylightCtrlNum // Current daylighting control number
    5019             : )
    5020             : {
    5021             : 
    5022             :     // SUBROUTINE INFORMATION:
    5023             :     //       AUTHOR         Fred Winkelmann
    5024             :     //       DATE WRITTEN   July 1997
    5025             : 
    5026             :     // PURPOSE OF THIS SUBROUTINE:
    5027             :     // CALCULATE GLARE INDEX.
    5028             : 
    5029             :     // METHODOLOGY EMPLOYED:
    5030             :     // Called from DayltgInteriorIllum.  Finds glare index at reference
    5031             :     // point no. IL in a space using the Cornell/BRS large source
    5032             :     // glare formula. BLUM is the background luminance (cd/m**2).
    5033             :     // TH comment 1/21/2010: The SurfaceWindow(IWin)%ShadingFlag has to be set
    5034             :     //  before calling this subroutine. For switchable glazings this is tricky
    5035             :     //  because the ZoneDaylight(ZoneNum)%SourceLumFromWinAtRefPt(IL,2,loop)
    5036             :     //  may change every time step to represent intermediate switched state.
    5037             : 
    5038             :     // REFERENCES:
    5039             :     // Based on DOE-2.1E subroutine DGLARE.
    5040             : 
    5041     1018666 :     Real64 GTOT = 0.0; // Glare constant
    5042             : 
    5043     1018666 :     auto &dl = state.dataDayltg;
    5044             : 
    5045             :     // Loop over exterior windows associated with zone
    5046     1018666 :     auto &thisDayltgCtrl = dl->daylightControl(daylightCtrlNum);
    5047     1018666 :     auto &thisEnclDaylight = dl->enclDaylight(thisDayltgCtrl.enclIndex);
    5048     3672935 :     for (int loop = 1; loop <= thisEnclDaylight.NumOfDayltgExtWins; ++loop) {
    5049     2654269 :         int IWin = thisEnclDaylight.DayltgExtWinSurfNums(loop);
    5050     2654269 :         int WinShadingIndex = findWinShadingStatus(state, IWin);
    5051             :         // Conversion from ft-L to cd/m2, with cd/m2 = 0.2936 ft-L, gives the 0.4794 factor
    5052             :         // below, which is (0.2936)**0.6
    5053     2654269 :         auto const &extWin = thisDayltgCtrl.refPts(IL).extWins(loop);
    5054     2654269 :         Real64 GTOT1 = 0.4794 * (std::pow(extWin.lums[iLum_Source][WinShadingIndex - 1], 1.6)) * std::pow(extWin.solidAngWtd, 0.8);
    5055     2654269 :         Real64 GTOT2 = BLUM + 0.07 * std::sqrt(extWin.solidAng) * extWin.lums[iLum_Source][WinShadingIndex - 1];
    5056     2654269 :         GTOT += GTOT1 / (GTOT2 + 0.000001);
    5057             :     }
    5058             : 
    5059             :     // Glare index (adding 0.000001 prevents LOG10 (0))
    5060     1018666 :     return max(0.0, 10.0 * std::log10(GTOT + 0.000001));
    5061             : }
    5062             : 
    5063        1015 : void DayltgGlareWithIntWins(EnergyPlusData &state,
    5064             :                             int const daylightCtrlNum // Current daylighting control number
    5065             : )
    5066             : {
    5067             : 
    5068             :     // SUBROUTINE INFORMATION:
    5069             :     //       AUTHOR         Fred Winkelmann
    5070             :     //       DATE WRITTEN   March 2004
    5071             : 
    5072             :     // PURPOSE OF THIS SUBROUTINE:
    5073             :     // Calculate daylighting glare index for zones with interior windows.
    5074             : 
    5075             :     // METHODOLOGY EMPLOYED:
    5076             :     // Finds glare index at reference point IL in a daylit zone using the Cornell/BRS large source
    5077             :     // glare formula. Takes into account inter-reflected illuminance from light entering
    5078             :     // the zone through interior windows
    5079             : 
    5080             :     // REFERENCES:
    5081             :     // Based on subroutine DayltgGlare.
    5082             : 
    5083        1015 :     Real64 GTOT = 0.0; // Glare constant(?) // TODO: does this need to be reset for every refPt?
    5084             : 
    5085             :     // Calculate background luminance including effect of inter-reflected illuminance from light
    5086             :     // entering zone through its interior windows
    5087        1015 :     auto &dl = state.dataDayltg;
    5088        1015 :     auto &thisDayltgCtrl = dl->daylightControl(daylightCtrlNum);
    5089        1015 :     auto &thisEnclDaylight = dl->enclDaylight(thisDayltgCtrl.enclIndex);
    5090        1015 :     int RefPoints = thisDayltgCtrl.TotalDaylRefPoints; // Number of daylighting reference points in zone
    5091        2030 :     for (int IL = 1; IL <= RefPoints; ++IL) {
    5092        1015 :         auto &refPt = thisDayltgCtrl.refPts(IL);
    5093             : 
    5094        1015 :         Real64 BackgroundLum = refPt.lums[iLum_Back] + thisEnclDaylight.InterReflIllFrIntWins * thisEnclDaylight.aveVisDiffReflect / Constant::Pi;
    5095        1015 :         BackgroundLum = max(refPt.illumSetPoint * thisEnclDaylight.aveVisDiffReflect / Constant::Pi, BackgroundLum);
    5096             : 
    5097             :         // Loop over exterior windows associated with zone
    5098        2030 :         for (int loop = 1; loop <= thisEnclDaylight.NumOfDayltgExtWins; ++loop) {
    5099        1015 :             int IWin = thisEnclDaylight.DayltgExtWinSurfNums(loop);
    5100        1015 :             int WinShadingIndex = findWinShadingStatus(state, IWin);
    5101             :             // Conversion from ft-L to cd/m2, with cd/m2 = 0.2936 ft-L, gives the 0.4794 factor
    5102             :             // below, which is (0.2936)**0.6
    5103        1015 :             auto const &extWin = thisDayltgCtrl.refPts(IL).extWins(loop);
    5104        1015 :             Real64 GTOT1 = 0.4794 * (std::pow(extWin.lums[iLum_Source][WinShadingIndex - 1], 1.6)) * std::pow(extWin.solidAngWtd, 0.8);
    5105        1015 :             Real64 GTOT2 = BackgroundLum + 0.07 * std::sqrt(extWin.solidAng) * extWin.lums[iLum_Source][WinShadingIndex - 1];
    5106        1015 :             GTOT += GTOT1 / (GTOT2 + 0.000001);
    5107             :         }
    5108             : 
    5109             :         // Glare index
    5110        1015 :         refPt.glareIndex = max(0.0, 10.0 * std::log10(GTOT + 0.000001));
    5111             :     } // for (IL)
    5112        1015 : } // DaylGlareWithIntWins()
    5113             : 
    5114        4697 : void DayltgExtHorizIllum(EnergyPlusData &state,
    5115             :                          Illums &HI // Horizontal illuminance from sky for different sky types
    5116             : )
    5117             : {
    5118             : 
    5119             :     // SUBROUTINE INFORMATION:
    5120             :     //       AUTHOR         Fred Winkelmann
    5121             :     //       DATE WRITTEN   July 1997
    5122             : 
    5123             :     // PURPOSE OF THIS SUBROUTINE:
    5124             :     // Calculates exterior daylight illuminance.
    5125             : 
    5126             :     // METHODOLOGY EMPLOYED:
    5127             :     // Called by CalcDayltgCoefficients. Calculates illuminance
    5128             :     // on unobstructed horizontal surface by integrating
    5129             :     // over the luminance distribution of standard CIE skies.
    5130             :     // Calculates horizontal beam illuminance.
    5131             :     // REFERENCES:
    5132             :     // Based on DOE-2.1E subroutine DHILL.
    5133             : 
    5134             :     // Argument array dimensioning
    5135             : 
    5136             :     // SUBROUTINE PARAMETER DEFINITIONS:
    5137        4697 :     Real64 constexpr DTH = (2.0 * Constant::Pi) / double(NTH); // Sky integration azimuth stepsize (radians)
    5138        4697 :     Real64 constexpr DPH = Constant::PiOvr2 / double(NPH);     // Sky integration altitude stepsize (radians)
    5139             : 
    5140             :     // Integrate to obtain illuminance from sky.
    5141             :     // The contribution in lumens/m2 from a patch of sky at altitude PH and azimuth TH
    5142             :     // is L(TH,PH)*SIN(PH)*COS(PH)*DTH*DPH, where L(TH,PH) is the luminance
    5143             :     // of the patch in cd/m2.
    5144        4697 :     auto &dl = state.dataDayltg;
    5145             : 
    5146             :     //  Init
    5147        4697 :     if (dl->DayltgExtHorizIllum_firstTime) {
    5148         576 :         for (int IPH = 1; IPH <= NPH; ++IPH) {
    5149         512 :             dl->PH[IPH] = (IPH - 0.5) * DPH;
    5150         512 :             dl->SPHCPH[IPH] = std::sin(dl->PH[IPH]) * std::cos(dl->PH[IPH]); // DA = COS(PH)*DTH*DPH
    5151             :         }
    5152        1216 :         for (int ITH = 1; ITH <= NTH; ++ITH) {
    5153        1152 :             dl->TH[ITH] = (ITH - 0.5) * DTH;
    5154             :         }
    5155          64 :         dl->DayltgExtHorizIllum_firstTime = false;
    5156             :     }
    5157             : 
    5158       18788 :     HI = Illums();
    5159             : 
    5160             :     // Sky integration
    5161       42273 :     for (int IPH = 1; IPH <= NPH; ++IPH) {
    5162       37576 :         Real64 const PH_IPH = dl->PH[IPH];
    5163       37576 :         Real64 const SPHCPH_IPH = dl->SPHCPH[IPH];
    5164      713944 :         for (int ITH = 1; ITH <= NTH; ++ITH) {
    5165      676368 :             Real64 const TH_ITH = dl->TH[ITH];
    5166     3381840 :             for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
    5167     2705472 :                 HI.sky[iSky] += DayltgSkyLuminance(state, static_cast<SkyType>(iSky), TH_ITH, PH_IPH) * SPHCPH_IPH;
    5168             :             }
    5169             :         }
    5170             :     }
    5171             : 
    5172       23485 :     for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
    5173       18788 :         HI.sky[iSky] *= DTH * DPH;
    5174             :     }
    5175             : 
    5176             :     // Direct solar horizontal illum (for unit direct normal illuminance)
    5177        4697 :     HI.sun = dl->sunAngles.sinPhi * 1.0;
    5178        4697 : } // DayltgExtHorizIllum()
    5179             : 
    5180             : // Product of solar transmittances of exterior obstructions
    5181     6890773 : Real64 DayltgHitObstruction(EnergyPlusData &state,
    5182             :                             int const IHOUR,           // Hour number
    5183             :                             int const IWin,            // Window index
    5184             :                             Vector3<Real64> const &R1, // Origin of ray (m)
    5185             :                             Vector3<Real64> const &RN  // Unit vector along ray
    5186             : )
    5187             : {
    5188             : 
    5189             :     // SUBROUTINE INFORMATION:
    5190             :     //       AUTHOR         Fred Winkelmann
    5191             :     //       DATE WRITTEN   July 1997
    5192             :     //       MODIFIED       FCW, May 2003: update list of surface classes that qualify as obstructions;
    5193             :     //                        add interior surfaces as possible obstructors;
    5194             :     //                        return from DO loop over surfaces as soon as any obstruction is hit;
    5195             :     //                      FCW, July 2003: change from returning whether an obstruction is hit or not
    5196             :     //                        to product of solar transmittances of hit obstructions.
    5197             :     //                      FCW, Nov 2003: remove interior surfaces as possible obstructors since there
    5198             :     //                        is now a separate check for interior obstructions; exclude windows and
    5199             :     //                        doors as obstructors since if they are obstructors their base surfaces will
    5200             :     //                        also be obstructors
    5201             :     //       RE-ENGINEERED  Sept 2015. Stuart Mentzer. Octree for performance.
    5202             : 
    5203             :     // PURPOSE OF THIS SUBROUTINE:
    5204             :     // Determines the product of the solar transmittances of the obstructions hit by a ray
    5205             :     // from R1 in the direction of vector RN.
    5206             : 
    5207             :     // REFERENCES:
    5208             :     // Based on DOE-2.1E subroutine DHITSH.
    5209             : 
    5210             :     // Local declarations
    5211             :     bool hit; // True iff a particular obstruction is hit
    5212             : 
    5213     6890773 :     Real64 ObTrans = 1.0;
    5214             : 
    5215     6890773 :     auto const &window = state.dataSurface->Surface(IWin);
    5216     6890773 :     int const window_iBaseSurf = window.BaseSurf;
    5217             : 
    5218     6890773 :     Vector3<Real64> DayltgHitObstructionHP;
    5219             :     // Loop over potentially obstructing surfaces, which can be building elements, like walls, or shadowing surfaces, like overhangs
    5220             :     // Building elements are assumed to be opaque
    5221             :     // A shadowing surface is opaque unless its transmittance schedule value is non-zero
    5222     6890773 :     if (state.dataSurface->TotSurfaces < octreeCrossover) { // Linear search through surfaces
    5223             : 
    5224    58062399 :         for (int ISurf : state.dataSurface->AllShadowPossObstrSurfaceList) {
    5225    53412214 :             auto const &surface = state.dataSurface->Surface(ISurf);
    5226    53412214 :             SurfaceClass IType = surface.Class;
    5227    53412214 :             if ((IType == SurfaceClass::Wall || IType == SurfaceClass::Roof || IType == SurfaceClass::Floor) && (ISurf != window_iBaseSurf)) {
    5228    48010663 :                 hit = PierceSurface(state, ISurf, R1, RN, DayltgHitObstructionHP);
    5229    48010663 :                 if (hit) { // Building element is hit (assumed opaque)
    5230       45108 :                     ObTrans = 0.0;
    5231       45108 :                     break;
    5232             :                 }
    5233     5401551 :             } else if (surface.IsShadowing) {
    5234      706392 :                 hit = PierceSurface(state, ISurf, R1, RN, DayltgHitObstructionHP);
    5235      706392 :                 if (hit) { // Shading surface is hit
    5236             :                     // Get solar transmittance of the shading surface
    5237             :                     Real64 const Trans(
    5238       75782 :                         surface.SchedShadowSurfIndex > 0 ? ScheduleManager::LookUpScheduleValue(state, surface.SchedShadowSurfIndex, IHOUR, 1) : 0.0);
    5239       75782 :                     if (Trans < 1.e-6) {
    5240       75782 :                         ObTrans = 0.0;
    5241       75782 :                         break;
    5242             :                     } else {
    5243           0 :                         ObTrans *= Trans;
    5244             :                     }
    5245             :                 }
    5246             :             }
    5247     4771075 :         }
    5248             : 
    5249             :     } else { // Surface octree search
    5250             : 
    5251     2119698 :         auto const &window_base(window_iBaseSurf > 0 ? state.dataSurface->Surface(window_iBaseSurf) : window);
    5252     2119698 :         auto const *window_base_p(&window_base);
    5253             : 
    5254             :         // Lambda function for the octree to test for surface hit and update transmittance if hit
    5255    64744377 :         auto solarTransmittance = [=, &state, &R1, &RN, &hit, &ObTrans](SurfaceData const &surface) -> bool {
    5256   299776131 :             if (!surface.IsShadowPossibleObstruction) return false; // Do Consider separate octree without filtered surfaces
    5257    66829301 :             DataSurfaces::SurfaceClass const sClass(surface.Class);
    5258    66829301 :             Vector3<Real64> HP;
    5259    66829301 :             if ((sClass == SurfaceClass::Wall || sClass == SurfaceClass::Roof || sClass == SurfaceClass::Floor) && (&surface != window_base_p)) {
    5260    64697724 :                 hit = PierceSurface(surface, R1, RN, HP);
    5261    64697724 :                 if (hit) { // Building element is hit (assumed opaque)
    5262       72303 :                     ObTrans = 0.0;
    5263       72303 :                     return true;
    5264             :                 }
    5265     2131577 :             } else if (surface.IsShadowing) {
    5266       46653 :                 hit = PierceSurface(surface, R1, RN, HP);
    5267       46653 :                 if (hit) { // Shading surface is hit
    5268             :                     // Get solar transmittance of the shading surface
    5269             :                     Real64 const Trans(
    5270           0 :                         surface.SchedShadowSurfIndex > 0 ? ScheduleManager::LookUpScheduleValue(state, surface.SchedShadowSurfIndex, IHOUR, 1) : 0.0);
    5271           0 :                     if (Trans < 1.e-6) {
    5272           0 :                         ObTrans = 0.0;
    5273           0 :                         return true;
    5274             :                     } else {
    5275           0 :                         ObTrans *= Trans;
    5276           0 :                         return ObTrans == 0.0;
    5277             :                     }
    5278             :                 }
    5279             :             }
    5280    66756998 :             return false;
    5281    66829301 :         };
    5282             : 
    5283             :         // Check octree surface candidates for hits: short circuits if zero transmittance reached
    5284     2119698 :         Vector3<Real64> const RN_inv(SurfaceOctreeCube::safe_inverse(RN));
    5285     2119698 :         state.dataHeatBalMgr->surfaceOctree.processSomeSurfaceRayIntersectsCube(state, R1, RN, RN_inv, solarTransmittance);
    5286     2119698 :     }
    5287             : 
    5288     6890773 :     return ObTrans;
    5289     6890773 : } // DayltgHitObstruction()
    5290             : 
    5291     2818653 : bool DayltgHitInteriorObstruction(EnergyPlusData &state,
    5292             :                                   int const IWin,            // Window index
    5293             :                                   Vector3<Real64> const &R1, // Origin of ray (m)
    5294             :                                   Vector3<Real64> const &R2  // Destination of ray (m)
    5295             : )
    5296             : {
    5297             : 
    5298             :     // SUBROUTINE INFORMATION:
    5299             :     //       AUTHOR         Fred Winkelmann
    5300             :     //       DATE WRITTEN   July 1997
    5301             :     //       RE-ENGINEERED  Sept 2015. Stuart Mentzer. Octree for performance.
    5302             : 
    5303             :     // PURPOSE OF THIS SUBROUTINE:
    5304             :     // This subroutine checks for interior obstructions between reference point and window element.
    5305             : 
    5306             :     // Preconditions
    5307     2818653 :     assert(magnitude(R2 - R1) > 0.0); // Protect normalize() from divide by zero
    5308             : 
    5309     2818653 :     bool hit = false;
    5310     2818653 :     Vector3<Real64> RN = (R2 - R1).normalize(); // Make unit vector
    5311     2818653 :     Real64 const d12 = distance(R1, R2);        // Distance between R1 and R2
    5312             : 
    5313     2818653 :     auto const &window = state.dataSurface->Surface(IWin);
    5314     2818653 :     int const window_Enclosure = window.SolarEnclIndex;
    5315     2818653 :     int const window_iBaseSurf = window.BaseSurf;
    5316     2818653 :     auto const &window_base = window_iBaseSurf > 0 ? state.dataSurface->Surface(window_iBaseSurf) : window;
    5317     2818653 :     int const window_base_iExtBoundCond = window_base.ExtBoundCond;
    5318             : 
    5319             :     // Loop over potentially obstructing surfaces, which can be building elements, like walls, or shadowing surfaces, like overhangs
    5320     2818653 :     if (state.dataSurface->TotSurfaces < octreeCrossover) { // Linear search through surfaces
    5321             :         // Hit coordinates, if ray hits an obstruction
    5322     2308235 :         Vector3<Real64> DayltgHitInteriorObstructionHP;
    5323             : 
    5324    76265487 :         for (int ISurf = 1; ISurf <= state.dataSurface->TotSurfaces; ++ISurf) {
    5325    73988574 :             auto const &surface = state.dataSurface->Surface(ISurf);
    5326    73988574 :             SurfaceClass IType = surface.Class;
    5327    73988574 :             if ((surface.IsShadowing) ||                         // Shadowing surface
    5328    73360072 :                 ((surface.SolarEnclIndex == window_Enclosure) && // Wall/ceiling/floor is in same zone as window
    5329    34720785 :                  (IType == SurfaceClass::Wall || IType == SurfaceClass::Roof || IType == SurfaceClass::Floor) && (ISurf != window_iBaseSurf) &&
    5330             :                  (ISurf != window_base_iExtBoundCond))) // Exclude window's base or base-adjacent surfaces
    5331             :             {
    5332    12351769 :                 hit = PierceSurface(state, ISurf, R1, RN, d12, DayltgHitInteriorObstructionHP); // Check if R2-R1 segment pierces surface
    5333    12351769 :                 if (hit) break;                                                                 // Segment pierces surface: Don't check the rest
    5334             :             }
    5335             :         }
    5336             : 
    5337     2308235 :     } else { // Surface octree search
    5338             : 
    5339      510418 :         auto const *window_base_p = &window_base;
    5340      510418 :         auto const &window_base_adjacent = window_base_iExtBoundCond > 0 ? state.dataSurface->Surface(window_base_iExtBoundCond) : window_base;
    5341      510418 :         auto const *window_base_adjacent_p = &window_base_adjacent;
    5342             : 
    5343             :         // Lambda function for the octree to test for surface hit
    5344    81568322 :         auto surfaceHit = [=, &R1, &hit](SurfaceData const &surface) -> bool {
    5345    76735020 :             DataSurfaces::SurfaceClass const sClass = surface.Class;
    5346    76735020 :             Vector3<Real64> HP;                                  // Hit point
    5347    76735020 :             if ((surface.IsShadowing) ||                         // Shadowing surface
    5348    76728114 :                 ((surface.SolarEnclIndex == window_Enclosure) && // Surface is in same zone as window
    5349     2896963 :                  (sClass == SurfaceClass::Wall || sClass == SurfaceClass::Roof || sClass == SurfaceClass::Floor) && // Wall, ceiling/roof, or floor
    5350     2920163 :                  (&surface != window_base_p) && (&surface != window_base_adjacent_p))) // Exclude window's base or base-adjacent surfaces
    5351             :             {
    5352     2416651 :                 hit = PierceSurface(surface, R1, RN, d12, HP); // Check if R2-R1 segment pierces surface
    5353     2416651 :                 return hit;
    5354             :             } else {
    5355    74318369 :                 return false;
    5356             :             }
    5357    77245438 :         };
    5358             : 
    5359             :         // Check octree surface candidates until a hit is found, if any
    5360      510418 :         state.dataHeatBalMgr->surfaceOctree.hasSurfaceSegmentIntersectsCube(R1, R2, surfaceHit);
    5361      510418 :     }
    5362             : 
    5363     2818653 :     return hit;
    5364     2818653 : } // DayltgHitInteriorObstruction()
    5365             : 
    5366       20412 : bool DayltgHitBetWinObstruction(EnergyPlusData &state,
    5367             :                                 int const IWin1,           // Surface number of origin window
    5368             :                                 int const IWin2,           // Surface number of destination window
    5369             :                                 Vector3<Real64> const &R1, // Origin of ray (on IWin1) (m)
    5370             :                                 Vector3<Real64> const &R2  // Destination of ray (on IWin2) (m)
    5371             : )
    5372             : {
    5373             : 
    5374             :     // SUBROUTINE INFORMATION:
    5375             :     //       AUTHOR         Fred Winkelmann
    5376             :     //       DATE WRITTEN   Feb 2004
    5377             :     //       RE-ENGINEERED  Sept 2015. Stuart Mentzer. Octree for performance.
    5378             : 
    5379             :     // PURPOSE OF THIS SUBROUTINE:
    5380             :     // Determines if a ray from point R1 on window IWin1 to point R2
    5381             :     // on window IWin2 hits an obstruction
    5382             : 
    5383             :     // Preconditions
    5384       20412 :     assert(magnitude(R2 - R1) > 0.0); // Protect normalize() from divide by zero
    5385             : 
    5386             :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    5387             :     SurfaceClass IType; // Surface type/class
    5388             : 
    5389       20412 :     bool hit = false;
    5390       20412 :     Vector3<Real64> RN = (R2 - R1).normalize(); // Unit vector
    5391             : 
    5392       20412 :     Real64 const d12 = distance(R1, R2); // Distance between R1 and R2 (m)
    5393             : 
    5394       20412 :     auto const &window1 = state.dataSurface->Surface(IWin1);
    5395       20412 :     int const window1_iBaseSurf = window1.BaseSurf;
    5396       20412 :     auto const &window1_base = window1_iBaseSurf > 0 ? state.dataSurface->Surface(window1_iBaseSurf) : window1;
    5397       20412 :     int const window1_base_iExtBoundCond = window1_base.ExtBoundCond;
    5398             : 
    5399       20412 :     auto const &window2 = state.dataSurface->Surface(IWin2);
    5400       20412 :     int const window2_Enclosure = window2.SolarEnclIndex;
    5401       20412 :     int const window2_iBaseSurf = window2.BaseSurf;
    5402       20412 :     auto const &window2_base = window2_iBaseSurf > 0 ? state.dataSurface->Surface(window2_iBaseSurf) : window2;
    5403       20412 :     int const window2_base_iExtBoundCond = window2_base.ExtBoundCond;
    5404             : 
    5405             :     // Preconditions
    5406             :     //        assert( window1.Zone == window2_Zone ); //? This is violated in PurchAirWithDoubleFacadeDaylighting so then why the asymmetry
    5407             :     // of  only checking for wall/roof/floor for window2 zone below?
    5408             : 
    5409             :     // Loop over potentially obstructing surfaces, which can be building elements, like walls, or shadowing surfaces, like overhangs
    5410       20412 :     if (state.dataSurface->TotSurfaces < octreeCrossover) { // Linear search through surfaces
    5411             : 
    5412      612360 :         for (int ISurf = 1; ISurf <= state.dataSurface->TotSurfaces; ++ISurf) {
    5413      591948 :             auto const &surface = state.dataSurface->Surface(ISurf);
    5414      591948 :             IType = surface.Class;
    5415      591948 :             if ((surface.IsShadowing) ||                          // Shadowing surface
    5416      591948 :                 ((surface.SolarEnclIndex == window2_Enclosure) && // Wall/ceiling/floor is in same zone as windows
    5417      163296 :                  (IType == SurfaceClass::Wall || IType == SurfaceClass::Roof || IType == SurfaceClass::Floor) && // Wall, ceiling/roof, or floor
    5418      122472 :                  (ISurf != window1_iBaseSurf) && (ISurf != window2_iBaseSurf) &&                                 // Exclude windows' base surfaces
    5419       81648 :                  (ISurf != window1_base_iExtBoundCond) && (ISurf != window2_base_iExtBoundCond))) // Exclude windows' base-adjacent surfaces
    5420             :             {
    5421       81648 :                 Vector3<Real64> HP;
    5422       81648 :                 hit = PierceSurface(state, ISurf, R1, RN, d12, HP); // Check if R2-R1 segment pierces surface
    5423       81648 :                 if (hit) break;                                     // Segment pierces surface: Don't check the rest
    5424       81648 :             }
    5425             :         }
    5426             : 
    5427             :     } else { // Surface octree search
    5428             : 
    5429           0 :         auto const *window1_base_p = &window1_base;
    5430           0 :         auto const &window1_base_adjacent = window1_base_iExtBoundCond > 0 ? state.dataSurface->Surface(window1_base_iExtBoundCond) : window1_base;
    5431           0 :         auto const *window1_base_adjacent_p = &window1_base_adjacent;
    5432             : 
    5433           0 :         auto const *window2_base_p = &window2_base;
    5434           0 :         auto const &window2_base_adjacent = (window2_base_iExtBoundCond > 0) ? state.dataSurface->Surface(window2_base_iExtBoundCond) : window2_base;
    5435           0 :         auto const *window2_base_adjacent_p = &window2_base_adjacent;
    5436             : 
    5437             :         // Lambda function for the octree to test for surface hit
    5438           0 :         auto surfaceHit = [=, &R1, &RN, &hit](SurfaceData const &surface) -> bool {
    5439           0 :             DataSurfaces::SurfaceClass const sClass = surface.Class;
    5440           0 :             Vector3<Real64> HP;
    5441           0 :             if ((surface.IsShadowing) ||                          // Shadowing surface
    5442           0 :                 ((surface.SolarEnclIndex == window2_Enclosure) && // Surface is in same zone as window
    5443           0 :                  (sClass == SurfaceClass::Wall || sClass == SurfaceClass::Roof || sClass == SurfaceClass::Floor) && // Wall, ceiling/roof, or floor
    5444           0 :                  (&surface != window1_base_p) && (&surface != window2_base_p) &&                                    // Exclude windows' base surfaces
    5445           0 :                  (&surface != window1_base_adjacent_p) && (&surface != window2_base_adjacent_p))) // Exclude windows' base-adjacent surfaces
    5446             :             {
    5447           0 :                 hit = PierceSurface(surface, R1, RN, d12, HP); // Check if R2-R1 segment pierces surface
    5448           0 :                 return hit;
    5449             :             } else {
    5450           0 :                 return false;
    5451             :             }
    5452           0 :         };
    5453             : 
    5454             :         // Check octree surface candidates until a hit is found, if any
    5455           0 :         state.dataHeatBalMgr->surfaceOctree.hasSurfaceSegmentIntersectsCube(R1, R2, surfaceHit);
    5456             :     }
    5457             : 
    5458       20412 :     return hit;
    5459       20412 : } // DayltingHitBetWinObstruction()
    5460             : 
    5461     2804678 : void initDaylighting(EnergyPlusData &state, bool const initSurfaceHeatBalancefirstTime)
    5462             : {
    5463             :     // For daylit zones, calculate interior daylight illuminance at reference points and
    5464             :     // simulate lighting control system to get overhead electric lighting reduction
    5465             :     // factor due to daylighting.
    5466     2804678 :     auto &dl = state.dataDayltg;
    5467             : 
    5468    25996598 :     for (int SurfNum : state.dataSurface->AllExtSolWindowSurfaceList) {
    5469    27709728 :         for (auto &refPt : state.dataSurface->SurfaceWindow(SurfNum).refPts) {
    5470     4517808 :             refPt.illumFromWinRep = refPt.lumWinRep = 0.0;
    5471             :         }
    5472     2804678 :     }
    5473             : 
    5474             :     // Reset space power reduction factors
    5475    22720622 :     for (int spaceNum = 1; spaceNum <= state.dataGlobal->numSpaces; ++spaceNum) {
    5476    19915944 :         dl->spacePowerReductionFactor(spaceNum) = 1.0;
    5477             :     }
    5478     4262759 :     for (int daylightCtrlNum = 1; daylightCtrlNum <= (int)dl->daylightControl.size(); ++daylightCtrlNum) {
    5479     1458081 :         auto &thisDayltgCtrl = dl->daylightControl(daylightCtrlNum);
    5480     1458081 :         thisDayltgCtrl.PowerReductionFactor = 1.0;
    5481     1458081 :         if (state.dataEnvrn->PreviousSolRadPositive) {
    5482             :             // Reset to zero only if there was solar in the previous timestep, otherwise these are already zero
    5483      430121 :             dl->enclDaylight(thisDayltgCtrl.enclIndex).InterReflIllFrIntWins = 0.0; // inter-reflected illuminance from interior windows
    5484     1006994 :             for (int refPtNum = 1; refPtNum <= thisDayltgCtrl.TotalDaylRefPoints; ++refPtNum) {
    5485      576873 :                 auto &refPt = thisDayltgCtrl.refPts(refPtNum);
    5486      576873 :                 refPt.lums[iLum_Illum] = 0.0;
    5487      576873 :                 refPt.glareIndex = 0.0;
    5488      576873 :                 refPt.timeExceedingGlareIndexSetPoint = 0.0;
    5489      576873 :                 refPt.timeExceedingDaylightIlluminanceSetPoint = 0.0;
    5490             :             }
    5491             :         }
    5492             : 
    5493     1458081 :         if (state.dataEnvrn->SunIsUp && thisDayltgCtrl.TotalDaylRefPoints != 0) {
    5494      722471 :             if (initSurfaceHeatBalancefirstTime) DisplayString(state, "Computing Interior Daylighting Illumination");
    5495      722471 :             DayltgInteriorIllum(state, daylightCtrlNum);
    5496             :         }
    5497             :     }
    5498             : 
    5499             :     // The following report variables are valid only for daylit zones/enclosures without interior windows
    5500     2804678 :     if (state.dataEnvrn->SunIsUp) {
    5501    11354044 :         for (int enclNum = 1; enclNum <= state.dataViewFactor->NumOfSolarEnclosures; ++enclNum) {
    5502    10672942 :             if ((state.dataViewFactor->EnclSolInfo(enclNum).TotalEnclosureDaylRefPoints == 0) ||
    5503      720434 :                 (state.dataViewFactor->EnclSolInfo(enclNum).HasInterZoneWindow))
    5504     9233089 :                 continue;
    5505             : 
    5506      719419 :             auto &thisEnclDaylight = dl->enclDaylight(enclNum);
    5507     2147788 :             for (int extWinNum = 1; extWinNum <= thisEnclDaylight.NumOfDayltgExtWins; ++extWinNum) {
    5508     1428369 :                 int IWin = thisEnclDaylight.DayltgExtWinSurfNums(extWinNum);
    5509     1428369 :                 WinCover winCover = WinCover::Bare;
    5510     4198635 :                 if (state.dataSurface->SurfWinWindowModelType(IWin) != WindowModel::BSDF &&
    5511     2770266 :                     (IS_SHADED(state.dataSurface->SurfWinShadingFlag(IWin)) || state.dataSurface->SurfWinSolarDiffusing(IWin))) {
    5512       81040 :                     winCover = WinCover::Shaded;
    5513             :                 }
    5514     1428369 :                 int refPtCount = 0;
    5515     2862849 :                 for (int controlNum : dl->enclDaylight(enclNum).daylightControlIndexes) {
    5516     1434480 :                     auto &daylCtrl = dl->daylightControl(controlNum);
    5517     1434480 :                     if (daylCtrl.DaylightMethod != DaylightingMethod::SplitFlux) continue;
    5518             : 
    5519     3659411 :                     for (int refPtNum = 1; refPtNum <= daylCtrl.TotalDaylRefPoints; ++refPtNum) {
    5520     2224931 :                         ++refPtCount; // Count reference points across each daylighting control in the same enclosure
    5521     2224931 :                         auto &refPt = state.dataSurface->SurfaceWindow(IWin).refPts(refPtCount);
    5522     2224931 :                         auto const &daylCtrlRefPt = daylCtrl.refPts(refPtNum);
    5523     2224931 :                         refPt.illumFromWinRep = daylCtrlRefPt.extWins(extWinNum).lums[iLum_Illum][(int)winCover];
    5524     2224931 :                         refPt.lumWinRep = daylCtrlRefPt.extWins(extWinNum).lums[iLum_Source][(int)winCover];
    5525             :                     }
    5526     1428369 :                 } // for (controlNum)
    5527             :             }     // for (extWinNum)
    5528             :         }         // for (enclNum)
    5529             :     }             // if (SunIsUp)
    5530             : 
    5531     2804678 :     if (state.dataEnvrn->SunIsUp && (int)state.dataDaylightingDevicesData->TDDPipe.size() > 0) {
    5532        1015 :         if (initSurfaceHeatBalancefirstTime) DisplayString(state, "Computing Interior Daylighting Illumination for TDD pipes");
    5533        1015 :         DayltgInteriorTDDIllum(state);
    5534             :     }
    5535             : 
    5536     4262759 :     for (int daylightCtrlNum = 1; daylightCtrlNum <= (int)dl->daylightControl.size(); ++daylightCtrlNum) {
    5537     1458081 :         auto &thisDayltgCtrl = dl->daylightControl(daylightCtrlNum);
    5538             : 
    5539             :         // RJH DElight Modification Begin - Call to DElight electric lighting control subroutine
    5540             :         // Check if the sun is up and the current Thermal Zone hosts a Daylighting:DElight object
    5541     1458081 :         if (state.dataEnvrn->SunIsUp && thisDayltgCtrl.TotalDaylRefPoints != 0 && (thisDayltgCtrl.DaylightMethod == DaylightingMethod::DElight)) {
    5542        2037 :             int zoneNum = thisDayltgCtrl.zoneIndex;
    5543             :             // Call DElight interior illuminance and electric lighting control subroutine
    5544        2037 :             Real64 dPowerReducFac = 1.0; // Return value Electric Lighting Power Reduction Factor for current Zone and Timestep
    5545        2037 :             Real64 dHISKFFC = state.dataEnvrn->HISKF * DataDElight::LUX2FC;
    5546        2037 :             Real64 dHISUNFFC = state.dataEnvrn->HISUNF * DataDElight::LUX2FC;
    5547        2037 :             Real64 dSOLCOS1 = state.dataEnvrn->SOLCOS.x;
    5548        2037 :             Real64 dSOLCOS2 = state.dataEnvrn->SOLCOS.y;
    5549        2037 :             Real64 dSOLCOS3 = state.dataEnvrn->SOLCOS.z;
    5550        2037 :             Real64 dLatitude = state.dataEnvrn->Latitude;
    5551        2037 :             Real64 dCloudFraction = state.dataEnvrn->CloudFraction;
    5552             :             // Init Error Flag to 0 (no Warnings or Errors) (returned from DElight)
    5553        2037 :             int iErrorFlag = 0;
    5554             : 
    5555        2037 :             DElightManagerF::DElightElecLtgCtrl(len(state.dataHeatBal->Zone(zoneNum).Name),
    5556        2037 :                                                 state.dataHeatBal->Zone(zoneNum).Name,
    5557             :                                                 dLatitude,
    5558             :                                                 dHISKFFC,
    5559             :                                                 dHISUNFFC,
    5560             :                                                 dCloudFraction,
    5561             :                                                 dSOLCOS1,
    5562             :                                                 dSOLCOS2,
    5563             :                                                 dSOLCOS3,
    5564             :                                                 dPowerReducFac,
    5565             :                                                 iErrorFlag);
    5566             :             // Check Error Flag for Warnings or Errors returning from DElight
    5567             :             // RJH 2008-03-07: If no warnings/errors then read refpt illuminances for standard output reporting
    5568        2037 :             if (iErrorFlag != 0) {
    5569           0 :                 std::string cErrorMsg; // Each DElight Error Message can be up to 200 characters long
    5570             :                 // Open DElight Electric Lighting Error File for reading
    5571           0 :                 auto iDElightErrorFile = state.files.outputDelightDfdmpFilePath.try_open(state.files.outputControl.delightdfdmp); // (THIS_AUTO_OK)
    5572           0 :                 bool elOpened = iDElightErrorFile.good();
    5573             : 
    5574             :                 // Sequentially read lines in DElight Electric Lighting Error File
    5575             :                 // and process them using standard EPlus warning/error handling calls
    5576           0 :                 bool bEndofErrFile = false;
    5577           0 :                 while (!bEndofErrFile && elOpened) {
    5578           0 :                     auto cErrorLine = iDElightErrorFile.readLine(); // (THIS_AUTO_OK)
    5579           0 :                     if (cErrorLine.eof) {
    5580           0 :                         bEndofErrFile = true;
    5581           0 :                         continue;
    5582             :                     }
    5583             : 
    5584             :                     // Is the current line a Warning message?
    5585           0 :                     if (has_prefix(cErrorLine.data, "WARNING: ")) {
    5586           0 :                         cErrorMsg = cErrorLine.data.substr(9);
    5587           0 :                         ShowWarningError(state, cErrorMsg);
    5588             :                     }
    5589             :                     // Is the current line an Error message?
    5590           0 :                     if (has_prefix(cErrorLine.data, "ERROR: ")) {
    5591           0 :                         cErrorMsg = cErrorLine.data.substr(7);
    5592           0 :                         ShowSevereError(state, cErrorMsg);
    5593           0 :                         iErrorFlag = 1;
    5594             :                     }
    5595           0 :                 }
    5596             : 
    5597             :                 // Close DElight Error File and delete
    5598             : 
    5599           0 :                 if (elOpened) {
    5600           0 :                     iDElightErrorFile.close();
    5601           0 :                     FileSystem::removeFile(iDElightErrorFile.filePath);
    5602             :                 }
    5603             :                 // If any DElight Error occurred then ShowFatalError to terminate
    5604           0 :                 if (iErrorFlag > 0) {
    5605           0 :                     ShowFatalError(state, "End of DElight Error Messages");
    5606             :                 }
    5607           0 :             } else { // RJH 2008-03-07: No errors
    5608             :                      // extract reference point illuminance values from DElight Electric Lighting dump file for reporting
    5609             :                      // Open DElight Electric Lighting Dump File for reading
    5610        2037 :                 auto iDElightErrorFile = state.files.outputDelightEldmpFilePath.try_open(state.files.outputControl.delighteldmp); // (THIS_AUTO_OK)
    5611        2037 :                 bool elOpened = iDElightErrorFile.is_open();
    5612             : 
    5613             :                 // Sequentially read lines in DElight Electric Lighting Dump File
    5614             :                 // and extract refpt illuminances for standard EPlus output handling
    5615        2037 :                 bool bEndofErrFile = false;
    5616        2037 :                 int iDElightRefPt = 0; // Reference Point number for reading DElight Dump File (eplusout.delighteldmp)
    5617        8148 :                 while (!bEndofErrFile && elOpened) {
    5618        6111 :                     auto line = iDElightErrorFile.read<Real64>(); // (THIS_AUTO_OK)
    5619        6111 :                     Real64 dRefPtIllum = line.data;               // tmp var for reading RefPt illuminance
    5620        6111 :                     if (line.eof) {
    5621        2037 :                         bEndofErrFile = true;
    5622        2037 :                         continue;
    5623             :                     }
    5624             :                     // Increment refpt counter
    5625        4074 :                     ++iDElightRefPt;
    5626             :                     // Assure refpt index does not exceed number of refpts in this zone
    5627        4074 :                     if (iDElightRefPt <= thisDayltgCtrl.TotalDaylRefPoints) {
    5628        4074 :                         thisDayltgCtrl.refPts(iDElightRefPt).lums[iLum_Illum] = dRefPtIllum;
    5629             :                     }
    5630             :                 }
    5631             : 
    5632             :                 // Close DElight Electric Lighting Dump File and delete
    5633        2037 :                 if (elOpened) {
    5634        2037 :                     iDElightErrorFile.close();
    5635        2037 :                     FileSystem::removeFile(iDElightErrorFile.filePath);
    5636             :                 };
    5637        2037 :             }
    5638             :             // Store the calculated total zone Power Reduction Factor due to DElight daylighting
    5639             :             // in the ZoneDaylight structure for later use
    5640        2037 :             thisDayltgCtrl.PowerReductionFactor = dPowerReducFac;
    5641             :         }
    5642             :         // RJH DElight Modification End - Call to DElight electric lighting control subroutine
    5643             :     }
    5644             : 
    5645     2804678 :     if (state.dataEnvrn->SunIsUp && !state.dataGlobal->DoingSizing) {
    5646      990056 :         DayltgInteriorMapIllum(state);
    5647             :     }
    5648    22672022 :     for (int zoneNum = 1; zoneNum <= state.dataGlobal->NumOfZones; ++zoneNum) {
    5649    39783288 :         for (int const spaceNum : state.dataHeatBal->Zone(zoneNum).spaceIndexes) {
    5650    19915944 :             auto const &thisSpace = state.dataHeatBal->space(spaceNum);
    5651    43139106 :             for (int SurfNum = thisSpace.WindowSurfaceFirst; SurfNum <= thisSpace.WindowSurfaceLast; ++SurfNum) {
    5652    23223162 :                 state.dataSurface->SurfWinFracTimeShadingDeviceOn(SurfNum) = 0.0;
    5653    23223162 :                 if (IS_SHADED(state.dataSurface->SurfWinShadingFlag(SurfNum))) {
    5654      286781 :                     state.dataSurface->SurfWinFracTimeShadingDeviceOn(SurfNum) = 1.0;
    5655             :                 } else {
    5656    22936381 :                     state.dataSurface->SurfWinFracTimeShadingDeviceOn(SurfNum) = 0.0;
    5657             :                 }
    5658             :             }
    5659    19867344 :         }
    5660             :     }
    5661     2804678 : }
    5662             : 
    5663     2804678 : void manageDaylighting(EnergyPlusData &state)
    5664             : {
    5665     2804678 :     auto &dl = state.dataDayltg;
    5666             : 
    5667     2804678 :     if (state.dataEnvrn->SunIsUp && (state.dataEnvrn->BeamSolarRad + state.dataEnvrn->GndSolarRad + state.dataEnvrn->DifSolarRad > 0.0)) {
    5668     7240173 :         for (int enclNum = 1; enclNum <= state.dataViewFactor->NumOfSolarEnclosures; ++enclNum) {
    5669     6323710 :             auto &enclSol = state.dataViewFactor->EnclSolInfo(enclNum);
    5670     6323710 :             if (enclSol.TotalEnclosureDaylRefPoints == 0 || !enclSol.HasInterZoneWindow) continue;
    5671             : 
    5672        1015 :             DayltgInterReflIllFrIntWins(state, enclNum);
    5673        2030 :             for (int daylightCtrlNum : dl->enclDaylight(enclNum).daylightControlIndexes) {
    5674        1015 :                 DayltgGlareWithIntWins(state, daylightCtrlNum);
    5675        1015 :             }
    5676             :         }
    5677      916463 :         DayltgElecLightingControl(state);
    5678     1888215 :     } else if (dl->mapResultsToReport && state.dataGlobal->TimeStep == state.dataGlobal->NumOfTimeStepInHour) {
    5679          41 :         for (int MapNum = 1; MapNum <= (int)dl->illumMaps.size(); ++MapNum) {
    5680          22 :             ReportIllumMap(state, MapNum);
    5681             :         }
    5682          19 :         dl->mapResultsToReport = false;
    5683             :     }
    5684     2804678 : } // manageDaylighting()
    5685             : 
    5686      722471 : void DayltgInteriorIllum(EnergyPlusData &state,
    5687             :                          int const daylightCtrlNum) // Daylighting:Controls number
    5688             : {
    5689             : 
    5690             :     // SUBROUTINE INFORMATION:
    5691             :     //       AUTHOR         Fred Winkelmann
    5692             :     //       DATE WRITTEN   July 1997
    5693             :     //       MODIFIED       March 2000, FCW: interpolate clear-sky daylight factors using
    5694             :     //                      HourOfDay/WeightNow and NextHour/WeightNextHour. Previously
    5695             :     //                      only HourOfDay was used
    5696             :     //                      Jan 2001, FCW: interpolate in slat angle for windows with blinds
    5697             :     //                      that have movable slats
    5698             :     //                      Oct 2002, LKL: changed interpolation steps to HourOfDay/WeightNow
    5699             :     //                      LastHour/WeightPreviousHour
    5700             :     //                      Aug 2003, FCW: fix bug that prevented shadingControlType =
    5701             :     //                      MEETDAYLIGHTILLUMINANCESETPOINT from working
    5702             :     //                      Mar 2004, FCW: fix bug in calc of illuminance setpoint contribution
    5703             :     //                      to background luminance: now it is divided by pi to give cd/m2
    5704             :     //                      Mar 2004, FCW: modify to handle daylighting through interior windows
    5705             :     //                      June 2009, TH: modified for thermochromic windows
    5706             :     //                      Jan 2010, TH (CR 7984): added iterations for switchable windows with shading
    5707             :     //                       control of MeetDaylightIlluminanceSetpoint and glare control is active
    5708             :     //                       Also corrected bugs (CR 7988) for switchable glazings not related to CR 7984
    5709             : 
    5710             :     // PURPOSE OF THIS SUBROUTINE:
    5711             :     // Using daylighting factors and exterior illuminance, determine
    5712             :     // the current-hour interior daylight illuminance and glare index
    5713             :     // at each reference point in a space. Deploy window shading window by window
    5714             :     // if glare control is active for window and if the acceptable glare index
    5715             :     // is exceeded at both reference points.
    5716             : 
    5717             :     // Called by InitSurfaceHeatBalance.
    5718             : 
    5719             :     // REFERENCES:
    5720             :     // Based on DOE-2.1E subroutine DINTIL.
    5721      722471 :     auto &dl = state.dataDayltg;
    5722             : 
    5723      722471 :     Real64 constexpr tmpSWIterStep(0.05); // step of switching factor, assuming maximum of 20 switching states
    5724             : 
    5725             :     int NREFPT; // Number of daylighting reference points
    5726             :     int iSky1;  // Sky type index values for averaging two sky types
    5727             :     int iSky2;
    5728      722471 :     Array1D<Real64> SetPnt; // Illuminance setpoint at reference points (lux)
    5729      722471 :     Array1D<Real64> GLRNEW; // New glare index at reference point
    5730             : 
    5731      722471 :     auto &thisDayltgCtrl = dl->daylightControl(daylightCtrlNum);
    5732      722471 :     int enclNum = thisDayltgCtrl.enclIndex;
    5733      722471 :     auto &thisEnclDaylight = dl->enclDaylight(enclNum);
    5734             :     int ISWFLG; // Switchable glazing flag: =1 if one or more windows in a zone
    5735             :     //  has switchable glazing that adjusts visible transmittance to just meet
    5736             :     //  daylighting setpoint; =0 otherwise.
    5737             :     int ICtrl;           // Window shading control pointer
    5738             :     Real64 VTRAT;        // Ratio between switched and unswitched visible transmittance at normal incidence
    5739             :     Real64 BACL;         // Window background (surround) luminance for glare calc (cd/m2)
    5740             :     Real64 SkyWeight;    // Weighting factor used to average two different sky types
    5741             :     Real64 HorIllSkyFac; // Ratio between horizontal illuminance from sky horizontal irradiance and
    5742             :     //   luminous efficacy and horizontal illuminance from averaged sky
    5743             :     bool GlareFlag; // True if maximum glare is exceeded
    5744             : 
    5745             :     Real64 VTRatio;  // VT (visible transmittance) ratio = VTNow / VTMaster
    5746             :     Real64 VTNow;    // VT of the time step actual TC window
    5747             :     Real64 VTMaster; // VT of the base/master TC window
    5748             : 
    5749      722471 :     Array2D<std::array<std::array<Real64, (int)DataSurfaces::WinCover::Num>, (int)Lum::Num>> tmpDaylFromWinAtRefPt;
    5750             : 
    5751      722471 :     bool breakOuterLoop(false);
    5752      722471 :     bool continueOuterLoop(false);
    5753             : 
    5754             :     struct ShadeGroupLums
    5755             :     {
    5756             :         Array1D<std::array<std::array<Real64, (int)DataSurfaces::WinCover::Num>, (int)Lum::Num>> WDAYIL; // Illuminance from window at ref-point
    5757             :         Array1D<std::array<Real64, (int)Lum::Num>> RDAYIL; // Illuminance from window at ref-point after closing shade
    5758             :         Real64 switchedWinLum;
    5759             :         Real64 unswitchedWinLum;
    5760             :         Real64 switchedTvis;
    5761             :         Real64 unswitchedTvis;
    5762             :         Real64 lumRatio;
    5763             :     };
    5764             : 
    5765      722471 :     Array1D<ShadeGroupLums> shadeGroupsLums;
    5766             : 
    5767             :     // Array2D<std::array<std::array<Real64, (int)DataSurfaces::WinCover::Num>, iLum_Num>> WDAYIL; // Illuminance from window at reference point
    5768             :     // (second index)
    5769             :     //   the number of shade deployment groups (third index)
    5770             :     // Array2D<std::array<Real64, (int)DataSurfaces::WinCover::Num>> WBACLU; // Background illuminance from window at reference point (second index)
    5771             :     //   the number of shade deployment groups (third index)
    5772             :     // Array2D<std::array<Real64, iLum_Num>> RDAYIL; // Illuminance from window at reference point after closing shade
    5773             :     // Array2D<Real64> RBACLU; // Background illuminance from window at reference point after closing shade
    5774             :     // Array1D<Real64> DILLSW;         // Illuminance a ref point from a group of windows that can be switched together,
    5775             :     // Array1D<Real64> DILLUN;         //  and from those that aren't (lux)
    5776             :     // Array1D<Real64> TVIS1;  // Visible transmittance at normal incidence of unswitched glazing
    5777             :     // Array1D<Real64> TVIS2;  // Visible transmittance at normal incidence of fully-switched glazing
    5778             :     // Array1D<Real64> ASETIL; // Illuminance ratio (lux)
    5779             : 
    5780      722471 :     if (thisDayltgCtrl.DaylightMethod != DaylightingMethod::SplitFlux) return;
    5781             : 
    5782      720434 :     NREFPT = thisDayltgCtrl.TotalDaylRefPoints;
    5783             : 
    5784      720434 :     if (dl->DayltgInteriorIllum_firstTime) {
    5785          64 :         dl->DaylIllum.allocate(dl->maxNumRefPtInAnyDaylCtrl);
    5786          64 :         dl->DayltgInteriorIllum_firstTime = false;
    5787             :     }
    5788             : 
    5789             :     // size these for the maximum of the shade deployment order
    5790      720434 :     shadeGroupsLums.allocate(dl->maxShadeDeployOrderExtWins);
    5791      825315 :     for (auto &shadeGroupLums : shadeGroupsLums) {
    5792      104881 :         shadeGroupLums.WDAYIL.allocate(dl->maxControlRefPoints);
    5793      104881 :         shadeGroupLums.RDAYIL.allocate(dl->maxControlRefPoints);
    5794             :     }
    5795             : 
    5796             :     // Three arrays to save original clear and dark (fully switched) states'
    5797             :     //  zone/window daylighting properties.
    5798      720434 :     tmpDaylFromWinAtRefPt.allocate(dl->maxNumRefPtInAnyDaylCtrl, dl->maxEnclSubSurfaces);
    5799             : 
    5800      720434 :     SetPnt.allocate(dl->maxNumRefPtInAnyDaylCtrl);
    5801      720434 :     dl->DaylIllum.allocate(dl->maxNumRefPtInAnyDaylCtrl);
    5802      720434 :     GLRNEW.allocate(dl->maxNumRefPtInAnyDaylCtrl);
    5803             : 
    5804     1683884 :     for (int iRefPt = 1; iRefPt <= (int)dl->maxNumRefPtInAnyDaylCtrl; ++iRefPt) {
    5805    11332598 :         for (int iExtWin = 1; iExtWin <= (int)dl->maxEnclSubSurfaces; ++iExtWin) {
    5806    10369148 :             auto &tmpDayl = tmpDaylFromWinAtRefPt(iRefPt, iExtWin);
    5807    10369148 :             tmpDayl[iLum_Illum] = tmpDayl[iLum_Back] = tmpDayl[iLum_Source] = {0.0, 0.0};
    5808             :         }
    5809             :     }
    5810             : 
    5811             :     // Initialize reference point illuminance and window background luminance
    5812     1677232 :     for (int IL = 1; IL <= NREFPT; ++IL) {
    5813      956798 :         auto &refPt = thisDayltgCtrl.refPts(IL);
    5814      956798 :         SetPnt(IL) = refPt.illumSetPoint;
    5815      956798 :         dl->DaylIllum(IL) = 0.0;
    5816      956798 :         refPt.lums[iLum_Back] = 0.0;
    5817             :     }
    5818             : 
    5819      720434 :     if (state.dataEnvrn->SkyClearness > 3.0) { // Sky is average of clear and clear turbid
    5820      337939 :         SkyWeight = min(1.0, (state.dataEnvrn->SkyClearness - 3.0) / 3.0);
    5821      337939 :         iSky1 = (int)SkyType::Clear;
    5822      337939 :         iSky2 = (int)SkyType::ClearTurbid;
    5823      382495 :     } else if (state.dataEnvrn->SkyClearness > 1.2) { // Sky is average of clear turbid and intermediate
    5824       78949 :         SkyWeight = (state.dataEnvrn->SkyClearness - 1.2) / 1.8;
    5825       78949 :         iSky1 = (int)SkyType::ClearTurbid;
    5826       78949 :         iSky2 = (int)SkyType::Intermediate;
    5827             :     } else { // Sky is average of intermediate and overcast
    5828      303546 :         SkyWeight = min(1.0, max(0.0, (state.dataEnvrn->SkyClearness - 1.0) / 0.2, (state.dataEnvrn->SkyBrightness - 0.05) / 0.4));
    5829      303546 :         iSky1 = (int)SkyType::Intermediate;
    5830      303546 :         iSky2 = (int)SkyType::Overcast;
    5831             :     }
    5832             : 
    5833             :     // First loop over exterior windows associated with this zone. The window may be an exterior window in
    5834             :     // the zone or an exterior window in an adjacent zone that shares an interior window with the zone.
    5835             :     // Find contribution of each window to the daylight illum and to the glare numerator at each reference point.
    5836             :     // Use shading flags set in WindowShadingManager.
    5837     2155929 :     for (int loop = 1; loop <= thisEnclDaylight.NumOfDayltgExtWins; ++loop) {
    5838     1435495 :         int IWin = thisEnclDaylight.DayltgExtWinSurfNums(loop);
    5839             : 
    5840             :         // Added TH 6/29/2009 for thermochromic windows
    5841     1435495 :         VTRatio = 1.0;
    5842     1435495 :         if (NREFPT > 0) {
    5843     1435495 :             int const IConst = state.dataSurface->Surface(IWin).Construction;
    5844     1435495 :             auto const &construction = state.dataConstruction->Construct(IConst);
    5845     1435495 :             if (construction.TCFlag == 1) {
    5846             :                 // For thermochromic windows, daylight and glare factors are always calculated
    5847             :                 //  based on the master construction. They need to be adjusted by the VTRatio, including:
    5848             :                 //  ZoneDaylight()%DaylIllFacSky, DaylIllFacSun, DaylIllFacSunDisk; DaylBackFacSky,
    5849             :                 //  DaylBackFacSun, DaylBackFacSunDisk, DaylSourceFacSky, DaylSourceFacSun, DaylSourceFacSunDisk
    5850           0 :                 VTNow = General::POLYF(1.0, construction.TransVisBeamCoef);
    5851           0 :                 VTMaster = General::POLYF(1.0, state.dataConstruction->Construct(construction.TCMasterConst).TransVisBeamCoef);
    5852           0 :                 VTRatio = VTNow / VTMaster;
    5853             :             }
    5854             :         }
    5855             : 
    5856     4177794 :         bool ShadedOrDiffusingGlassWin = state.dataSurface->SurfWinWindowModelType(IWin) != WindowModel::BSDF &&
    5857     2742299 :                                          (IS_SHADED(state.dataSurface->SurfWinShadingFlag(IWin)) || state.dataSurface->SurfWinSolarDiffusing(IWin));
    5858             : 
    5859     1435495 :         Real64 wgtCurrHr = state.dataGlobal->WeightNow;
    5860     1435495 :         Real64 wgtPrevHr = state.dataGlobal->WeightPreviousHour;
    5861             : 
    5862     1435495 :         std::array<Illums, (int)DataSurfaces::WinCover::Num> SFHR; // Sky source luminance factor for sky type, bare/shaded window
    5863     1435495 :         std::array<Illums, (int)DataSurfaces::WinCover::Num> DFHR; // Sky daylight factor for sky type, bare/shaded window
    5864     1435495 :         std::array<Illums, (int)DataSurfaces::WinCover::Num> BFHR; // Sky background luminance factor for sky type, bare/shaded window
    5865             : 
    5866             :         // Loop over reference points
    5867     3661441 :         for (int IL = 1; IL <= NREFPT; ++IL) {
    5868             : 
    5869     2225946 :             auto const &daylFacCurr = thisDayltgCtrl.daylFac[state.dataGlobal->HourOfDay](loop, IL, 1);
    5870     2225946 :             auto const &daylFacPrev = thisDayltgCtrl.daylFac[state.dataGlobal->PreviousHour](loop, IL, 1);
    5871             :             // Daylight factors for current sun position
    5872     2225946 :             auto const &illFacCurr = daylFacCurr[iLum_Illum];
    5873     2225946 :             auto const &illFacPrev = daylFacPrev[iLum_Illum];
    5874     2225946 :             auto &dfhr = DFHR[iWinCover_Bare];
    5875     2225946 :             auto const &backFacCurr = daylFacCurr[iLum_Back];
    5876     2225946 :             auto const &backFacPrev = daylFacPrev[iLum_Back];
    5877     2225946 :             auto &bfhr = BFHR[iWinCover_Bare];
    5878     2225946 :             auto const &sourceFacCurr = daylFacCurr[iLum_Source];
    5879     2225946 :             auto const &sourceFacPrev = daylFacPrev[iLum_Source];
    5880     2225946 :             auto &sfhr = SFHR[iWinCover_Bare];
    5881             : 
    5882     2225946 :             auto const &daylFac2Curr = thisDayltgCtrl.daylFac[state.dataGlobal->HourOfDay](loop, IL, 2);
    5883     2225946 :             auto const &daylFac2Prev = thisDayltgCtrl.daylFac[state.dataGlobal->PreviousHour](loop, IL, 2);
    5884             : 
    5885     2225946 :             auto const &illFac2Curr = daylFac2Curr[iLum_Illum];
    5886     2225946 :             auto const &illFac2Prev = daylFac2Prev[iLum_Illum];
    5887     2225946 :             auto &dfhr2 = DFHR[iWinCover_Shaded];
    5888     2225946 :             auto const &backFac2Curr = daylFac2Curr[iLum_Back];
    5889     2225946 :             auto const &backFac2Prev = daylFac2Prev[iLum_Back];
    5890     2225946 :             auto &bfhr2 = BFHR[iWinCover_Shaded];
    5891     2225946 :             auto const &sourceFac2Curr = daylFac2Curr[iLum_Source];
    5892     2225946 :             auto const &sourceFac2Prev = daylFac2Prev[iLum_Source];
    5893     2225946 :             auto &sfhr2 = SFHR[iWinCover_Shaded];
    5894             : 
    5895     2225946 :             int SurfWinSlatsAngIndex = state.dataSurface->SurfWinSlatsAngIndex(IWin);
    5896     2225946 :             int slatAngLo = SurfWinSlatsAngIndex + 1;
    5897     2225946 :             int slatAngHi = min(Material::MaxSlatAngs + 1, slatAngLo + 1);
    5898     2225946 :             Real64 interpFac = state.dataSurface->SurfWinSlatsAngInterpFac(IWin);
    5899             : 
    5900     2225946 :             auto const &daylFacLoCurr = thisDayltgCtrl.daylFac[state.dataGlobal->HourOfDay](loop, IL, slatAngLo);
    5901     2225946 :             auto const &daylFacLoPrev = thisDayltgCtrl.daylFac[state.dataGlobal->PreviousHour](loop, IL, slatAngLo);
    5902     2225946 :             auto const &daylFacHiCurr = thisDayltgCtrl.daylFac[state.dataGlobal->HourOfDay](loop, IL, slatAngHi);
    5903     2225946 :             auto const &daylFacHiPrev = thisDayltgCtrl.daylFac[state.dataGlobal->PreviousHour](loop, IL, slatAngHi);
    5904             : 
    5905     2225946 :             auto const &illFacLoCurr = daylFacLoCurr[iLum_Illum];
    5906     2225946 :             auto const &illFacLoPrev = daylFacLoPrev[iLum_Illum];
    5907     2225946 :             auto const &illFacHiCurr = daylFacHiCurr[iLum_Illum];
    5908     2225946 :             auto const &illFacHiPrev = daylFacHiPrev[iLum_Illum];
    5909             : 
    5910     2225946 :             auto const &backFacLoCurr = daylFacLoCurr[iLum_Back];
    5911     2225946 :             auto const &backFacLoPrev = daylFacLoPrev[iLum_Back];
    5912     2225946 :             auto const &backFacHiCurr = daylFacHiCurr[iLum_Back];
    5913     2225946 :             auto const &backFacHiPrev = daylFacHiPrev[iLum_Back];
    5914             : 
    5915     2225946 :             auto const &sourceFacLoCurr = daylFacLoCurr[iLum_Source];
    5916     2225946 :             auto const &sourceFacLoPrev = daylFacLoPrev[iLum_Source];
    5917     2225946 :             auto const &sourceFacHiCurr = daylFacHiCurr[iLum_Source];
    5918     2225946 :             auto const &sourceFacHiPrev = daylFacHiPrev[iLum_Source];
    5919             : 
    5920    11129730 :             for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
    5921             : 
    5922             :                 // ===Bare window===
    5923             :                 // Sky daylight factor for sky type (second index), bare/shaded window (first index)
    5924     8903784 :                 dfhr.sky[iSky] = VTRatio * (wgtCurrHr * illFacCurr.sky[iSky] + wgtPrevHr * illFacPrev.sky[iSky]);
    5925     8903784 :                 bfhr.sky[iSky] = VTRatio * (wgtCurrHr * backFacCurr.sky[iSky] + wgtPrevHr * backFacPrev.sky[iSky]);
    5926     8903784 :                 sfhr.sky[iSky] = VTRatio * (wgtCurrHr * sourceFacCurr.sky[iSky] + wgtPrevHr * sourceFacPrev.sky[iSky]);
    5927             : 
    5928     8903784 :                 if (ShadedOrDiffusingGlassWin) {
    5929             : 
    5930             :                     // ===Shaded window or window with diffusing glass===
    5931      638724 :                     if (!state.dataSurface->SurfWinMovableSlats(IWin)) {
    5932             :                         // Shade, screen, blind with fixed slats, or diffusing glass
    5933      638724 :                         dfhr2.sky[iSky] = VTRatio * (wgtCurrHr * illFac2Curr.sky[iSky] + wgtPrevHr * illFac2Prev.sky[iSky]);
    5934      638724 :                         bfhr2.sky[iSky] = VTRatio * (wgtCurrHr * backFac2Curr.sky[iSky] + wgtPrevHr * backFac2Prev.sky[iSky]);
    5935      638724 :                         sfhr2.sky[iSky] = VTRatio * (wgtCurrHr * sourceFac2Curr.sky[iSky] + wgtPrevHr * sourceFac2Prev.sky[iSky]);
    5936             : 
    5937             :                     } else { // Blind with movable slats
    5938           0 :                         Real64 illSkyCurr = General::Interp(illFacLoCurr.sky[iSky], illFacHiCurr.sky[iSky], interpFac);
    5939           0 :                         Real64 backSkyCurr = General::Interp(backFacLoCurr.sky[iSky], backFacHiCurr.sky[iSky], interpFac);
    5940           0 :                         Real64 sourceSkyCurr = General::Interp(sourceFacLoCurr.sky[iSky], sourceFacHiCurr.sky[iSky], interpFac);
    5941             : 
    5942           0 :                         Real64 illSkyPrev = General::Interp(illFacLoPrev.sky[iSky], illFacHiPrev.sky[iSky], interpFac);
    5943           0 :                         Real64 backSkyPrev = General::Interp(backFacLoPrev.sky[iSky], backFacHiPrev.sky[iSky], interpFac);
    5944           0 :                         Real64 sourceSkyPrev = General::Interp(sourceFacLoPrev.sky[iSky], sourceFacHiPrev.sky[iSky], interpFac);
    5945             : 
    5946           0 :                         dfhr2.sky[iSky] = VTRatio * (wgtCurrHr * illSkyCurr + wgtPrevHr * illSkyPrev);
    5947           0 :                         bfhr2.sky[iSky] = VTRatio * (wgtCurrHr * backSkyCurr + wgtPrevHr * backSkyPrev);
    5948           0 :                         sfhr2.sky[iSky] = VTRatio * (wgtCurrHr * sourceSkyCurr + wgtPrevHr * sourceSkyPrev);
    5949             : 
    5950             :                     } // End of check if window has blind with movable slats
    5951             :                 }     // End of check if window is shaded or has diffusing glass
    5952             :             }         // for (iSky)
    5953             : 
    5954             :             // Sun daylight factor for bare/shaded window
    5955     4451892 :             DFHR[iWinCover_Bare].sun =
    5956     2225946 :                 VTRatio * (wgtCurrHr * (illFacCurr.sun + illFacCurr.sunDisk) + wgtPrevHr * (illFacPrev.sun + illFacPrev.sunDisk));
    5957             : 
    5958             :             // Sun background luminance factor for bare/shaded window
    5959     4451892 :             BFHR[iWinCover_Bare].sun =
    5960     2225946 :                 VTRatio * (wgtCurrHr * (backFacCurr.sun + backFacCurr.sunDisk) + wgtPrevHr * (backFacPrev.sun + backFacPrev.sunDisk));
    5961             : 
    5962             :             // Sun source luminance factor for bare/shaded window
    5963     4451892 :             SFHR[iWinCover_Bare].sun =
    5964     2225946 :                 VTRatio * (wgtCurrHr * (sourceFacCurr.sun + sourceFacCurr.sunDisk) + wgtPrevHr * (sourceFacPrev.sun + sourceFacPrev.sunDisk));
    5965             : 
    5966     2225946 :             if (ShadedOrDiffusingGlassWin) {
    5967             : 
    5968             :                 // ===Shaded window or window with diffusing glass===
    5969      159681 :                 if (!state.dataSurface->SurfWinMovableSlats(IWin)) {
    5970             :                     // Shade, screen, blind with fixed slats, or diffusing glass
    5971      159681 :                     DFHR[iWinCover_Shaded].sun = VTRatio * (wgtCurrHr * illFac2Curr.sun + wgtPrevHr * illFac2Prev.sun);
    5972      159681 :                     BFHR[iWinCover_Shaded].sun = VTRatio * (wgtCurrHr * backFac2Curr.sun + wgtPrevHr * backFac2Prev.sun);
    5973      159681 :                     SFHR[iWinCover_Shaded].sun = VTRatio * (wgtCurrHr * sourceFac2Curr.sun + wgtPrevHr * sourceFac2Prev.sun);
    5974             : 
    5975      159681 :                     if (!state.dataSurface->SurfWinSlatsBlockBeam(IWin)) {
    5976      159681 :                         DFHR[iWinCover_Shaded].sun += VTRatio * (wgtCurrHr * illFac2Curr.sunDisk + wgtPrevHr * illFac2Prev.sunDisk);
    5977      159681 :                         BFHR[iWinCover_Shaded].sun += VTRatio * (wgtCurrHr * backFac2Curr.sunDisk + wgtPrevHr * backFac2Prev.sunDisk);
    5978      159681 :                         SFHR[iWinCover_Shaded].sun += VTRatio * (wgtCurrHr * sourceFac2Curr.sunDisk + wgtPrevHr * sourceFac2Prev.sunDisk);
    5979             :                     }
    5980             : 
    5981             :                 } else { // Blind with movable slats
    5982             :                     // int SurfWinSlatsAngIndex = state.dataSurface->SurfWinSlatsAngIndex(IWin);
    5983             :                     // int slatAngLo = SurfWinSlatsAngIndex + 1;
    5984             :                     // int slatAngHi = min(Material::MaxSlatAngs + 1, slatAngLo + 1);
    5985           0 :                     Real64 SurfWinSlatsAngInterpFac = state.dataSurface->SurfWinSlatsAngInterpFac(IWin);
    5986             : 
    5987           0 :                     Real64 DaylIllFacSunNow = General::Interp(illFacLoCurr.sun, illFacHiCurr.sun, SurfWinSlatsAngInterpFac);
    5988           0 :                     Real64 DaylBackFacSunNow = General::Interp(backFacLoCurr.sun, backFacHiCurr.sun, SurfWinSlatsAngInterpFac);
    5989           0 :                     Real64 DaylSourceFacSunNow = General::Interp(sourceFacLoCurr.sun, sourceFacHiCurr.sun, SurfWinSlatsAngInterpFac);
    5990           0 :                     Real64 DaylIllFacSunPrev = General::Interp(illFacLoPrev.sun, illFacHiPrev.sun, SurfWinSlatsAngInterpFac);
    5991           0 :                     Real64 DaylBackFacSunPrev = General::Interp(backFacLoPrev.sun, backFacHiPrev.sun, SurfWinSlatsAngInterpFac);
    5992           0 :                     Real64 DaylSourceFacSunPrev = General::Interp(sourceFacLoPrev.sun, sourceFacHiPrev.sun, SurfWinSlatsAngInterpFac);
    5993           0 :                     DFHR[iWinCover_Shaded].sun = VTRatio * (wgtCurrHr * DaylIllFacSunNow + wgtPrevHr * DaylIllFacSunPrev);
    5994           0 :                     BFHR[iWinCover_Shaded].sun = VTRatio * (wgtCurrHr * DaylBackFacSunNow + wgtPrevHr * DaylBackFacSunPrev);
    5995           0 :                     SFHR[iWinCover_Shaded].sun = VTRatio * (wgtCurrHr * DaylSourceFacSunNow + wgtPrevHr * DaylSourceFacSunPrev);
    5996             : 
    5997             :                     // We add the contribution from the solar disk if slats do not block beam solar
    5998             :                     // TH CR 8010, DaylIllFacSunDisk needs to be interpolated
    5999           0 :                     if (!state.dataSurface->SurfWinSlatsBlockBeam(IWin)) {
    6000           0 :                         Real64 DaylIllFacSunDiskNow = General::Interp(illFacLoCurr.sunDisk, illFacHiCurr.sunDisk, SurfWinSlatsAngInterpFac);
    6001           0 :                         Real64 DaylBackFacSunDiskNow = General::Interp(backFacLoCurr.sunDisk, backFacHiCurr.sunDisk, SurfWinSlatsAngInterpFac);
    6002           0 :                         Real64 DaylSourceFacSunDiskNow = General::Interp(sourceFacLoCurr.sunDisk, sourceFacHiCurr.sunDisk, SurfWinSlatsAngInterpFac);
    6003           0 :                         Real64 DaylIllFacSunDiskPrev = General::Interp(illFacLoPrev.sunDisk, illFacHiPrev.sunDisk, SurfWinSlatsAngInterpFac);
    6004           0 :                         Real64 DaylBackFacSunDiskPrev = General::Interp(backFacLoPrev.sunDisk, backFacHiPrev.sunDisk, SurfWinSlatsAngInterpFac);
    6005           0 :                         Real64 DaylSourceFacSunDiskPrev = General::Interp(sourceFacLoPrev.sunDisk, sourceFacHiPrev.sunDisk, SurfWinSlatsAngInterpFac);
    6006           0 :                         DFHR[iWinCover_Shaded].sun += VTRatio * (wgtCurrHr * DaylIllFacSunDiskNow + wgtPrevHr * DaylIllFacSunDiskPrev);
    6007           0 :                         BFHR[iWinCover_Shaded].sun += VTRatio * (wgtCurrHr * DaylBackFacSunDiskNow + wgtPrevHr * DaylBackFacSunDiskPrev);
    6008           0 :                         SFHR[iWinCover_Shaded].sun += VTRatio * (wgtCurrHr * DaylSourceFacSunDiskNow + wgtPrevHr * DaylSourceFacSunDiskPrev);
    6009             :                     }
    6010             :                 } // End of check if window has blind with movable slats
    6011             :             }     // End of check if window is shaded or has diffusing glass
    6012             : 
    6013             :             // Get illuminance at ref point from bare and shaded window by
    6014             :             // multiplying daylight factors by exterior horizontal illuminance
    6015             : 
    6016             :             // Adding 0.001 in the following prevents zero HorIllSky in early morning or late evening when sun
    6017             :             // is up in the present time step but GILSK(ISky,HourOfDay) and GILSK(ISky,NextHour) are both zero.
    6018     2225946 :             auto const &gilskCurr = dl->horIllum[state.dataGlobal->HourOfDay];
    6019     2225946 :             auto const &gilskPrev = dl->horIllum[state.dataGlobal->PreviousHour];
    6020             : 
    6021             :             // HISKF is current time step horizontal illuminance from sky, calculated in DayltgLuminousEfficacy,
    6022             :             // which is called in WeatherManager. HISUNF is current time step horizontal illuminance from sun,
    6023             :             // also calculated in DayltgLuminousEfficacy.
    6024             :             Real64 horIllSky1 =
    6025     2225946 :                 state.dataGlobal->WeightNow * gilskCurr.sky[iSky1] + state.dataGlobal->WeightPreviousHour * gilskPrev.sky[iSky1] + 0.001;
    6026             :             Real64 horIllSky2 =
    6027     2225946 :                 state.dataGlobal->WeightNow * gilskCurr.sky[iSky2] + state.dataGlobal->WeightPreviousHour * gilskPrev.sky[iSky2] + 0.001;
    6028             : 
    6029     2225946 :             HorIllSkyFac = state.dataEnvrn->HISKF / ((1 - SkyWeight) * horIllSky2 + SkyWeight * horIllSky1);
    6030             : 
    6031     2225946 :             auto &daylFromWinAtRefPt = thisDayltgCtrl.refPts(IL).extWins(loop).lums;
    6032     2225946 :             auto &tmpDayl = tmpDaylFromWinAtRefPt(IL, loop);
    6033     4611573 :             for (int iWinCover = 0; iWinCover < (int)WinCover::Num; ++iWinCover) {
    6034     4451892 :                 auto const &dfhr = DFHR[iWinCover];
    6035     4451892 :                 auto const &bfhr = BFHR[iWinCover];
    6036     4451892 :                 auto const &sfhr = SFHR[iWinCover];
    6037             : 
    6038             :                 // What is this?
    6039     4451892 :                 if (iWinCover == iWinCover_Shaded && !ShadedOrDiffusingGlassWin) break;
    6040             : 
    6041     2385627 :                 daylFromWinAtRefPt[iLum_Illum][iWinCover] =
    6042     2385627 :                     dfhr.sun * state.dataEnvrn->HISUNF +
    6043     2385627 :                     HorIllSkyFac * (dfhr.sky[iSky1] * SkyWeight * horIllSky1 + dfhr.sky[iSky2] * (1.0 - SkyWeight) * horIllSky2);
    6044     2385627 :                 daylFromWinAtRefPt[iLum_Back][iWinCover] =
    6045     2385627 :                     bfhr.sun * state.dataEnvrn->HISUNF +
    6046     2385627 :                     HorIllSkyFac * (bfhr.sky[iSky1] * SkyWeight * horIllSky1 + bfhr.sky[iSky2] * (1.0 - SkyWeight) * horIllSky2);
    6047     2385627 :                 daylFromWinAtRefPt[iLum_Source][iWinCover] =
    6048     2385627 :                     sfhr.sun * state.dataEnvrn->HISUNF +
    6049     2385627 :                     HorIllSkyFac * (sfhr.sky[iSky1] * SkyWeight * horIllSky1 + sfhr.sky[iSky2] * (1.0 - SkyWeight) * horIllSky2);
    6050             : 
    6051     2385627 :                 daylFromWinAtRefPt[iLum_Source][iWinCover] = max(daylFromWinAtRefPt[iLum_Source][iWinCover], 0.0);
    6052             : 
    6053             :                 // Added TH 1/21/2010 - save the original clear and dark (fully switched) states'
    6054             :                 //  zone daylighting values, needed for switachable glazings
    6055     2385627 :                 tmpDayl[iLum_Illum][iWinCover] = daylFromWinAtRefPt[iLum_Illum][iWinCover];
    6056     2385627 :                 tmpDayl[iLum_Back][iWinCover] = daylFromWinAtRefPt[iLum_Back][iWinCover];
    6057     2385627 :                 tmpDayl[iLum_Source][iWinCover] = daylFromWinAtRefPt[iLum_Source][iWinCover];
    6058             :             } // for for (iWinCover)
    6059             : 
    6060             :         } // End of reference point loop, IL
    6061             :     }     // End of first loop over exterior windows associated with this zone
    6062             : 
    6063             :     // Initialize flag that one or more windows has switchable glazing
    6064             :     // control that adjusts visible transmittance to just meet dayltg setpoint
    6065             :     // (and the window has not already been switched)
    6066      720434 :     ISWFLG = 0;
    6067             : 
    6068             :     // Second loop over windows. Find total daylight illuminance and background luminance
    6069             :     // for each ref pt from all exterior windows associated with the zone.  Use shading flags.
    6070             :     // This illuminance excludes contribution of inter-reflected illuminance produced by solar
    6071             :     // entering the zone through interior windows (which is calculated in DayltgInterReflIllFrIntWins.
    6072             : 
    6073     2155929 :     for (int loop = 1; loop <= thisEnclDaylight.NumOfDayltgExtWins; ++loop) {
    6074     1435495 :         int IWin = thisEnclDaylight.DayltgExtWinSurfNums(loop);
    6075     1435495 :         int ICtrl = state.dataSurface->Surface(IWin).activeWindowShadingControl;
    6076     1435495 :         if (state.dataSurface->Surface(IWin).HasShadeControl && ISWFLG == 0) {
    6077      121929 :             if (state.dataSurface->WindowShadingControl(ICtrl).shadingControlType == WindowShadingControlType::MeetDaylIlumSetp &&
    6078         665 :                 state.dataSurface->SurfWinShadingFlag(IWin) == WinShadingType::GlassConditionallyLightened)
    6079         665 :                 ISWFLG = 1;
    6080             :         }
    6081             : 
    6082             :         // Determine if illuminance contribution is from bare or shaded window
    6083             :         //  For switchable glazings with shading control type of WSCT_MeetDaylIlumSetp,
    6084             :         //   the shading flag is initialized at GlassConditionallyLightened (20), and
    6085             :         //   the window is initialized at clear state: IS = 1
    6086             :         //  For other windows with glare control, the shading flag is initialized at >10, to be determined
    6087     1435495 :         int IS = findWinShadingStatus(state, IWin);
    6088             : 
    6089     3661441 :         for (int IL = 1; IL <= NREFPT; ++IL) {
    6090     2225946 :             auto &refPt = thisDayltgCtrl.refPts(IL);
    6091     2225946 :             dl->DaylIllum(IL) += refPt.extWins(loop).lums[iLum_Illum][IS - 1];
    6092     2225946 :             refPt.lums[iLum_Back] += refPt.extWins(loop).lums[iLum_Back][IS - 1];
    6093             :         }
    6094             :     } // End of second window loop over exterior windows associated with this zone
    6095             : 
    6096             :     // Optical switching control (e.g. electrochromic glass) to adjust
    6097             :     // window's vis trans downward so daylight level equals or is as
    6098             :     // close as possible to the illuminance setpoint at first reference point.
    6099             :     // Assumes vis trans in the fully switched state is less than that in the
    6100             :     // unswitched state. Assumes some windows in a space may have this control and
    6101             :     // others not.
    6102             : 
    6103      720434 :     int count = 0;
    6104             : 
    6105             :     // If daylight illuminance is above setpoint, allow switching
    6106      720434 :     if (ISWFLG != 0 && dl->DaylIllum(1) > SetPnt(1)) {
    6107             : 
    6108             :         // array of flags to indicate that previously groups would have already shaded this window
    6109         385 :         Array1D_bool previously_shaded;
    6110         385 :         previously_shaded.dimension(dl->maxDayltgExtWins, false);
    6111             : 
    6112             :         // Third loop over windows.  Get illuminance at ref pt 1 from
    6113             :         // windows that can be switched (DILLSW) with a group and those that can't (DILLUN).
    6114             :         // Windows that can be switched are initially in the unswitched state. For subsequent
    6115             :         // groups the windows in previous groups are fully switched.
    6116        1155 :         for (auto &shadeGroupLum : shadeGroupsLums) {
    6117         770 :             shadeGroupLum.switchedWinLum = shadeGroupLum.unswitchedWinLum = 0.0;
    6118             :         }
    6119             : 
    6120        1155 :         for (std::size_t igroup = 1; igroup <= thisDayltgCtrl.ShadeDeployOrderExtWins.size(); igroup++) {
    6121         770 :             auto &shadeGroupLums = shadeGroupsLums(igroup);
    6122         770 :             std::vector<int> const &listOfExtWin = thisDayltgCtrl.ShadeDeployOrderExtWins[igroup - 1];
    6123        2310 :             for (const int IWin : listOfExtWin) {
    6124        1540 :                 ++count;
    6125             :                 // need to map back to the original order of the "loop" to not change all the other data structures
    6126        1540 :                 int loop = thisDayltgCtrl.MapShdOrdToLoopNum(count);
    6127        1540 :                 if (loop == 0) continue;
    6128             : 
    6129        1540 :                 if (!state.dataSurface->Surface(IWin).HasShadeControl) continue;
    6130             : 
    6131        1540 :                 int ICtrl = state.dataSurface->Surface(IWin).activeWindowShadingControl;
    6132        1540 :                 int IS = findWinShadingStatus(state, IWin);
    6133             : 
    6134        1540 :                 auto const &daylFromWinAtRefPt = thisDayltgCtrl.refPts(1).extWins(loop).lums[iLum_Illum];
    6135        1540 :                 if (state.dataSurface->SurfWinShadingFlag(IWin) == WinShadingType::GlassConditionallyLightened &&
    6136        2310 :                     state.dataSurface->WindowShadingControl(ICtrl).shadingControlType == WindowShadingControlType::MeetDaylIlumSetp &&
    6137         770 :                     !previously_shaded(loop)) {
    6138         770 :                     shadeGroupLums.switchedWinLum += daylFromWinAtRefPt[IS - 1];
    6139         770 :                     previously_shaded(loop) = true;
    6140             :                 } else {
    6141         770 :                     shadeGroupLums.unswitchedWinLum += !previously_shaded(loop) ? daylFromWinAtRefPt[IS - 1] : daylFromWinAtRefPt[iWinCover_Shaded];
    6142             :                 }
    6143         770 :             } // for (IWin)
    6144             :         }     // for (igroup)
    6145             : 
    6146             :         // Transmittance multiplier
    6147        1155 :         for (auto &shadeGroupLums : shadeGroupsLums) {
    6148         770 :             shadeGroupLums.lumRatio = (SetPnt(1) - shadeGroupLums.unswitchedWinLum) / (shadeGroupLums.switchedWinLum + 0.00001);
    6149             :         }
    6150             : 
    6151             :         // ASETIL < 1 means there's enough light, so check for switching
    6152             : 
    6153             :         // Fourth loop over windows to determine which to switch
    6154             :         // iterate in the order that the shades are specified in WindowShadeControl
    6155         385 :         count = 0;
    6156         385 :         breakOuterLoop = false;
    6157         385 :         continueOuterLoop = false;
    6158        1155 :         for (std::size_t igroup = 1; igroup <= thisDayltgCtrl.ShadeDeployOrderExtWins.size(); igroup++) {
    6159         770 :             auto &shadeGroupLums = shadeGroupsLums(igroup);
    6160         770 :             std::vector<int> const &listOfExtWin = thisDayltgCtrl.ShadeDeployOrderExtWins[igroup - 1];
    6161             : 
    6162        2310 :             for (const int IWin : listOfExtWin) {
    6163        1540 :                 ++count;
    6164        1540 :                 auto const &surfWin = state.dataSurface->SurfaceWindow(IWin);
    6165             :                 // need to map back to the original order of the "loop" to not change all the other data structures
    6166        1540 :                 int loop = thisDayltgCtrl.MapShdOrdToLoopNum(count);
    6167        1540 :                 if (loop > 0 && shadeGroupLums.lumRatio < 1.0) {
    6168             : 
    6169        1022 :                     int ICtrl = state.dataSurface->Surface(IWin).activeWindowShadingControl;
    6170        1022 :                     if (!state.dataSurface->Surface(IWin).HasShadeControl) {
    6171           0 :                         continueOuterLoop = true;
    6172           0 :                         continue;
    6173             :                     }
    6174        1764 :                     if (state.dataSurface->SurfWinShadingFlag(IWin) != WinShadingType::GlassConditionallyLightened ||
    6175         742 :                         state.dataSurface->WindowShadingControl(ICtrl).shadingControlType != WindowShadingControlType::MeetDaylIlumSetp) {
    6176         280 :                         continueOuterLoop = true;
    6177         280 :                         continue;
    6178             :                     }
    6179             : 
    6180         742 :                     int const IConst = state.dataSurface->SurfActiveConstruction(IWin);
    6181             :                     // Vis trans at normal incidence of unswitched glass
    6182         742 :                     shadeGroupLums.unswitchedTvis =
    6183         742 :                         General::POLYF(1.0, state.dataConstruction->Construct(IConst).TransVisBeamCoef) * surfWin.glazedFrac;
    6184             : 
    6185             :                     // Vis trans at normal incidence of fully switched glass
    6186         742 :                     int const IConstShaded = state.dataSurface->Surface(IWin).activeShadedConstruction;
    6187         742 :                     shadeGroupLums.switchedTvis =
    6188         742 :                         General::POLYF(1.0, state.dataConstruction->Construct(IConstShaded).TransVisBeamCoef) * surfWin.glazedFrac;
    6189             : 
    6190             :                     // Reset shading flag to indicate that window is shaded by being partially or fully switched
    6191         742 :                     state.dataSurface->SurfWinShadingFlag(IWin) = WinShadingType::SwitchableGlazing;
    6192             : 
    6193             :                     // ASETIL < 0 means illuminance from non-daylight-switchable windows exceeds setpoint,
    6194             :                     // so completely switch all daylight-switchable windows to minimize solar gain
    6195         742 :                     if (shadeGroupLums.lumRatio <= 0.0) {
    6196           0 :                         state.dataSurface->SurfWinSwitchingFactor(IWin) = 1.0;
    6197           0 :                         state.dataSurface->SurfWinVisTransSelected(IWin) = shadeGroupLums.switchedTvis;
    6198             :                     } else {
    6199             :                         // Case where 0 < ASETIL < 1: darken glass in all
    6200             :                         // daylight-switchable windows to just meet illuminance setpoint
    6201             :                         // From this equation: SETPNT(1) = DILLUN + DILLSW/TVIS1 * VisTransSelected
    6202         742 :                         state.dataSurface->SurfWinVisTransSelected(IWin) =
    6203         742 :                             max(shadeGroupLums.switchedTvis, shadeGroupLums.lumRatio * shadeGroupLums.unswitchedTvis) + 0.000001;
    6204         742 :                         state.dataSurface->SurfWinSwitchingFactor(IWin) =
    6205         742 :                             (shadeGroupLums.unswitchedTvis - state.dataSurface->SurfWinVisTransSelected(IWin)) /
    6206         742 :                             (shadeGroupLums.unswitchedTvis - shadeGroupLums.switchedTvis + 0.000001);
    6207             :                         // bound switching factor between 0 and 1
    6208         742 :                         state.dataSurface->SurfWinSwitchingFactor(IWin) = min(1.0, state.dataSurface->SurfWinSwitchingFactor(IWin));
    6209         742 :                         state.dataSurface->SurfWinSwitchingFactor(IWin) = max(0.0, state.dataSurface->SurfWinSwitchingFactor(IWin));
    6210             :                     }
    6211             : 
    6212             :                     // Adjust daylight quantities based on ratio between switched and unswitched visible transmittance
    6213        1484 :                     for (int IL = 1; IL <= NREFPT; ++IL) {
    6214             :                         // DaylIllum(IL) and BacLum(IL) were calculated at the clear state: IS = 1,
    6215             :                         //  and need to adjusted for intermediate switched state at VisTransSelected: IS = 2
    6216         742 :                         int IS = 1;
    6217             : 
    6218         742 :                         auto &daylFromWinAtRefPt = thisDayltgCtrl.refPts(IL).extWins(loop).lums;
    6219         742 :                         auto const &tmpDayl = tmpDaylFromWinAtRefPt(IL, loop);
    6220             : 
    6221         742 :                         VTRAT = state.dataSurface->SurfWinVisTransSelected(IWin) / (shadeGroupLums.unswitchedTvis + 0.000001);
    6222         742 :                         dl->DaylIllum(IL) += (VTRAT - 1.0) * daylFromWinAtRefPt[iLum_Illum][IS - 1];
    6223         742 :                         thisDayltgCtrl.refPts(IL).lums[iLum_Back] += (VTRAT - 1.0) * daylFromWinAtRefPt[iLum_Back][IS - 1];
    6224             : 
    6225             :                         // Adjust illum, background illum and source luminance for this window in intermediate switched state
    6226             :                         //  for later use in the DayltgGlare calc because SurfaceWindow(IWin)%ShadingFlag = WinShadingType::SwitchableGlazing = 2
    6227         742 :                         IS = 2;
    6228         742 :                         VTRAT = state.dataSurface->SurfWinVisTransSelected(IWin) / (shadeGroupLums.switchedTvis + 0.000001);
    6229         742 :                         daylFromWinAtRefPt[iLum_Illum][IS - 1] = VTRAT * tmpDayl[iLum_Illum][IS - 1];
    6230         742 :                         daylFromWinAtRefPt[iLum_Back][IS - 1] = VTRAT * tmpDayl[iLum_Back][IS - 1];
    6231         742 :                         daylFromWinAtRefPt[iLum_Source][IS - 1] = VTRAT * tmpDayl[iLum_Source][IS - 1];
    6232             :                     } // for (IL)
    6233             :                 }     // if (loop > 0 && ASETIL < 1)
    6234             :                 // If new daylight does not exceed the illuminance setpoint, done, no more checking other groups of switchable glazings
    6235        1260 :                 if (dl->DaylIllum(1) <= SetPnt(1)) {
    6236           0 :                     breakOuterLoop = true;
    6237           0 :                     break;
    6238             :                 }
    6239         770 :             } // for (Win)
    6240         770 :             if (breakOuterLoop) break;
    6241         770 :             if (continueOuterLoop) continue;
    6242             :         } // for (igroup)
    6243             : 
    6244         385 :     } // ISWFLG /= 0 .AND. DaylIllum(1) > SETPNT(1)
    6245             : 
    6246             :     // loop over windows to do luminance based control
    6247      720434 :     count = 0;
    6248      823985 :     for (int igroup = 1; igroup <= (int)thisDayltgCtrl.ShadeDeployOrderExtWins.size(); igroup++) {
    6249      226810 :         for (int const IWin : thisDayltgCtrl.ShadeDeployOrderExtWins[igroup - 1]) {
    6250      123259 :             ++count;
    6251      123259 :             int ICtrl = state.dataSurface->Surface(IWin).activeWindowShadingControl;
    6252      123259 :             WindowShadingControlType shCtrlType = state.dataSurface->WindowShadingControl(ICtrl).shadingControlType;
    6253      123259 :             if (!((shCtrlType == WindowShadingControlType::HiSolar_HiLumin_OffMidNight) ||
    6254             :                   (shCtrlType == WindowShadingControlType::HiSolar_HiLumin_OffSunset) ||
    6255             :                   (shCtrlType == WindowShadingControlType::HiSolar_HiLumin_OffNextMorning)))
    6256      121929 :                 continue;
    6257             :             // need to map back to the original order of the "loop" to not change all the other data structures
    6258        1330 :             int loop = thisDayltgCtrl.MapShdOrdToLoopNum(count);
    6259        1330 :             if (loop == 0) continue;
    6260             : 
    6261        1330 :             WinShadingType currentFlag = state.dataSurface->SurfWinShadingFlag(IWin);
    6262        1330 :             WinShadingType ShType = state.dataSurface->WindowShadingControl(ICtrl).ShadingType;
    6263        1330 :             if ((currentFlag != WinShadingType::IntShadeConditionallyOff) && (currentFlag != WinShadingType::GlassConditionallyLightened) &&
    6264         714 :                 (currentFlag != WinShadingType::ExtShadeConditionallyOff) && (currentFlag != WinShadingType::IntBlindConditionallyOff) &&
    6265         714 :                 (currentFlag != WinShadingType::ExtBlindConditionallyOff) && (currentFlag != WinShadingType::BGShadeConditionallyOff) &&
    6266             :                 (currentFlag != WinShadingType::BGBlindConditionallyOff))
    6267         714 :                 continue;
    6268             : 
    6269         616 :             auto const &daylFromWinAtRefPt = thisDayltgCtrl.refPts(1).extWins(loop).lums;
    6270         616 :             if (daylFromWinAtRefPt[iLum_Source][iWinCover_Bare] > state.dataSurface->WindowShadingControl(ICtrl).SetPoint2) {
    6271             :                 // shade on if luminance of this window is above setpoint
    6272           0 :                 state.dataSurface->SurfWinShadingFlag(IWin) = ShType;
    6273             :                 // update total illuminance and background luminance
    6274           0 :                 for (int IL = 1; IL <= NREFPT; ++IL) {
    6275           0 :                     dl->DaylIllum(IL) += daylFromWinAtRefPt[iLum_Illum][iWinCover_Shaded] - daylFromWinAtRefPt[iLum_Illum][iWinCover_Bare];
    6276           0 :                     thisDayltgCtrl.refPts(IL).lums[iLum_Back] +=
    6277           0 :                         daylFromWinAtRefPt[iLum_Back][iWinCover_Shaded] - daylFromWinAtRefPt[iLum_Back][iWinCover_Bare];
    6278             :                 }
    6279             :             } else {
    6280             :                 // shade off if luminance is below setpoint
    6281         616 :                 state.dataSurface->SurfWinShadingFlag(IWin) = WinShadingType::ShadeOff;
    6282             :             }
    6283      103551 :         } // for (IWin)
    6284             :     }     // for (igroup)
    6285             : 
    6286             :     // Calculate glare index at each reference point assuming the daylight illuminance setpoint is
    6287             :     //  met at both reference points, either by daylight or electric lights
    6288     1677232 :     for (int IL = 1; IL <= NREFPT; ++IL) {
    6289      956798 :         auto &refPt = thisDayltgCtrl.refPts(IL);
    6290      956798 :         BACL = max(SetPnt(IL) * dl->enclDaylight(enclNum).aveVisDiffReflect / Constant::Pi, refPt.lums[iLum_Back]);
    6291             :         // DayltgGlare uses ZoneDaylight(ZoneNum)%SourceLumFromWinAtRefPt(IL,1,loop) for unshaded windows, and
    6292             :         //  ZoneDaylight(ZoneNum)%SourceLumFromWinAtRefPt(IL,2,loop) for shaded windows
    6293      956798 :         refPt.glareIndex = DayltgGlare(state, IL, BACL, daylightCtrlNum);
    6294             :     }
    6295             : 
    6296             :     // Check if glare level is less than maximum allowed at each ref pt.  If maximum
    6297             :     // is exceeded at either ref pt, attempt to reduce glare to acceptable level by closing
    6298             :     // shading device on windows that have shades that have not already been closed.
    6299      720434 :     GlareFlag = false;
    6300     1531519 :     for (auto const &refPt : thisDayltgCtrl.refPts) {
    6301      925380 :         if (refPt.glareIndex > thisDayltgCtrl.MaxGlareallowed) {
    6302      114295 :             GlareFlag = true;
    6303      114295 :             break;
    6304             :         }
    6305             :     }
    6306             : 
    6307      720434 :     if (GlareFlag) {
    6308             :         bool blnCycle;
    6309             :         bool GlareOK;
    6310             :         Real64 tmpMult;
    6311             :         // Glare is too high at a ref pt.  Loop through windows.
    6312      114295 :         count = 0;
    6313             : 
    6314      114295 :         continueOuterLoop = false;
    6315      158423 :         for (std::size_t igroup = 1; igroup <= thisDayltgCtrl.ShadeDeployOrderExtWins.size(); igroup++) {
    6316       57952 :             auto &shadeGroupLums = shadeGroupsLums(igroup);
    6317       57952 :             std::vector<int> const &listOfExtWin = thisDayltgCtrl.ShadeDeployOrderExtWins[igroup - 1];
    6318             : 
    6319       57952 :             int countBeforeListOfExtWinLoop = count;
    6320       57952 :             bool atLeastOneGlareControlIsActive = false;
    6321             : 
    6322      132912 :             for (const int IWin : listOfExtWin) {
    6323       74960 :                 ++count;
    6324             :                 // need to map back to the original order of the "loop" to not change all the other data structures
    6325       74960 :                 int loop = thisDayltgCtrl.MapShdOrdToLoopNum(count);
    6326       74960 :                 if (loop == 0) continue;
    6327             : 
    6328       74960 :                 auto const &surfWin = state.dataSurface->SurfaceWindow(IWin);
    6329             :                 // Check if window is eligible for glare control
    6330             :                 // TH 1/21/2010. Switchable glazings already in partially switched state
    6331             :                 //  should be allowed to further dim to control glare
    6332             :                 // if (SurfWinShadingFlag(IWin) <= BGBlind && SurfWinShadingFlag(IWin) != SwitchableGlazing) {
    6333      149920 :                 if (NOT_SHADED(state.dataSurface->SurfWinShadingFlag(IWin)) || ANY_SHADE_SCREEN(state.dataSurface->SurfWinShadingFlag(IWin)) ||
    6334       74960 :                     ANY_BLIND(state.dataSurface->SurfWinShadingFlag(IWin))) {
    6335           0 :                     continueOuterLoop = false;
    6336           0 :                     continue;
    6337             :                 }
    6338       74960 :                 ICtrl = state.dataSurface->Surface(IWin).activeWindowShadingControl;
    6339       74960 :                 if (!state.dataSurface->Surface(IWin).HasShadeControl) {
    6340           0 :                     continueOuterLoop = false;
    6341           0 :                     continue;
    6342             :                 }
    6343       74960 :                 if (state.dataSurface->WindowShadingControl(ICtrl).GlareControlIsActive) {
    6344       74960 :                     atLeastOneGlareControlIsActive = true;
    6345             : 
    6346             :                     // Illuminance (WDAYIL) and background luminance (WBACLU) contribution from this
    6347             :                     // window without shading (IS=1) and with shading (IS=2) for each ref pt
    6348             :                     //  For switchable windows, this may be partially switched rather than fully dark
    6349      153836 :                     for (int IL = 1; IL <= NREFPT; ++IL) {
    6350       78876 :                         auto const &daylFromWinAtRefPt = thisDayltgCtrl.refPts(IL).extWins(loop).lums;
    6351      236628 :                         for (int iWinCover = 0; iWinCover < (int)WinCover::Num; ++iWinCover) {
    6352      157752 :                             shadeGroupLums.WDAYIL(IL)[iLum_Illum][iWinCover] = daylFromWinAtRefPt[iLum_Illum][iWinCover];
    6353      157752 :                             shadeGroupLums.WDAYIL(IL)[iLum_Back][iWinCover] = daylFromWinAtRefPt[iLum_Back][iWinCover];
    6354             :                         }
    6355             :                     }
    6356             : 
    6357             :                     // Recalculate illuminance and glare with shading on this window.
    6358             :                     //  For switchable glazings, this is the fully switched (dark) state
    6359      153836 :                     for (int IL = 1; IL <= NREFPT; ++IL) {
    6360       78876 :                         auto &rdayil = shadeGroupLums.RDAYIL(IL);
    6361       78876 :                         auto const &wdayil = shadeGroupLums.WDAYIL(IL);
    6362       78876 :                         auto const &refPt = thisDayltgCtrl.refPts(IL);
    6363             : 
    6364       78876 :                         if (state.dataSurface->SurfWinShadingFlag(IWin) != WinShadingType::SwitchableGlazing) {
    6365             :                             // for non switchable glazings or switchable glazings not switched yet (still in clear state)
    6366             :                             //  SurfaceWindow(IWin)%ShadingFlag = WinShadingFlag::GlassConditionallyLightened
    6367       78876 :                             rdayil[iLum_Illum] = dl->DaylIllum(IL) - wdayil[iLum_Illum][iWinCover_Bare] + wdayil[iLum_Illum][iWinCover_Shaded];
    6368       78876 :                             rdayil[iLum_Back] = refPt.lums[iLum_Back] - wdayil[iLum_Back][iWinCover_Bare] + wdayil[iLum_Back][iWinCover_Shaded];
    6369             :                         } else {
    6370             :                             // switchable glazings already in partially switched state when calc the RDAYIL(IL) & RBACLU(IL)
    6371           0 :                             auto &tmpDayl = tmpDaylFromWinAtRefPt(loop, IL);
    6372           0 :                             rdayil[iLum_Illum] = dl->DaylIllum(IL) - wdayil[iLum_Illum][iWinCover_Shaded] + tmpDayl[iLum_Illum][iWinCover_Shaded];
    6373           0 :                             rdayil[iLum_Back] = refPt.lums[iLum_Back] - wdayil[iLum_Back][iWinCover_Shaded] + tmpDayl[iLum_Back][iWinCover_Shaded];
    6374             :                         }
    6375             :                     } // for (IL)
    6376             : 
    6377       74960 :                     if (state.dataSurface->SurfWinShadingFlag(IWin) == WinShadingType::GlassConditionallyLightened)
    6378       71044 :                         state.dataSurface->SurfWinShadingFlag(IWin) = WinShadingType::SwitchableGlazing;
    6379        3916 :                     else if (state.dataSurface->SurfWinShadingFlag(IWin) == WinShadingType::IntShadeConditionallyOff)
    6380           0 :                         state.dataSurface->SurfWinShadingFlag(IWin) = WinShadingType::IntShade;
    6381        3916 :                     else if (state.dataSurface->SurfWinShadingFlag(IWin) == WinShadingType::ExtShadeConditionallyOff)
    6382        3916 :                         state.dataSurface->SurfWinShadingFlag(IWin) = WinShadingType::ExtShade;
    6383           0 :                     else if (state.dataSurface->SurfWinShadingFlag(IWin) == WinShadingType::IntBlindConditionallyOff)
    6384           0 :                         state.dataSurface->SurfWinShadingFlag(IWin) = WinShadingType::IntBlind;
    6385           0 :                     else if (state.dataSurface->SurfWinShadingFlag(IWin) == WinShadingType::ExtBlindConditionallyOff)
    6386           0 :                         state.dataSurface->SurfWinShadingFlag(IWin) = WinShadingType::ExtBlind;
    6387           0 :                     else if (state.dataSurface->SurfWinShadingFlag(IWin) == WinShadingType::BGShadeConditionallyOff)
    6388           0 :                         state.dataSurface->SurfWinShadingFlag(IWin) = WinShadingType::BGShade;
    6389           0 :                     else if (state.dataSurface->SurfWinShadingFlag(IWin) == WinShadingType::BGBlindConditionallyOff)
    6390           0 :                         state.dataSurface->SurfWinShadingFlag(IWin) = WinShadingType::BGBlind;
    6391             : 
    6392             :                     // For switchable glazings, it is switched to fully dark state,
    6393             :                     // update ZoneDaylight(ZoneNum)%SourceLumFromWinAtRefPt(IL,2,loop) for use in DayltgGlare
    6394       74960 :                     if (state.dataSurface->SurfWinShadingFlag(IWin) == WinShadingType::SwitchableGlazing) {
    6395      142088 :                         for (int IL = 1; IL <= NREFPT; ++IL) {
    6396       71044 :                             auto &daylFromWinAtRefPt = thisDayltgCtrl.refPts(IL).extWins(loop).lums;
    6397       71044 :                             auto const &tmpDayl = tmpDaylFromWinAtRefPt(IL, loop);
    6398             : 
    6399       71044 :                             daylFromWinAtRefPt[iLum_Source][iWinCover_Shaded] = tmpDayl[iLum_Source][iWinCover_Shaded];
    6400       71044 :                             daylFromWinAtRefPt[iLum_Illum][iWinCover_Shaded] = tmpDayl[iLum_Illum][iWinCover_Shaded];
    6401       71044 :                             daylFromWinAtRefPt[iLum_Back][iWinCover_Shaded] = tmpDayl[iLum_Back][iWinCover_Shaded];
    6402             :                         }
    6403             : 
    6404       71044 :                         int const IConst = state.dataSurface->SurfActiveConstruction(IWin);
    6405             :                         // Vis trans at normal incidence of unswitched glass
    6406       71044 :                         shadeGroupLums.unswitchedTvis =
    6407       71044 :                             General::POLYF(1.0, state.dataConstruction->Construct(IConst).TransVisBeamCoef) * surfWin.glazedFrac;
    6408             : 
    6409             :                         // Vis trans at normal incidence of fully switched glass
    6410       71044 :                         int const IConstShaded = state.dataSurface->Surface(IWin).activeShadedConstruction;
    6411       71044 :                         shadeGroupLums.switchedTvis =
    6412       71044 :                             General::POLYF(1.0, state.dataConstruction->Construct(IConstShaded).TransVisBeamCoef) * surfWin.glazedFrac;
    6413             :                     } // if (switchableGlazing)
    6414             :                 }     // if (GlareControlIsActive)
    6415       57952 :             }         // for (IWin)
    6416       57952 :             if (continueOuterLoop) continue;
    6417             : 
    6418       57952 :             if (atLeastOneGlareControlIsActive) {
    6419             : 
    6420             :                 // Re-calc daylight and glare at shaded state. For switchable glazings, it is the fully dark state.
    6421      119820 :                 for (int IL = 1; IL <= NREFPT; ++IL) {
    6422       61868 :                     BACL = max(SetPnt(IL) * dl->enclDaylight(enclNum).aveVisDiffReflect / Constant::Pi, shadeGroupLums.RDAYIL(IL)[iLum_Back]);
    6423             :                     // DayltgGlare uses ZoneDaylight(ZoneNum)%SourceLumFromWinAtRefPt(IL,2,loop) for shaded state
    6424       61868 :                     GLRNEW(IL) = DayltgGlare(state, IL, BACL, daylightCtrlNum);
    6425             :                 }
    6426             : 
    6427             :                 // Check if the shading did not improve the glare conditions
    6428             :                 //
    6429             :                 // blnCycle when true resets the specific window to its non-shaded condition. A later comment says
    6430             :                 //      Shading this window has not improved the glare situation.
    6431             :                 //      Reset shading flag to no shading condition, go to next window.
    6432             :                 //
    6433             :                 // If the original glare was too high at all reference points and the new glare is lower at all reference points it is good, don't
    6434             :                 // reset it. For each reference point, if the original glare was too high but ok at other reference points and the glare gets
    6435             :                 // lower at the reference and stays ok at the other reference points it is good, don't reset it.
    6436             :                 //
    6437             :                 // The old comments when there were only two reference points were:
    6438             :                 //     One ref pt;  go to next window if glare has increased.
    6439             :                 //     Two ref pts.  There are three cases depending on glare values.
    6440             :                 //         (1) Initial glare too high at both ref pts.  Deploy shading on
    6441             :                 //             this window if this decreases glare at both ref pts.
    6442             :                 //         (2) Initial glare too high only at first ref pt.  Deploy shading
    6443             :                 //             on this window if glare at first ref pt decreases and
    6444             :                 //             glare at second ref pt stays below max.
    6445             :                 //         (3) Initial glare too high at second ref pt.  Deploy shading if glare
    6446             :                 //             at second ref pt decreases and glare at first ref pt stays below max.
    6447             :                 //
    6448             :                 // The approach taken is just to count the number of reference points that fulfill the individual requirements and see if it
    6449             :                 // covers all the reference points.
    6450       57952 :                 int numRefPtOldAboveMaxNewBelowOld = 0;
    6451       57952 :                 int numRefPtOldBelowMaxNewBelowMax = 0;
    6452      119820 :                 for (int IL = 1; IL <= NREFPT; ++IL) {
    6453       61868 :                     auto const &refPt = thisDayltgCtrl.refPts(IL);
    6454             : 
    6455       61868 :                     if (refPt.glareIndex > thisDayltgCtrl.MaxGlareallowed && GLRNEW(IL) <= refPt.glareIndex)
    6456       57852 :                         ++numRefPtOldAboveMaxNewBelowOld;
    6457        4016 :                     else if (refPt.glareIndex <= thisDayltgCtrl.MaxGlareallowed && GLRNEW(IL) <= thisDayltgCtrl.MaxGlareallowed)
    6458        3916 :                         ++numRefPtOldBelowMaxNewBelowMax;
    6459             :                 }
    6460       57952 :                 blnCycle = true;
    6461       57952 :                 if ((numRefPtOldAboveMaxNewBelowOld + numRefPtOldBelowMaxNewBelowMax) == NREFPT) blnCycle = false;
    6462             :             }
    6463             : 
    6464             :             // restore the count to the value prior to the last loop through the group of exterior windows
    6465       57952 :             count = countBeforeListOfExtWinLoop;
    6466       57952 :             breakOuterLoop = false;
    6467             : 
    6468      117668 :             for (const int IWin : listOfExtWin) {
    6469       73540 :                 ++count;
    6470             :                 // need to map back to the original order of the "loop" to not change all the other data structures
    6471       73540 :                 int loop = thisDayltgCtrl.MapShdOrdToLoopNum(count);
    6472       73540 :                 if (loop == 0) continue;
    6473             : 
    6474             :                 // if (SurfWinShadingFlag(IWin) <= BGBlind && SurfWinShadingFlag(IWin) != SwitchableGlazing) {
    6475      143164 :                 if (NOT_SHADED(state.dataSurface->SurfWinShadingFlag(IWin)) || ANY_SHADE_SCREEN(state.dataSurface->SurfWinShadingFlag(IWin)) ||
    6476       69624 :                     ANY_BLIND(state.dataSurface->SurfWinShadingFlag(IWin)))
    6477        3916 :                     continue;
    6478             : 
    6479       69624 :                 ICtrl = state.dataSurface->Surface(IWin).activeWindowShadingControl;
    6480       69624 :                 if (!state.dataSurface->Surface(IWin).HasShadeControl) continue;
    6481       69624 :                 if (state.dataSurface->WindowShadingControl(ICtrl).GlareControlIsActive) {
    6482             : 
    6483             :                     // Shading this window has not improved the glare situation.
    6484             :                     // Reset shading flag to no shading condition, go to next window.
    6485       69624 :                     if (blnCycle) {
    6486             :                         //  for switchable glazings, reset properties to clear state or partial switched state?
    6487           0 :                         if (state.dataSurface->SurfWinShadingFlag(IWin) == WinShadingType::SwitchableGlazing) {
    6488           0 :                             state.dataSurface->SurfWinSwitchingFactor(IWin) = 0.0;
    6489           0 :                             state.dataSurface->SurfWinVisTransSelected(IWin) = shadeGroupLums.unswitchedTvis;
    6490             : 
    6491             :                             // RESET properties for fully dark state
    6492           0 :                             for (int IL = 1; IL <= NREFPT; ++IL) {
    6493           0 :                                 auto &daylFromWinAtRefPt = thisDayltgCtrl.refPts(IL).extWins(loop).lums;
    6494           0 :                                 auto const &tmpDayl = tmpDaylFromWinAtRefPt(IL, loop);
    6495           0 :                                 daylFromWinAtRefPt[iLum_Illum][iWinCover_Shaded] = tmpDayl[iLum_Illum][iWinCover_Shaded];
    6496           0 :                                 daylFromWinAtRefPt[iLum_Source][iWinCover_Shaded] = tmpDayl[iLum_Source][iWinCover_Shaded];
    6497           0 :                                 daylFromWinAtRefPt[iLum_Back][iWinCover_Shaded] = tmpDayl[iLum_Back][iWinCover_Shaded];
    6498             :                             }
    6499             :                         }
    6500             : 
    6501           0 :                         state.dataSurface->SurfWinShadingFlag(IWin) = WinShadingType::ShadeOff;
    6502           0 :                         continue;
    6503           0 :                     }
    6504             : 
    6505             :                     // Shading this window has improved the glare situation.
    6506             :                     // Reset background luminance, glare index, and daylight illuminance at each ref pt.
    6507             :                     // For switchable glazings, this is fully switched, dark state
    6508      139248 :                     for (int IL = 1; IL <= NREFPT; ++IL) {
    6509       69624 :                         auto &refPt = thisDayltgCtrl.refPts(IL);
    6510       69624 :                         refPt.lums[iLum_Back] = shadeGroupLums.RDAYIL(IL)[iLum_Back];
    6511       69624 :                         refPt.glareIndex = GLRNEW(IL);
    6512       69624 :                         dl->DaylIllum(IL) = shadeGroupLums.RDAYIL(IL)[iLum_Illum];
    6513             :                     }
    6514             : 
    6515             :                     // TH comments (5/22/2009): seems for EC windows, if the calculated glare exceeds the max setpoint,
    6516             :                     //  the EC windows will be reset to fully dark state which significantly reduces the available daylight.
    6517             :                     //  A better way is to dim the EC windows as necessary just to meet the glare index, which will still
    6518             :                     //  provide more daylight while not exceeding the max glare! The question is then how to set the
    6519             :                     //  SwitchingFactor to just meet the glare index.
    6520             :                     //  This was addressed in CR 7984 for E+ 5.0. 1/19/2010
    6521             : 
    6522             :                     // If switchable glazing, set switching factor to 1: fully switched.
    6523       69624 :                     if (state.dataSurface->SurfWinShadingFlag(IWin) == WinShadingType::SwitchableGlazing) {
    6524             :                         // tmpSWFactor0 = SurfaceWindow( IWin ).SwitchingFactor; // save original
    6525             :                         // switching  factor
    6526             :                         ////Unused Set but never used
    6527       69624 :                         state.dataSurface->SurfWinSwitchingFactor(IWin) = 1.0;
    6528       69624 :                         state.dataSurface->SurfWinVisTransSelected(IWin) = shadeGroupLums.switchedTvis;
    6529             : 
    6530             :                         // restore fully dark values
    6531      139248 :                         for (int IL = 1; IL <= NREFPT; ++IL) {
    6532       69624 :                             auto &daylFromWinAtRefPt = thisDayltgCtrl.refPts(IL).extWins(loop).lums;
    6533       69624 :                             auto const &tmpDayl = tmpDaylFromWinAtRefPt(IL, loop);
    6534       69624 :                             auto &wdayil = shadeGroupLums.WDAYIL(IL);
    6535       69624 :                             wdayil[iLum_Illum][iWinCover_Shaded] = tmpDayl[iLum_Illum][iWinCover_Shaded];
    6536       69624 :                             wdayil[iLum_Back][iWinCover_Shaded] = tmpDayl[iLum_Back][iWinCover_Shaded];
    6537       69624 :                             daylFromWinAtRefPt[iLum_Illum][iWinCover_Shaded] = tmpDayl[iLum_Illum][iWinCover_Shaded];
    6538       69624 :                             daylFromWinAtRefPt[iLum_Back][iWinCover_Shaded] = tmpDayl[iLum_Back][iWinCover_Shaded];
    6539       69624 :                             daylFromWinAtRefPt[iLum_Source][iWinCover_Shaded] = tmpDayl[iLum_Source][iWinCover_Shaded];
    6540             :                         }
    6541             :                     }
    6542             : 
    6543             :                     // Check if glare now acceptable at each ref pt.
    6544       69624 :                     GlareOK = false;
    6545       69624 :                     if (NREFPT == 1) {
    6546       69624 :                         if (thisDayltgCtrl.refPts(1).glareIndex <= thisDayltgCtrl.MaxGlareallowed) GlareOK = true;
    6547           0 :                     } else if (NREFPT > 1) {
    6548           0 :                         if (thisDayltgCtrl.refPts(1).glareIndex <= thisDayltgCtrl.MaxGlareallowed &&
    6549           0 :                             thisDayltgCtrl.refPts(2).glareIndex <= thisDayltgCtrl.MaxGlareallowed)
    6550           0 :                             GlareOK = true;
    6551             :                     }
    6552             : 
    6553       69624 :                     if (GlareOK) {
    6554       27648 :                         if (state.dataSurface->SurfWinShadingFlag(IWin) == WinShadingType::SwitchableGlazing &&
    6555       13824 :                             state.dataSurface->WindowShadingControl(ICtrl).shadingControlType == WindowShadingControlType::MeetDaylIlumSetp) {
    6556             :                             // Added TH 1/14/2010
    6557             :                             // Only for switchable glazings with MeetDaylightIlluminanceSetpoint control
    6558             :                             // The glazing is in fully dark state, it might lighten a bit to provide more daylight
    6559             :                             //  while meeting maximum discomfort glare index
    6560             :                             // Iteration to find the right switching factor meeting the glare index
    6561             : 
    6562             :                             // get fully dark state values
    6563           0 :                             Real64 tmpSWSL1 = tmpDaylFromWinAtRefPt(1, loop)[iLum_Source][iWinCover_Shaded];
    6564           0 :                             Real64 tmpSWSL2 = (NREFPT > 1) ? tmpDaylFromWinAtRefPt(2, loop)[iLum_Source][iWinCover_Shaded] : 0.0;
    6565             : 
    6566             :                             // use simple fixed step search in iteraction, can be improved in future
    6567           0 :                             Real64 tmpSWFactor = 1.0 - tmpSWIterStep;
    6568           0 :                             while (tmpSWFactor > 0) {
    6569             :                                 // calc new glare at new switching state
    6570           0 :                                 for (int IL = 1; IL <= NREFPT; ++IL) {
    6571           0 :                                     auto &rdayil = shadeGroupLums.RDAYIL(IL);
    6572           0 :                                     auto const &wdayil = shadeGroupLums.WDAYIL(IL);
    6573           0 :                                     auto &refPt = thisDayltgCtrl.refPts(IL);
    6574           0 :                                     rdayil[iLum_Illum] =
    6575           0 :                                         dl->DaylIllum(IL) +
    6576           0 :                                         (wdayil[iLum_Illum][iWinCover_Bare] - wdayil[iLum_Illum][iWinCover_Shaded]) * (1.0 - tmpSWFactor);
    6577           0 :                                     rdayil[iLum_Back] =
    6578           0 :                                         refPt.lums[iLum_Back] +
    6579           0 :                                         (wdayil[iLum_Back][iWinCover_Bare] - wdayil[iLum_Back][iWinCover_Shaded]) * (1.0 - tmpSWFactor);
    6580           0 :                                     BACL = max(SetPnt(IL) * dl->enclDaylight(enclNum).aveVisDiffReflect / Constant::Pi, rdayil[iLum_Back]);
    6581             :                                     // needs to update SourceLumFromWinAtRefPt(IL,2,loop) before re-calc DayltgGlare
    6582           0 :                                     tmpMult = (shadeGroupLums.unswitchedTvis -
    6583           0 :                                                (shadeGroupLums.unswitchedTvis - shadeGroupLums.switchedTvis) * tmpSWFactor) /
    6584           0 :                                               shadeGroupLums.switchedTvis;
    6585           0 :                                     refPt.extWins(loop).lums[iLum_Source][iWinCover_Shaded] = ((IL == 1) ? tmpSWSL1 : tmpSWSL2) * tmpMult;
    6586             :                                     // Calc new glare
    6587           0 :                                     GLRNEW(IL) = DayltgGlare(state, IL, BACL, daylightCtrlNum);
    6588             :                                 } // for (IL)
    6589             : 
    6590             :                                 // Check whether new glare is OK
    6591           0 :                                 GlareOK = false;
    6592           0 :                                 if (NREFPT == 1) {
    6593           0 :                                     if (GLRNEW(1) <= thisDayltgCtrl.MaxGlareallowed) GlareOK = true;
    6594           0 :                                 } else if (NREFPT > 1) {
    6595           0 :                                     if (GLRNEW(1) <= thisDayltgCtrl.MaxGlareallowed && GLRNEW(2) <= thisDayltgCtrl.MaxGlareallowed) GlareOK = true;
    6596             :                                 }
    6597             : 
    6598           0 :                                 if (GlareOK) {
    6599           0 :                                     if (tmpSWFactor >= tmpSWIterStep) {
    6600             :                                         // Continue to lighten the glazing
    6601           0 :                                         tmpSWFactor -= tmpSWIterStep;
    6602           0 :                                         continue;
    6603             :                                     } else {
    6604             :                                         // Glare still OK but glazing already in clear state, no more lighten
    6605           0 :                                         breakOuterLoop = true;
    6606           0 :                                         break;
    6607             :                                     }
    6608             :                                 } else {
    6609             :                                     // Glare too high, exit and use previous switching state
    6610           0 :                                     tmpSWFactor += tmpSWIterStep;
    6611           0 :                                     breakOuterLoop = true;
    6612           0 :                                     break;
    6613             :                                 }
    6614             :                             } // if (tmpSWFactor > 0)
    6615             : 
    6616             :                             // Final re-calculation if needed
    6617           0 :                             if (!GlareOK) {
    6618             :                                 // Glare too high, use previous state and re-calc
    6619           0 :                                 for (int IL = 1; IL <= NREFPT; ++IL) {
    6620           0 :                                     auto &rdayil = shadeGroupLums.RDAYIL(IL);
    6621           0 :                                     auto const &wdayil = shadeGroupLums.WDAYIL(IL);
    6622           0 :                                     rdayil[iLum_Illum] =
    6623           0 :                                         dl->DaylIllum(IL) +
    6624           0 :                                         (wdayil[iLum_Illum][iWinCover_Bare] - wdayil[iLum_Illum][iWinCover_Shaded]) * (1.0 - tmpSWFactor);
    6625           0 :                                     rdayil[iLum_Back] =
    6626           0 :                                         thisDayltgCtrl.refPts(IL).lums[iLum_Back] +
    6627           0 :                                         (wdayil[iLum_Back][iWinCover_Bare] - wdayil[iLum_Back][iWinCover_Shaded]) * (1.0 - tmpSWFactor);
    6628           0 :                                     BACL = max(SetPnt(IL) * dl->enclDaylight(enclNum).aveVisDiffReflect / Constant::Pi, rdayil[iLum_Back]);
    6629             : 
    6630             :                                     // needs to update SourceLumFromWinAtRefPt(IL,2,IWin) before re-calc DayltgGlare
    6631           0 :                                     tmpMult = (shadeGroupLums.unswitchedTvis -
    6632           0 :                                                (shadeGroupLums.unswitchedTvis - shadeGroupLums.switchedTvis) * tmpSWFactor) /
    6633           0 :                                               shadeGroupLums.switchedTvis;
    6634           0 :                                     thisDayltgCtrl.refPts(1).extWins(loop).lums[iLum_Source][iWinCover_Shaded] =
    6635           0 :                                         ((IL == 1) ? tmpSWSL1 : tmpSWSL2) * tmpMult;
    6636           0 :                                     GLRNEW(IL) = DayltgGlare(state, IL, BACL, daylightCtrlNum);
    6637             :                                 }
    6638             :                             }
    6639             : 
    6640             :                             // Update final results
    6641           0 :                             for (int IL = 1; IL <= NREFPT; ++IL) {
    6642           0 :                                 auto &refPt = thisDayltgCtrl.refPts(IL);
    6643           0 :                                 auto const &rdayil = shadeGroupLums.RDAYIL(IL);
    6644           0 :                                 refPt.lums[iLum_Back] = rdayil[iLum_Back];
    6645           0 :                                 refPt.glareIndex = GLRNEW(IL);
    6646           0 :                                 dl->DaylIllum(IL) = rdayil[iLum_Illum];
    6647             : 
    6648           0 :                                 tmpMult =
    6649           0 :                                     (shadeGroupLums.unswitchedTvis - (shadeGroupLums.unswitchedTvis - shadeGroupLums.switchedTvis) * tmpSWFactor) /
    6650           0 :                                     shadeGroupLums.switchedTvis;
    6651             :                                 // update report variables
    6652           0 :                                 auto &daylFromWinAtRefPt = refPt.extWins(loop).lums;
    6653           0 :                                 auto const &tmpDayl = tmpDaylFromWinAtRefPt(IL, loop);
    6654           0 :                                 daylFromWinAtRefPt[iLum_Illum][iWinCover_Shaded] = tmpDayl[iLum_Illum][iWinCover_Shaded] * tmpMult;
    6655           0 :                                 daylFromWinAtRefPt[iLum_Back][iWinCover_Shaded] = tmpDayl[iLum_Back][iWinCover_Shaded] * tmpMult;
    6656             :                             }
    6657           0 :                             state.dataSurface->SurfWinSwitchingFactor(IWin) = tmpSWFactor;
    6658           0 :                             state.dataSurface->SurfWinVisTransSelected(IWin) =
    6659           0 :                                 shadeGroupLums.unswitchedTvis - (shadeGroupLums.unswitchedTvis - shadeGroupLums.switchedTvis) * tmpSWFactor;
    6660             : 
    6661             :                         } else {
    6662             :                             // For un-switchable glazing or switchable glazing but not MeetDaylightIlluminaceSetpoint control,
    6663             :                             // it is in shaded state and glare is ok - job is done, exit the window loop - IWin
    6664       13824 :                             breakOuterLoop = true;
    6665       13824 :                             break;
    6666             :                         }
    6667             :                     } // if (glareOK)
    6668             :                 }     // if (glareControlIsActive)
    6669       57952 :             }         // for (IWin)
    6670       57952 :             if (breakOuterLoop) break;
    6671             :         } // for (igroup)
    6672             :     }     // if (GlareFlag)
    6673             : 
    6674             :     // Loop again over windows and reset remaining shading flags that
    6675             :     // are 10 or higher (i.e., conditionally off) to off
    6676     1444942 :     for (int spaceNum : state.dataHeatBal->Zone(thisDayltgCtrl.zoneIndex).spaceIndexes) {
    6677      724508 :         auto const &thisSpace = state.dataHeatBal->space(spaceNum);
    6678     2151855 :         for (int IWin = thisSpace.WindowSurfaceFirst; IWin <= thisSpace.WindowSurfaceLast; ++IWin) {
    6679     1427347 :             if (state.dataSurface->Surface(IWin).ExtBoundCond != ExternalEnvironment) continue;
    6680     1426332 :             bool anyGlareControl = (state.dataSurface->SurfWinShadingFlag(IWin) == WinShadingType::IntShadeConditionallyOff) ||
    6681     1426332 :                                    (state.dataSurface->SurfWinShadingFlag(IWin) == WinShadingType::GlassConditionallyLightened) ||
    6682     1412611 :                                    (state.dataSurface->SurfWinShadingFlag(IWin) == WinShadingType::ExtShadeConditionallyOff) ||
    6683     4237393 :                                    (state.dataSurface->SurfWinShadingFlag(IWin) == WinShadingType::IntBlindConditionallyOff) ||
    6684     1384729 :                                    (state.dataSurface->SurfWinShadingFlag(IWin) == WinShadingType::ExtBlindConditionallyOff);
    6685     1426332 :             if (anyGlareControl) {
    6686       41603 :                 state.dataSurface->SurfWinShadingFlag(IWin) = WinShadingType::ShadeOff;
    6687             :             }
    6688             :         }
    6689      720434 :     }
    6690             : 
    6691             :     // Variables for reporting
    6692     1677232 :     for (int IL = 1; IL <= NREFPT; ++IL) {
    6693      956798 :         auto &refPt = thisDayltgCtrl.refPts(IL);
    6694      956798 :         refPt.lums[iLum_Illum] = dl->DaylIllum(IL);
    6695             : 
    6696             :         // added TH 12/2/2008
    6697      956798 :         refPt.timeExceedingGlareIndexSetPoint = (refPt.glareIndex > thisDayltgCtrl.MaxGlareallowed) ? state.dataGlobal->TimeStepZone : 0.0;
    6698             :         // added TH 7/6/2009
    6699      956798 :         refPt.timeExceedingDaylightIlluminanceSetPoint = (dl->DaylIllum(IL) > refPt.illumSetPoint) ? state.dataGlobal->TimeStepZone : 0.0;
    6700             :     }
    6701      728582 : } // DayltgInteriorIllum()
    6702             : 
    6703        1015 : void DayltgInteriorTDDIllum(EnergyPlusData &state)
    6704             : {
    6705             : 
    6706             :     // SUBROUTINE INFORMATION:
    6707             :     //       AUTHOR         Linda Lawrie
    6708             :     //       DATE WRITTEN   October 2006
    6709             : 
    6710             :     // PURPOSE OF THIS SUBROUTINE:
    6711             :     // Calculate the TDD Pipe illuminance values
    6712        1015 :     auto &dl = state.dataDayltg;
    6713             : 
    6714             :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    6715             :     int iSky1; // Sky type index values for averaging two sky types
    6716             :     int iSky2;
    6717             :     Real64 SkyWeight; // Weighting factor used to average two different sky types
    6718             : 
    6719        1015 :     if (state.dataEnvrn->SkyClearness > 3.0) { // Sky is average of clear and clear turbid
    6720         518 :         SkyWeight = min(1.0, (state.dataEnvrn->SkyClearness - 3.0) / 3.0);
    6721         518 :         iSky1 = (int)SkyType::Clear;
    6722         518 :         iSky2 = (int)SkyType::ClearTurbid;
    6723         497 :     } else if (state.dataEnvrn->SkyClearness > 1.2) { // Sky is average of clear turbid and intermediate
    6724          91 :         SkyWeight = (state.dataEnvrn->SkyClearness - 1.2) / 1.8;
    6725          91 :         iSky1 = (int)SkyType::ClearTurbid;
    6726          91 :         iSky2 = (int)SkyType::Intermediate;
    6727             :     } else { // Sky is average of intermediate and overcast
    6728         406 :         SkyWeight = min(1.0, max(0.0, (state.dataEnvrn->SkyClearness - 1.0) / 0.2, (state.dataEnvrn->SkyBrightness - 0.05) / 0.4));
    6729         406 :         iSky1 = (int)SkyType::Intermediate;
    6730         406 :         iSky2 = (int)SkyType::Overcast;
    6731             :     }
    6732             : 
    6733             :     // Calculate and report TDD visible transmittances
    6734        3045 :     for (int PipeNum = 1; PipeNum <= (int)state.dataDaylightingDevicesData->TDDPipe.size(); ++PipeNum) {
    6735             : 
    6736        2030 :         state.dataDaylightingDevicesData->TDDPipe(PipeNum).TransVisBeam =
    6737        2030 :             state.dataGlobal->WeightNow * dl->TDDTransVisBeam(state.dataGlobal->HourOfDay, PipeNum) +
    6738        2030 :             state.dataGlobal->WeightPreviousHour * dl->TDDTransVisBeam(state.dataGlobal->PreviousHour, PipeNum);
    6739             : 
    6740        2030 :         auto const &tddFluxIncCurr = dl->TDDFluxInc(state.dataGlobal->HourOfDay, PipeNum);
    6741        2030 :         auto const &tddFluxIncPrev = dl->TDDFluxInc(state.dataGlobal->PreviousHour, PipeNum);
    6742             : 
    6743        2030 :         auto const &tddFluxTransCurr = dl->TDDFluxTrans(state.dataGlobal->HourOfDay, PipeNum);
    6744        2030 :         auto const &tddFluxTransPrev = dl->TDDFluxTrans(state.dataGlobal->PreviousHour, PipeNum);
    6745             : 
    6746        2030 :         Illums TDDTransVisDiff;
    6747        6090 :         for (int iSky = iSky1; iSky <= iSky2; ++iSky) {
    6748        4060 :             Real64 tddTransVisDiffCurr = (tddFluxIncCurr.sky[iSky] > 0.0) ? (tddFluxTransCurr.sky[iSky] / tddFluxIncCurr.sky[iSky]) : 0.0;
    6749        4060 :             Real64 tddTransVisDiffPrev = (tddFluxIncPrev.sky[iSky] > 0.0) ? (tddFluxTransPrev.sky[iSky] / tddFluxIncPrev.sky[iSky]) : 0.0;
    6750             : 
    6751        4060 :             TDDTransVisDiff.sky[iSky] =
    6752        4060 :                 state.dataGlobal->WeightNow * tddTransVisDiffCurr + state.dataGlobal->WeightPreviousHour * tddTransVisDiffPrev;
    6753             :         } // for (iSky)
    6754             : 
    6755        2030 :         state.dataDaylightingDevicesData->TDDPipe(PipeNum).TransVisDiff =
    6756        2030 :             SkyWeight * TDDTransVisDiff.sky[iSky1] + (1.0 - SkyWeight) * TDDTransVisDiff.sky[iSky2];
    6757             :     } // for (PipeNum)
    6758        1015 : } // DayltgInteriorTDDIllum()
    6759             : 
    6760      916463 : void DayltgElecLightingControl(EnergyPlusData &state)
    6761             : {
    6762             : 
    6763             :     // SUBROUTINE INFORMATION:
    6764             :     //       AUTHOR         Fred Winkelmann
    6765             :     //       DATE WRITTEN   July 1997
    6766             :     //       MODIFIED       Mar 2004, FCW: add inter-reflected illuminance from interior windows to DaylIllum
    6767             :     //                      Apr 2004, FCW: move CALL ReportIllumMap from DayltgInteriorIllum2 (DayltgInteriorMapIllum)
    6768             :     //                      Apr 2010, BG NREL: remove inter-reflected illuminance to stop double counting
    6769             :     //                      Aug 2012, BG NREL: added availability schedule logic
    6770             : 
    6771             :     // PURPOSE OF THIS SUBROUTINE:
    6772             :     // For a daylit space, determines lighting power reduction factor due to
    6773             :     // daylighting for different lighting control systems.
    6774             : 
    6775             :     // Called by InitSurfaceHeatBalance.
    6776             : 
    6777             :     // REFERENCES:
    6778             :     // Based on DOE-2.1E subroutine DLTSYS.
    6779      916463 :     auto &dl = state.dataDayltg;
    6780             : 
    6781      916463 :     if (dl->daylightControl.empty()) {
    6782      817243 :         return;
    6783             :     }
    6784             :     // Reset space power reduction factors
    6785      953130 :     for (int spaceNum = 1; spaceNum <= state.dataGlobal->numSpaces; ++spaceNum) {
    6786      853910 :         dl->spacePowerReductionFactor(spaceNum) = 1.0;
    6787             :     }
    6788             : 
    6789      529341 :     for (auto &thisDayltgCtrl : dl->daylightControl) {
    6790             : 
    6791      430121 :         if (thisDayltgCtrl.DaylightMethod != DaylightingMethod::SplitFlux) {
    6792             :             // Set space power reduction factors
    6793        2037 :             if (thisDayltgCtrl.PowerReductionFactor < 1.0) {
    6794        2016 :                 if (thisDayltgCtrl.spaceIndex > 0) {
    6795             :                     // This is a space-level daylighting control
    6796           0 :                     dl->spacePowerReductionFactor(thisDayltgCtrl.spaceIndex) = thisDayltgCtrl.PowerReductionFactor;
    6797             :                 } else {
    6798             :                     // This is a zone-level daylighting control
    6799        4032 :                     for (int spaceNum : state.dataHeatBal->Zone(thisDayltgCtrl.zoneIndex).spaceIndexes) {
    6800        2016 :                         dl->spacePowerReductionFactor(spaceNum) = thisDayltgCtrl.PowerReductionFactor;
    6801        2016 :                     }
    6802             :                 }
    6803             :             }
    6804        2037 :             continue;
    6805        2037 :         }
    6806             : 
    6807             :         // Electric lighting power reduction factor for a given daylighting control
    6808      428084 :         Real64 &TotReduction = thisDayltgCtrl.PowerReductionFactor;
    6809      428084 :         TotReduction = 0.0;
    6810      428084 :         Real64 ZFTOT = 0.0;
    6811             : 
    6812             :         // check if scheduled to be available
    6813      428084 :         if (ScheduleManager::GetCurrentScheduleValue(state, thisDayltgCtrl.AvailSchedNum) > 0.0) {
    6814             : 
    6815             :             // Loop over reference points
    6816      992623 :             for (int IL = 1; IL <= thisDayltgCtrl.TotalDaylRefPoints; ++IL) {
    6817      568669 :                 auto &refPt = thisDayltgCtrl.refPts(IL);
    6818             :                 // Total fraction of zone that is daylit
    6819      568669 :                 ZFTOT += refPt.fracZoneDaylit;
    6820             : 
    6821      568669 :                 dl->DaylIllum(IL) = refPt.lums[iLum_Illum];
    6822      568669 :                 Real64 FL = 0.0;
    6823      568669 :                 if (dl->DaylIllum(IL) < refPt.illumSetPoint) {
    6824      143729 :                     FL = (refPt.illumSetPoint - dl->DaylIllum(IL)) / refPt.illumSetPoint;
    6825             :                 }
    6826             : 
    6827             :                 // BRANCH ON LIGHTING SYSTEM TYPE
    6828      568669 :                 LtgCtrlType LSYSTP = thisDayltgCtrl.LightControlType;
    6829      568669 :                 Real64 FP = 0.0;
    6830      568669 :                 if (LSYSTP != LtgCtrlType::Stepped) {
    6831             :                     // Continuously dimmable system with linear power curve
    6832             :                     // Fractional output power required to meet setpoint
    6833      322161 :                     FP = 1.0;
    6834             :                     // LIGHT-CTRL-TYPE = CONTINUOUS (LSYSTP = 1)
    6835      322161 :                     if (FL <= thisDayltgCtrl.MinLightFraction) {
    6836      236773 :                         FP = thisDayltgCtrl.MinPowerFraction;
    6837             :                     }
    6838             :                     // LIGHT-CTRL-TYPE = CONTINUOUS/OFF (LSYSTP = 3)
    6839      322161 :                     if (FL <= thisDayltgCtrl.MinLightFraction && LSYSTP == LtgCtrlType::ContinuousOff) {
    6840      219328 :                         FP = 0.0;
    6841             :                     }
    6842      322161 :                     if (FL > thisDayltgCtrl.MinLightFraction && FL < 1.0) {
    6843       84941 :                         FP = (FL + (1.0 - FL) * thisDayltgCtrl.MinPowerFraction - thisDayltgCtrl.MinLightFraction) /
    6844       84941 :                              (1.0 - thisDayltgCtrl.MinLightFraction);
    6845             :                     }
    6846             : 
    6847             :                 } else { // LSYSTP = 2
    6848             :                     // Stepped system
    6849      246508 :                     FP = 0.0;
    6850             :                     // #9060: Use a tolerance, otherwise at very low (< 1e-12) daylighting conditions, you can get a multiplier > 1.0
    6851      246508 :                     if (dl->DaylIllum(IL) < 0.1) {
    6852        3602 :                         FP = 1.0;
    6853      242906 :                     } else if (dl->DaylIllum(IL) < refPt.illumSetPoint) {
    6854       33315 :                         FP = double(int(thisDayltgCtrl.LightControlSteps * FL) + 1) / double(thisDayltgCtrl.LightControlSteps);
    6855             :                     }
    6856             : 
    6857      246508 :                     if (thisDayltgCtrl.LightControlProbability < 1.0) {
    6858             :                         // Manual operation.  Occupant sets lights one level too high a fraction of the time equal to
    6859             :                         // 1. - ZoneDaylight(ZoneNum)%LightControlProbability.  RANDOM_NUMBER returns a random number
    6860             :                         // between 0 and 1.
    6861             :                         Real64 XRAN;
    6862           0 :                         RANDOM_NUMBER(XRAN);
    6863           0 :                         if (XRAN >= thisDayltgCtrl.LightControlProbability) {
    6864             :                             // Set level one higher
    6865           0 :                             if (FP < 1.0) {
    6866           0 :                                 FP += (1.0 / double(thisDayltgCtrl.LightControlSteps));
    6867             :                             }
    6868             :                         } // XRAN
    6869             :                     }     // Light Control Probability < 1
    6870             :                 }         // Lighting System Type
    6871             : 
    6872      568669 :                 refPt.powerReductionFactor = FP;
    6873             : 
    6874             :                 // Accumulate net ltg power reduction factor for entire zone
    6875      568669 :                 TotReduction += refPt.powerReductionFactor * refPt.fracZoneDaylit;
    6876             : 
    6877             :             } // End of loop over reference points, IL
    6878             : 
    6879             :             // Correct for fraction of zone (1-ZFTOT) not controlled by
    6880             :             // the reference points.  For this fraction (which is usually zero),
    6881             :             // the electric lighting is unaffected and the power reduction
    6882             :             // factor is therefore 1.0.
    6883      423954 :             TotReduction += (1.0 - ZFTOT);
    6884             :         } else { // controls not currently available
    6885        4130 :             TotReduction = 1.0;
    6886             :         }
    6887             : 
    6888             :         // Set space power reduction factors
    6889      428084 :         if (thisDayltgCtrl.spaceIndex > 0) {
    6890             :             // This is a space-level daylighting control
    6891        1239 :             dl->spacePowerReductionFactor(thisDayltgCtrl.spaceIndex) = TotReduction;
    6892             :         } else {
    6893             :             // This is a zone-level daylighting control
    6894      853690 :             for (int spaceNum : state.dataHeatBal->Zone(thisDayltgCtrl.zoneIndex).spaceIndexes) {
    6895      426845 :                 dl->spacePowerReductionFactor(spaceNum) = TotReduction;
    6896      426845 :             }
    6897             :         }
    6898             :     } // end daylighting control loop
    6899             : 
    6900             :     //  IF(TotIllumMaps > 0 .and. .not. DoingSizing .and. .not. WarmupFlag .and. .not. KickoffSimulation) THEN
    6901       99220 :     if ((int)dl->illumMaps.size() > 0 && !state.dataGlobal->DoingSizing && !state.dataGlobal->WarmupFlag) {
    6902        3019 :         for (int mapNum = 1; mapNum <= (int)dl->illumMaps.size(); ++mapNum) {
    6903        1627 :             auto &illumMap = dl->illumMaps(mapNum);
    6904        1627 :             if (state.dataGlobal->TimeStep == 1) dl->mapResultsToReport = false;
    6905      143752 :             for (auto &refPt : illumMap.refPts) {
    6906      142125 :                 refPt.lumsHr[iLum_Illum] += refPt.lums[iLum_Illum] / double(state.dataGlobal->NumOfTimeStepInHour);
    6907      142125 :                 if (refPt.lumsHr[iLum_Illum] > 0.0) {
    6908      142125 :                     dl->mapResultsToReport = true;
    6909      142125 :                     dl->mapResultsReported = true;
    6910             :                 }
    6911             :             }
    6912        1627 :             ReportIllumMap(state, mapNum);
    6913        1627 :             if (state.dataGlobal->TimeStep == state.dataGlobal->NumOfTimeStepInHour) {
    6914       24357 :                 for (auto &refPt : illumMap.refPts) {
    6915       24075 :                     refPt.lumsHr[iLum_Illum] = refPt.lums[iLum_Illum] = 0.0;
    6916             :                 }
    6917             :             }
    6918             :         } // for (mapNum)
    6919             :     }     // if (MapSize > 0)
    6920             : } // DayltgElecLightingControl()
    6921             : 
    6922      534586 : Real64 DayltgGlarePositionFactor(Real64 X, // Lateral and vertical distance of luminous window element from
    6923             :                                  Real64 Y)
    6924             : {
    6925             : 
    6926             :     // SUBROUTINE INFORMATION:
    6927             :     //       AUTHOR         Fred Winkelmann
    6928             :     //       DATE WRITTEN   July 1997
    6929             : 
    6930             :     // PURPOSE OF THIS SUBROUTINE:
    6931             :     // by table interpolation, evaluates the
    6932             :     // Hopkinson position factor used in glare calculation
    6933             :     // (Hopkinson, Petherbridge, AND Longmore -- Daylighting,
    6934             :     // London, 1966, PP 307, 323).  X (Y) is the lateral
    6935             :     // (vertical) distance of luminous window element from
    6936             :     // horizontal line of vision, divided by horizontal distance
    6937             :     // from eye of observer. The array PF contains values of
    6938             :     // the position factor for X = 0, 0.5, 1.0, 1.5, 2.0, 2.5,
    6939             :     // and 3.0 and Y = 0, 0.5, 1.0, 1.5, 2.0. Called by CalcDayltgCoefficients.
    6940             : 
    6941             :     // REFERENCES:
    6942             :     // Based on DOE-2.1E subroutine DPFAC.
    6943             : 
    6944             :     // Position factor array
    6945             :     static constexpr std::array<std::array<Real64, 7>, 5> PF = {{
    6946             :         {1.00, 0.492, 0.226, 0.128, 0.081, 0.061, 0.057},
    6947             :         {0.123, 0.119, 0.065, 0.043, 0.029, 0.026, 0.023},
    6948             :         {0.019, 0.026, 0.019, 0.016, 0.014, 0.011, 0.011},
    6949             :         {0.008, 0.008, 0.008, 0.008, 0.008, 0.006, 0.006},
    6950             :         {0.0, 0.0, 0.003, 0.003, 0.003, 0.003, 0.003},
    6951             :     }};
    6952             : 
    6953      534586 :     if (X < 0.0 || X >= 3.0) return 0.0;
    6954      183827 :     if (Y < 0.0 || Y >= 2.0) return 0.0;
    6955             : 
    6956      183274 :     int IX = 1 + int(2.0 * X);
    6957      183274 :     int IY = 1 + int(2.0 * Y);
    6958      183274 :     Real64 X1 = 0.5 * double(IX - 1);
    6959      183274 :     Real64 Y1 = 0.5 * double(IY - 1);
    6960      183274 :     Real64 FA = PF[IY - 1][IX - 1] + 2.0 * (X - X1) * (PF[IY - 1][IX] - PF[IY - 1][IX - 1]);
    6961      183274 :     Real64 FB = PF[IY][IX - 1] + 2.0 * (X - X1) * (PF[IY][IX] - PF[IY][IX - 1]);
    6962      183274 :     return FA + 2.0 * (Y - Y1) * (FB - FA);
    6963             : } // DayltgGlarePositionFactor()
    6964             : 
    6965      324804 : void DayltgInterReflectedIllum(EnergyPlusData &state,
    6966             :                                int const ISunPos, // Sun position counter; used to avoid calculating various
    6967             :                                int const IHR,     // Hour of day
    6968             :                                int const enclNum, // Daylighting enclosure index
    6969             :                                int const IWin     // Window index
    6970             : )
    6971             : {
    6972             : 
    6973             :     // SUBROUTINE INFORMATION:
    6974             :     //       AUTHOR         Fred Winkelmann
    6975             :     //       DATE WRITTEN   July 1997
    6976             :     //       MODIFIED       FCW December 1998
    6977             :     //                      FCW June 2001: Add blind calculations
    6978             :     //                      FCW Jan 2001: Add blinds with movable slats
    6979             :     //                      FCW Jan 2003: Add between-glass blinds
    6980             :     //                      FCW Jul 2003: account for transmittance of shading surfaces
    6981             :     //                       (previously these were assumed opaque even if transmittance schedule
    6982             :     //                        value was non-zero)
    6983             :     //                      FCW Aug 2003: modify initialization of WinLum from WinLum = 0. TO
    6984             :     //                        WinLum(:,:,IHR) = 0. Otherwise values calculated in previous
    6985             :     //                        call are incorrectly zeroed. Result was that window luminance with
    6986             :     //                        shade or blind included only contribution from first window element
    6987             :     //                        in window element loop in CalcDayltgCoefficients, thus seriously
    6988             :     //                        undercalculating window luminance for windows with more than one
    6989             :     //                        window element. Similarly, modified initialization of WLUMSU from
    6990             :     //                        WLUMSU = 0. to WLUMSU(:,IHR) = 0., and of WLUMSUdisk from
    6991             :     //                        WLUMSUdisk = 0. to WLUMSUdisk(:,IHR) = 0.
    6992             :     //                      PGE Aug 2003: Add daylighting shelves.
    6993             :     //                      FCW Nov 2003: Add beam solar and sky solar reflected from obstructions;
    6994             :     //                                    add beam solar reflected from ground accounting for obstructions.
    6995             :     //                      FCW Nov 2003: increase NPHMAX from 9 to 10 to avoid rays with altitude angle = 0
    6996             :     //                                    for vertical surfaces.
    6997             :     //                      FCW Nov 2003: fix the expression for min and max limits of azimuth; old expression
    6998             :     //                                    broke down for window normals with negative altitude angle
    6999             :     //                      FCW Nov 2003: add specular reflection from exterior obstructions
    7000             :     //                      FCW Apr 2004: add light well efficiency multiplying window transmittance
    7001             :     //                      FCW Apr 2004: add diffusing glazing
    7002             :     //                      RAR (FSEC)  May 2006: add exterior window screen
    7003             :     //                      B. Griffith NREL April 2010: CR7869 add adjacent zone area if window is not on this zone
    7004             :     //                                    apply interior window transmission and blocking to beam transmission from ext win
    7005             : 
    7006             :     // PURPOSE OF THIS SUBROUTINE:
    7007             :     // Called from CalcDayltgCoefficients for each window and reference point in a daylit
    7008             :     // space, for each sun position. Calculates illuminance (EINTSK and EINTSU) at reference point due
    7009             :     // to internally reflected light by integrating to determine the amount of flux from
    7010             :     // sky and ground (and beam reflected from obstructions) transmitted through
    7011             :     // the center of the window and then reflecting this
    7012             :     // light from the inside surfaces of the space.  The "split-flux" method is used
    7013             :     // (Lynes, Principles of Natural Lighting, 1968).  EINT is determined for
    7014             :     // different sky types and for window with and without shades, screens or blinds.
    7015             :     // Also finds luminance (WinLum and WLUMSU) of window with shade or blind, &
    7016             :     // or with diffusing glass, for different sky types.
    7017             : 
    7018             :     // REFERENCES:
    7019             :     // Based on DOE-2.1E subroutine DREFLT.
    7020             : 
    7021             :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    7022             :     // In the following I,J arrays:
    7023             :     // I = sky type;
    7024             :     // J = 1 for bare window, 2 and above for window with shade or blind.
    7025      324804 :     Illums ZSK;                   // Sky-related and sun-related illuminance on window from sky/ground
    7026      324804 :     Vector3<Real64> U;            // Unit vector in (PH,TH) direction
    7027      324804 :     Vector3<Real64> nearestHitPt; // Hit point of ray on nearest obstruction (m)
    7028      324804 :     Vector3<Real64> obsHitPt;     // Coordinates of hit point on an obstruction (m)
    7029      324804 :     Vector3<Real64> groundHitPt;  // Coordinates of point that ray from window center hits the ground (m)
    7030             :     // std::array<Real64, Material::MaxSlatAngs+1> FLFWSU = {0.0};                     // Sun-related downgoing luminous flux, excluding entering beam
    7031             :     // std::array<Real64, Material::MaxSlatAngs+1> FLFWSUdisk = {0.0};                 // Sun-related downgoing luminous flux, due to entering beam
    7032             :     // std::array<Real64, Material::MaxSlatAngs+1> FLCWSU = {0.0};                     // Sun-related upgoing luminous flux
    7033      324804 :     std::array<Dayltg::Illums, Material::MaxSlatAngs + 1> FLCW = {Illums()}; // Sky-related upgoing luminous flux
    7034      324804 :     std::array<Dayltg::Illums, Material::MaxSlatAngs + 1> FLFW = {Illums()}; // Sky-related downgoing luminous flux
    7035             :     std::array<Real64, (int)Material::MaxSlatAngs + 1> transMult;
    7036             :     std::array<Real64, (int)Material::MaxSlatAngs + 1> transBmBmMult;
    7037             : 
    7038             :     //  3=intermediate, 4=overcast
    7039             :     Real64 DPH; // Sky/ground element altitude and azimuth increments (radians)
    7040             :     Real64 DTH;
    7041             :     Real64 PH; // Sky/ground element altitude and azimuth (radians)
    7042             :     Real64 TH;
    7043             :     Real64 SPH; // Sine and cosine of PH
    7044             :     Real64 CPH;
    7045             :     Real64 PHMIN; // Limits of altitude integration (radians)
    7046             :     Real64 PHMAX;
    7047             :     Real64 ThMin; // Limits of azimuth integration (radians)
    7048             :     Real64 ThMax;
    7049             :     Real64 PhWin; // Altitude, azimuth angle of window normal (radians)
    7050             :     Real64 ThWin;
    7051             :     Real64 ACosTanTan; // ACOS(-TAN(Ph)*TAN(PhWin))
    7052             :     Real64 DA;         // CPH*DTH*DPH
    7053             :     Real64 COSB;       // Cosine of angle of incidence of light from sky or ground
    7054             :     Real64 TVISBR;     // Transmittance of window without shading at COSB
    7055             :     //  (times light well efficiency, if appropriate)
    7056             :     Real64 ZSU;
    7057             :     //  element for clear and overcast sky
    7058             :     Real64 ObTrans; // Product of solar transmittances of obstructions seen by a light ray
    7059             : 
    7060             :     // unused  REAL(r64)         :: HitPointLumFrClearSky     ! Luminance of obstruction from clear sky (cd/m2)
    7061             :     // unused  REAL(r64)         :: HitPointLumFrOvercSky     ! Luminance of obstruction from overcast sky (cd/m2)
    7062             :     // unused  REAL(r64)         :: HitPointLumFrSun          ! Luminance of obstruction from sun (cd/m2)
    7063             :     int ICtrl;       // Window control pointer
    7064             :     Real64 COSBSun;  // Cosine of angle of incidence of direct sun on window
    7065             :     Real64 TVISBSun; // Window's visible transmittance at COSBSun
    7066             :     //  (times light well efficiency, if appropriate)
    7067             :     Real64 ZSU1; // Transmitted direct normal illuminance (lux)
    7068             :     //  CHARACTER(len=32) :: ShType                    ! Window shading device type
    7069             :     bool ShadeOn;                // True if exterior or interior window shade present
    7070             :     bool BlindOn;                // True if exterior or interior window blind present
    7071             :     bool ScreenOn;               // True if exterior window screen present
    7072             :     int BlNum;                   // Blind number
    7073             :                                  //        int ScNum; // Screen number //Unused Set but never used
    7074             :     int PipeNum;                 // TDD pipe object number
    7075             :     int ShelfNum;                // Daylighting shelf object number
    7076             :     int InShelfSurf;             // Inside daylighting shelf surface number
    7077             :     int OutShelfSurf;            // Outside daylighting shelf surface number
    7078             :     Real64 TransBlBmDiffFront;   // Isolated blind vis beam-diffuse front transmittance
    7079             :     Real64 TransScBmDiffFront;   // Isolated screen vis beam-diffuse front transmittance
    7080             :     Real64 ReflGlDiffDiffBack;   // Bare glazing system vis diffuse back reflectance
    7081             :     Real64 ReflGlDiffDiffFront;  // Bare glazing system vis diffuse front reflectance
    7082             :     Real64 ReflBlBmDiffFront;    // Isolated blind vis beam-diffuse front reflectance
    7083             :     Real64 TransBlDiffDiffFront; // Isolated blind vis diffuse-diffuse front transmittance
    7084             :     Real64 ReflBlDiffDiffFront;  // Isolated blind vis diffuse-diffuse front reflectance
    7085             :     Real64 ReflBlDiffDiffBack;   // Isolated blind vis diffuse-diffuse back reflectance
    7086             :     Real64 ReflScDiffDiffBack;   // Isolated screen vis diffuse-diffuse back reflectance
    7087             : 
    7088             :     Real64 td2; // Diffuse-diffuse vis trans of bare glass layers 2 and 3
    7089             :     Real64 td3;
    7090             :     Real64 rbd1; // Beam-diffuse back vis reflectance of bare glass layers 1 and 2
    7091             :     Real64 rbd2;
    7092             :     Real64 rfd2; // Beam-diffuse front vis reflectance of bare glass layers 2 and 3
    7093             :     Real64 rfd3;
    7094             :     Real64 tfshd;      // Diffuse-diffuse front vis trans of bare blind
    7095             :     Real64 rbshd;      // Diffuse-diffuse back vis reflectance of bare blind
    7096             :     Real64 ZSUObsRefl; // Illuminance on window from beam solar reflected by an
    7097             :     //  obstruction (for unit beam normal illuminance)
    7098             :     int NearestHitSurfNum;  // Surface number of nearest obstruction
    7099             :     int NearestHitSurfNumX; // Surface number to use when obstruction is a shadowing surface
    7100             :     Real64 LumAtHitPtFrSun; // Luminance at hit point on obstruction from solar reflection
    7101             :     //  for unit beam normal illuminance (cd/m2)
    7102             :     Real64 SunObstructionMult; // = 1 if sun hits a ground point; otherwise = 0
    7103             :     bool hitObs;               // True iff obstruction is hit
    7104             :     Real64 ObsVisRefl;         // Visible reflectance of obstruction
    7105             :     Real64 SkyReflVisLum;      // Reflected sky luminance at hit point divided by unobstructed sky
    7106             :     //  diffuse horizontal illuminance [(cd/m2)/lux]
    7107             :     Real64 dReflObsSky; // Contribution to sky-related illuminance on window due to sky diffuse
    7108             :     //  reflection from an obstruction
    7109             :     Real64 TVisSunRefl; // Diffuse vis trans of bare window for beam reflection calc
    7110             :     //  (times light well efficiency, if appropriate)
    7111             :     Real64 ZSU1refl; // Beam normal illuminance times ZSU1refl = illuminance on window
    7112             :     //  due to specular reflection from exterior surfaces
    7113             : 
    7114             :     ExtWinType extWinType;      // Exterior window type (InZoneExtWin, AdjZoneExtWin, NotInOrAdjZoneExtWin)
    7115             :     Real64 EnclInsideSurfArea;  // temporary for calculations, total surface area of enclosure surfaces m2
    7116             :     int IntWinAdjZoneExtWinNum; // the index of the exterior window in IntWinAdjZoneExtWin nested struct
    7117             :     int IntWinNum;              // window index for interior windows associated with exterior windows
    7118             :     Real64 COSBintWin;
    7119             : 
    7120             :     WinShadingType ShType;
    7121             : 
    7122      324804 :     auto &dl = state.dataDayltg;
    7123      324804 :     auto &thisEnclDaylight = dl->enclDaylight(enclNum);
    7124      324804 :     auto const &surf = state.dataSurface->Surface(IWin);
    7125      324804 :     auto const &surfWin = state.dataSurface->SurfaceWindow(IWin);
    7126      324804 :     int const enclNumThisWin = state.dataSurface->Surface(surf.BaseSurf).SolarEnclIndex;
    7127             :     // The inside surface area, ZoneDaylight(ZoneNum)%totInsSurfArea was calculated in subr DayltgAveInteriorReflectance
    7128             : 
    7129      324804 :     if (enclNumThisWin == enclNum) {
    7130      317484 :         extWinType = ExtWinType::InZone;
    7131      317484 :         EnclInsideSurfArea = dl->enclDaylight(enclNumThisWin).totInsSurfArea;
    7132      317484 :         IntWinAdjZoneExtWinNum = 0;
    7133             :     } else {
    7134        7320 :         extWinType = ExtWinType::AdjZone;
    7135             :         // If window is exterior window in adjacent zone, then use areas of both enclosures
    7136        7320 :         EnclInsideSurfArea = dl->enclDaylight(enclNum).totInsSurfArea + dl->enclDaylight(enclNumThisWin).totInsSurfArea;
    7137             :         // find index in IntWinAdjZoneExtWin
    7138        7320 :         for (int AdjExtWinLoop = 1; AdjExtWinLoop <= thisEnclDaylight.NumOfIntWinAdjEnclExtWins; ++AdjExtWinLoop) {
    7139        2424 :             if (IWin == thisEnclDaylight.IntWinAdjEnclExtWin(AdjExtWinLoop).SurfNum) { // found it
    7140        2424 :                 IntWinAdjZoneExtWinNum = AdjExtWinLoop;
    7141        2424 :                 break; // added TH 4/13/2010
    7142             :             }
    7143             :         }
    7144             :     }
    7145             : 
    7146             :     // Initialize window luminance and fluxes for split-flux calculation
    7147     1299216 :     dl->winLum(IHR, _) = Illums();
    7148             :     // dl->WLUMSU(IHR, _) = 0.0;
    7149             :     // dl->WLUMSUdisk(IHR, _) = 0.0;
    7150             : 
    7151      324804 :     int const IConst = state.dataSurface->SurfActiveConstruction(IWin);
    7152      324804 :     auto const &construct = state.dataConstruction->Construct(IConst);
    7153             : 
    7154      324804 :     BlindOn = false;
    7155      324804 :     ShadeOn = false;
    7156      324804 :     ScreenOn = false;
    7157             : 
    7158      324804 :     if (state.dataSurface->Surface(IWin).OriginalClass == SurfaceClass::TDD_Dome) {
    7159        4896 :         PipeNum = state.dataSurface->SurfWinTDDPipeNum(IWin);
    7160             :     }
    7161             : 
    7162      324804 :     ShelfNum = state.dataSurface->SurfDaylightingShelfInd(IWin);
    7163      324804 :     if (ShelfNum > 0) {
    7164        2448 :         InShelfSurf = state.dataDaylightingDevicesData->Shelf(ShelfNum).InSurf;   // Inside daylighting shelf present if > 0
    7165        2448 :         OutShelfSurf = state.dataDaylightingDevicesData->Shelf(ShelfNum).OutSurf; // Outside daylighting shelf present if > 0
    7166             :     } else {
    7167      322356 :         InShelfSurf = 0;
    7168      322356 :         OutShelfSurf = 0;
    7169             :     }
    7170             : 
    7171             :     // Divide sky and ground into elements of altitude PH and
    7172             :     // azimuth TH, and add the contribution of light coming from each
    7173             :     // element to the transmitted flux at the center of the window
    7174             :     // Azimuth ranges over a maximum of 2 Pi radians.
    7175             :     // Altitude ranges over a maximum of Pi/2 radians between -Pi/2 < PH < +Pi/2, so that elements are not counted twice
    7176             :     // PH = 0 at the horizon; PH = Pi/2 at the zenith
    7177      324804 :     PHMIN = max(-Constant::PiOvr2, surfWin.phi - Constant::PiOvr2);
    7178      324804 :     PHMAX = min(Constant::PiOvr2, surfWin.phi + Constant::PiOvr2);
    7179      324804 :     DPH = (PHMAX - PHMIN) / double(NPHMAX);
    7180             : 
    7181             :     // Sky/ground element altitude integration
    7182      324804 :     Vector3<Real64> const SUNCOS_IHR(state.dataSurface->SurfSunCosHourly(IHR));
    7183     3572844 :     for (int IPH = 1; IPH <= NPHMAX; ++IPH) {
    7184     3248040 :         PH = PHMIN + (double(IPH) - 0.5) * DPH;
    7185             : 
    7186     3248040 :         SPH = std::sin(PH);
    7187     3248040 :         CPH = std::cos(PH);
    7188             :         // Third component of unit vector in (TH,PH) direction
    7189     3248040 :         U.z = SPH;
    7190             : 
    7191             :         // Limits of azimuth integration
    7192     3248040 :         PhWin = surfWin.phi;
    7193     3248040 :         ThWin = surfWin.theta;
    7194     3248040 :         if (PhWin >= 0.0) {
    7195     3248040 :             if (PH >= Constant::PiOvr2 - PhWin) {
    7196      633828 :                 ThMin = -Constant::Pi;
    7197      633828 :                 ThMax = Constant::Pi;
    7198             :             } else {
    7199     2614212 :                 ACosTanTan = std::acos(-std::tan(PH) * std::tan(PhWin));
    7200     2614212 :                 ThMin = ThWin - std::abs(ACosTanTan);
    7201     2614212 :                 ThMax = ThWin + std::abs(ACosTanTan);
    7202             :             }
    7203             : 
    7204             :         } else { // PhiSurf < 0.0
    7205           0 :             if (PH <= -PhWin - Constant::PiOvr2) {
    7206           0 :                 ThMin = -Constant::Pi;
    7207           0 :                 ThMax = Constant::Pi;
    7208             :             } else {
    7209           0 :                 ACosTanTan = std::acos(-std::tan(PH) * std::tan(PhWin));
    7210           0 :                 ThMin = ThWin - std::abs(ACosTanTan);
    7211           0 :                 ThMax = ThWin + std::abs(ACosTanTan);
    7212             :             }
    7213             :         }
    7214             : 
    7215     3248040 :         DTH = (ThMax - ThMin) / double(NTHMAX);
    7216     3248040 :         DA = CPH * DTH * DPH;
    7217             : 
    7218             :         // Sky/ground element azimuth integration
    7219     3248040 :         Real64 const sin_window_phi(std::sin(surfWin.phi));
    7220     3248040 :         Real64 const cos_window_phi(std::cos(surfWin.phi));
    7221    55216680 :         for (int ITH = 1; ITH <= NTHMAX; ++ITH) {
    7222    51968640 :             TH = ThMin + (double(ITH) - 0.5) * DTH;
    7223    51968640 :             U.x = CPH * std::cos(TH);
    7224    51968640 :             U.y = CPH * std::sin(TH);
    7225             :             // Cosine of angle of incidence of light from sky or ground element
    7226    51968640 :             COSB = SPH * sin_window_phi + CPH * cos_window_phi * std::cos(TH - surfWin.theta);
    7227    51968640 :             if (COSB < 0.0) continue; // Sky/ground elements behind window (although there shouldn't be any)
    7228             : 
    7229             :             // Initialize illuminance on window for this sky/ground element
    7230   207874560 :             ZSK = Illums();
    7231    51968640 :             ZSU = 0.0;
    7232             :             // Initialize illuminance on window from beam solar reflection if ray hits an obstruction
    7233    51968640 :             ZSUObsRefl = 0.0;
    7234             : 
    7235    51968640 :             if (ISunPos == 1) { // Intersection calculation has to be done only for first sun position
    7236             :                 // Determine net transmittance of obstructions that the ray hits. ObTrans will be 1.0
    7237             :                 // if no obstructions are hit.
    7238     4103360 :                 ObTrans = DayltgHitObstruction(state, IHR, IWin, state.dataSurface->SurfaceWindow(IWin).WinCenter, U);
    7239     4103360 :                 dl->ObTransM[IPH][ITH] = ObTrans;
    7240     4103360 :                 dl->SkyObstructionMult[IPH][ITH] = 1.0;
    7241             :             }
    7242             : 
    7243             :             // SKY AND GROUND RADIATION ON WINDOW
    7244             : 
    7245             :             // Contribution is from sky if PH > 0 (ray goes upward), and from ground if PH < 0 (ray goes downward)
    7246             :             // (There may also be contributions from reflection from obstructions; see 'BEAM SOLAR AND SKY SOLAR
    7247             :             // REFLECTED FROM NEAREST OBSTRUCTION,' below.)
    7248             : 
    7249    51968640 :             if (PH > 0.0) { // Contribution is from sky
    7250   155274720 :                 for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
    7251   124219776 :                     ZSK.sky[iSky] = DayltgSkyLuminance(state, static_cast<SkyType>(iSky), TH, PH) * COSB * DA * dl->ObTransM[IPH][ITH];
    7252             :                 }
    7253             :             } else { // PH <= 0.0; contribution is from ground
    7254    20913696 :                 if (state.dataSurface->CalcSolRefl && dl->ObTransM[IPH][ITH] > 1.e-6 && ISunPos == 1) {
    7255             :                     // Calculate effect of obstructions on shading of sky diffuse reaching the ground point hit
    7256             :                     // by the ray. This effect is given by the ratio SkyObstructionMult =
    7257             :                     // (obstructed sky diffuse at ground point)/(unobstructed sky diffuse at ground point).
    7258             :                     // This ratio is calculated for an isotropic sky.
    7259             :                     // Ground point hit by the ray:
    7260        6080 :                     Real64 Alfa = std::acos(-U.z);
    7261        6080 :                     Real64 Beta = std::atan2(U.y, U.x);
    7262        6080 :                     Real64 HorDis = (state.dataSurface->SurfaceWindow(IWin).WinCenter.z - state.dataSurface->GroundLevelZ) * std::tan(Alfa);
    7263        6080 :                     groundHitPt.z = state.dataSurface->GroundLevelZ;
    7264        6080 :                     groundHitPt.x = state.dataSurface->SurfaceWindow(IWin).WinCenter.x + HorDis * std::cos(Beta);
    7265        6080 :                     groundHitPt.y = state.dataSurface->SurfaceWindow(IWin).WinCenter.y + HorDis * std::sin(Beta);
    7266             : 
    7267        6080 :                     dl->SkyObstructionMult[IPH][ITH] =
    7268        6080 :                         CalcObstrMultiplier(state, groundHitPt, AltAngStepsForSolReflCalc, DataSurfaces::AzimAngStepsForSolReflCalc);
    7269             :                 } // End of check if solar reflection calc is in effect
    7270             : 
    7271    20913696 :                 auto const &gilsk = dl->horIllum[IHR];
    7272   104568480 :                 for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
    7273             :                     // Below, luminance of ground in cd/m2 is illuminance on ground in lumens/m2
    7274             :                     // times ground reflectance, divided by pi, times obstruction multiplier.
    7275   167309568 :                     ZSK.sky[iSky] = (gilsk.sky[iSky] * state.dataEnvrn->GndReflectanceForDayltg / Constant::Pi) * COSB * DA * dl->ObTransM[IPH][ITH] *
    7276    83654784 :                                     dl->SkyObstructionMult[IPH][ITH];
    7277             :                 }
    7278             :                 // Determine if sun illuminates the point that ray hits the ground. If the solar reflection
    7279             :                 // calculation has been requested (CalcSolRefl = .TRUE.) shading by obstructions, including
    7280             :                 // the building itself, is considered in determining whether sun hits the ground point.
    7281             :                 // Otherwise this shading is ignored and the sun always hits the ground point.
    7282    20913696 :                 SunObstructionMult = 1.0;
    7283    20913696 :                 if (state.dataSurface->CalcSolRefl && dl->ObTransM[IPH][ITH] > 1.e-6 && ISunPos == 1) {
    7284             :                     // Sun reaches ground point if vector from this point to the sun is unobstructed
    7285        6080 :                     hitObs = false;
    7286       27444 :                     for (int ObsSurfNum : state.dataSurface->AllShadowPossObstrSurfaceList) {
    7287       23806 :                         hitObs = PierceSurface(state, ObsSurfNum, groundHitPt, SUNCOS_IHR, obsHitPt);
    7288       23806 :                         if (hitObs) break;
    7289        6080 :                     }
    7290        6080 :                     if (hitObs) SunObstructionMult = 0.0;
    7291             :                 }
    7292    20913696 :                 ZSU = (dl->horIllum[IHR].sun * state.dataEnvrn->GndReflectanceForDayltg / Constant::Pi) * COSB * DA * dl->ObTransM[IPH][ITH] *
    7293             :                       SunObstructionMult;
    7294             :             }
    7295             :             // BEAM SOLAR AND SKY SOLAR REFLECTED FROM NEAREST OBSTRUCTION
    7296             : 
    7297    51968640 :             if (state.dataSurface->CalcSolRefl && dl->ObTransM[IPH][ITH] < 1.0) {
    7298             :                 // Find obstruction whose hit point is closest to the center of the window
    7299       36320 :                 DayltgClosestObstruction(state, state.dataSurface->SurfaceWindow(IWin).WinCenter, U, NearestHitSurfNum, nearestHitPt);
    7300       36320 :                 if (NearestHitSurfNum > 0) {
    7301             : 
    7302             :                     // Beam solar reflected from nearest obstruction.
    7303       36320 :                     LumAtHitPtFrSun = DayltgSurfaceLumFromSun(state, IHR, U, NearestHitSurfNum, nearestHitPt);
    7304       36320 :                     ZSUObsRefl = LumAtHitPtFrSun * COSB * DA;
    7305       36320 :                     ZSU += ZSUObsRefl;
    7306             : 
    7307             :                     // Sky solar reflected from nearest obstruction.
    7308       36320 :                     int const ObsConstrNum = state.dataSurface->Surface(NearestHitSurfNum).Construction;
    7309       36320 :                     if (ObsConstrNum > 0) {
    7310             :                         // Exterior building surface is nearest hit
    7311           0 :                         if (!state.dataConstruction->Construct(ObsConstrNum).TypeIsWindow) {
    7312             :                             // Obstruction is not a window, i.e., is an opaque surface
    7313           0 :                             ObsVisRefl = 1.0 - dynamic_cast<const Material::MaterialChild *>(
    7314           0 :                                                    state.dataMaterial->Material(state.dataConstruction->Construct(ObsConstrNum).LayerPoint(1)))
    7315           0 :                                                    ->AbsorpVisible;
    7316             :                         } else {
    7317             :                             // Obstruction is a window; assume it is bare
    7318           0 :                             ObsVisRefl = state.dataConstruction->Construct(ObsConstrNum).ReflectVisDiffFront;
    7319             :                         }
    7320             :                     } else {
    7321             :                         // Shadowing surface is nearest hit
    7322       36320 :                         if (state.dataSurface->SurfDaylightingShelfInd(NearestHitSurfNum) > 0) {
    7323             :                             // Skip daylighting shelves, whose reflection is separately calculated
    7324           0 :                             ObsVisRefl = 0.0;
    7325             :                         } else {
    7326       36320 :                             ObsVisRefl = state.dataSurface->SurfShadowDiffuseVisRefl(NearestHitSurfNum);
    7327       36320 :                             if (state.dataSurface->SurfShadowGlazingConstruct(NearestHitSurfNum) > 0)
    7328           0 :                                 ObsVisRefl += state.dataSurface->SurfShadowGlazingFrac(NearestHitSurfNum) *
    7329           0 :                                               state.dataConstruction->Construct(state.dataSurface->SurfShadowGlazingConstruct(NearestHitSurfNum))
    7330           0 :                                                   .ReflectVisDiffFront;
    7331             :                             // Note in the above that ShadowSurfDiffuseVisRefl is the reflectance of opaque part of
    7332             :                             // shadowing surface times (1 - ShadowSurfGlazingFrac)
    7333             :                         }
    7334             :                     }
    7335       36320 :                     NearestHitSurfNumX = NearestHitSurfNum;
    7336             :                     // Each shadowing surface has a "mirror" duplicate surface facing in the opposite direction.
    7337             :                     // The following gets the correct side of a shadowing surface for reflection.
    7338       36320 :                     if (state.dataSurface->Surface(NearestHitSurfNum).IsShadowing) {
    7339       36320 :                         if (dot(U, state.dataSurface->Surface(NearestHitSurfNum).OutNormVec) > 0.0) NearestHitSurfNumX = NearestHitSurfNum + 1;
    7340             :                     }
    7341       36320 :                     if (!state.dataSysVars->DetailedSkyDiffuseAlgorithm || !state.dataSurface->ShadingTransmittanceVaries ||
    7342           0 :                         state.dataHeatBal->SolarDistribution == DataHeatBalance::Shadowing::Minimal) {
    7343       36320 :                         SkyReflVisLum = ObsVisRefl * state.dataSurface->Surface(NearestHitSurfNumX).ViewFactorSky *
    7344       36320 :                                         state.dataSolarShading->SurfDifShdgRatioIsoSky(NearestHitSurfNumX) / Constant::Pi;
    7345             :                     } else {
    7346           0 :                         SkyReflVisLum = ObsVisRefl * state.dataSurface->Surface(NearestHitSurfNumX).ViewFactorSky *
    7347           0 :                                         state.dataSolarShading->SurfDifShdgRatioIsoSkyHRTS(1, IHR, NearestHitSurfNumX) / Constant::Pi;
    7348             :                     }
    7349       36320 :                     dReflObsSky = SkyReflVisLum * COSB * DA;
    7350             : 
    7351       36320 :                     auto const &gilsk = dl->horIllum[IHR];
    7352      181600 :                     for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
    7353      145280 :                         ZSK.sky[iSky] += gilsk.sky[iSky] * dReflObsSky;
    7354             :                     }
    7355             :                 }
    7356             :             } // End of check if exterior solar reflection calculation is active
    7357             : 
    7358             :             //  ===Bare window (no shade or blind; non-diffusing glass)===
    7359             : 
    7360             :             // Increment flux entering space and window luminance (cd/m2).
    7361             :             // FLCW--(I,J) = part of incoming flux (in lumens) that goes up to ceiling and upper part of walls.
    7362             :             // FLFW--(I,J) = part that goes down to floor and lower part of walls
    7363             : 
    7364    51968640 :             if (state.dataSurface->Surface(IWin).OriginalClass == SurfaceClass::TDD_Dome) {
    7365             :                 // Unshaded visible transmittance of TDD for a single ray from sky/ground element
    7366      783360 :                 TVISBR = TransTDD(state, PipeNum, COSB, RadType::VisibleBeam) * surfWin.glazedFrac;
    7367             : 
    7368             :                 // Make all transmitted light diffuse for a TDD with a bare diffuser
    7369      783360 :                 auto &wlumsk = dl->winLum(IHR, 1);
    7370      783360 :                 auto &flfwsk = FLFW[1];
    7371      783360 :                 auto &flcwsk = FLCW[1];
    7372             : 
    7373      783360 :                 auto &tddFluxInc = dl->TDDFluxInc(IHR, PipeNum);
    7374      783360 :                 auto &tddFluxTrans = dl->TDDFluxTrans(IHR, PipeNum);
    7375     3916800 :                 for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
    7376     3133440 :                     wlumsk.sky[iSky] += ZSK.sky[iSky] * TVISBR / Constant::Pi;
    7377     3133440 :                     flfwsk.sky[iSky] += ZSK.sky[iSky] * TVISBR * (1.0 - surfWin.fractionUpgoing);
    7378     3133440 :                     flcwsk.sky[iSky] += ZSK.sky[iSky] * TVISBR * surfWin.fractionUpgoing;
    7379             : 
    7380             :                     // For later calculation of diffuse visible transmittance
    7381     3133440 :                     tddFluxInc.sky[iSky] += ZSK.sky[iSky];
    7382     3133440 :                     tddFluxTrans.sky[iSky] += ZSK.sky[iSky] * TVISBR;
    7383             : 
    7384             :                 } // for (iSky)
    7385             : 
    7386      783360 :                 tddFluxInc.sky[(int)SkyType::Clear] += ZSU;
    7387      783360 :                 tddFluxTrans.sky[(int)SkyType::Clear] += ZSU * TVISBR;
    7388             : 
    7389      783360 :                 dl->winLum(IHR, 1).sun += ZSU * TVISBR / Constant::Pi;
    7390      783360 :                 FLFW[1].sun += ZSU * TVISBR * (1.0 - surfWin.fractionUpgoing);
    7391      783360 :                 FLCW[1].sun += ZSU * TVISBR * surfWin.fractionUpgoing;
    7392             : 
    7393             :             } else { // Bare window
    7394             :                 // Transmittance of bare window for this sky/ground element
    7395    51185280 :                 TVISBR = General::POLYF(COSB, construct.TransVisBeamCoef) * surfWin.glazedFrac * surfWin.lightWellEff;
    7396             : 
    7397    51185280 :                 if (InShelfSurf > 0) { // Inside daylighting shelf
    7398             :                     // Daylighting shelf simplification:  All light is diffuse
    7399             :                     // SurfaceWindow(IWin)%FractionUpgoing is already set to 1.0 earlier
    7400      391680 :                     auto &flcwsk = FLCW[1];
    7401     1958400 :                     for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
    7402     1566720 :                         flcwsk.sky[iSky] += ZSK.sky[iSky] * TVISBR * surfWin.fractionUpgoing;
    7403             :                     }
    7404      391680 :                     FLCW[1].sun += ZSU * TVISBR * surfWin.fractionUpgoing;
    7405             : 
    7406             :                 } else { // Normal window
    7407             : 
    7408             :                     // CR 7869  correct TVISBR if disk beam passes thru interior window
    7409    50793600 :                     if (extWinType == ExtWinType::AdjZone) {
    7410             :                         // modify TVISBR by second window transmission
    7411             :                         // first determine if ray from point passes thru any interior window
    7412      387840 :                         hitObs = false;
    7413      387840 :                         for (int IntWinLoop = 1; IntWinLoop <= thisEnclDaylight.IntWinAdjEnclExtWin(IntWinAdjZoneExtWinNum).NumOfIntWindows;
    7414             :                              ++IntWinLoop) {
    7415           0 :                             IntWinNum = thisEnclDaylight.IntWinAdjEnclExtWin(IntWinAdjZoneExtWinNum).IntWinNum(IntWinLoop);
    7416           0 :                             auto const &surfIntWin = state.dataSurface->SurfaceWindow(IntWinNum);
    7417           0 :                             hitObs = PierceSurface(state, IntWinNum, surfIntWin.WinCenter, SUNCOS_IHR, obsHitPt);
    7418           0 :                             if (hitObs) { // disk passes thru
    7419             :                                 // cosine of incidence angle of light from sky or ground element for
    7420           0 :                                 COSBintWin = SPH * std::sin(surfIntWin.phi) + CPH * std::cos(surfIntWin.phi) * std::cos(TH - surfIntWin.theta);
    7421           0 :                                 TVISBR *= General::POLYF(
    7422             :                                     COSBintWin,
    7423           0 :                                     state.dataConstruction->Construct(state.dataSurface->Surface(IntWinNum).Construction).TransVisBeamCoef);
    7424           0 :                                 break;
    7425             :                             }
    7426             :                         }
    7427      387840 :                         if (!hitObs) { // blocked by opaque parts, beam does not actually pass thru interior window to reach zone
    7428      387840 :                             TVISBR = 0.0;
    7429             :                         }
    7430             :                     }
    7431             : 
    7432    50793600 :                     auto &flfwsk = FLFW[1];
    7433    50793600 :                     auto &flcwsk = FLCW[1];
    7434   253968000 :                     for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
    7435             :                         // IF (PH < 0.0d0) THEN
    7436             :                         // Fixed by FCW, Nov. 2003:
    7437   203174400 :                         if (PH > 0.0) {
    7438   120616320 :                             flfwsk.sky[iSky] += ZSK.sky[iSky] * TVISBR;
    7439             :                         } else {
    7440    82558080 :                             flcwsk.sky[iSky] += ZSK.sky[iSky] * TVISBR;
    7441             :                         }
    7442             :                     } // for (iSky)
    7443             : 
    7444    50793600 :                     if (PH > 0.0) {
    7445    30154080 :                         FLFW[1].sun += ZSU * TVISBR;
    7446             :                     } else {
    7447    20639520 :                         FLCW[1].sun += ZSU * TVISBR;
    7448             :                     }
    7449             : 
    7450             :                 } // End of check if window with daylighting shelf or normal window
    7451             :             }     // End of check if TDD:DOME or bare window
    7452             : 
    7453             :             // Check if window has shade or blind
    7454    51968640 :             ICtrl = state.dataSurface->Surface(IWin).activeWindowShadingControl;
    7455    51968640 :             if (state.dataSurface->Surface(IWin).HasShadeControl) {
    7456    23911360 :                 ShType = state.dataSurface->WindowShadingControl(ICtrl).ShadingType;
    7457    23911360 :                 BlNum = state.dataSurface->SurfWinBlindNumber(IWin);
    7458             :                 //                    ScNum = SurfaceWindow( IWin ).ScreenNumber; //Unused Set but never used
    7459             : 
    7460    23911360 :                 ShadeOn = ANY_SHADE(ShType);
    7461    23911360 :                 BlindOn = ANY_BLIND(ShType);
    7462    23911360 :                 ScreenOn = (ShType == WinShadingType::ExtScreen);
    7463             :             }
    7464             : 
    7465    51968640 :             if (ShadeOn || BlindOn || ScreenOn || state.dataSurface->SurfWinSolarDiffusing(IWin)) {
    7466             : 
    7467             :                 // ===Window with interior or exterior shade or blind, exterior screen, or with diffusing glass===
    7468             : 
    7469             :                 // Increment flux entering space and window luminance. Shades and diffusing glass are
    7470             :                 // assumed to be perfect diffusers, i.e., the transmittance is independent of angle of
    7471             :                 // incidence and the transmitted light is isotropic. The transmittance of a blind is
    7472             :                 // assumed to depend on profile angle and slat angle; the diffuse light entering the room from
    7473             :                 // the slats of the blind is assumed to be isotropic. With blinds, light can also enter
    7474             :                 // the room by passing between the slats without reflection. The beam transmittance of a screen
    7475             :                 // is assumed to depend on sun azimuth and azimuth angle.
    7476             : 
    7477             :                 // For light from a shade, or from diffusing glass, or from the slats of a blind, a flux fraction,
    7478             :                 // SurfaceWindow(IWin)%FractionUpgoing (determined by window tilt), goes up toward
    7479             :                 // ceiling and upper part of walls, and 1-Surfacewindow(iwin)%FractionUpgoing
    7480             :                 // goes down toward floor and lower part of walls. For a blind, the light passing
    7481             :                 // between the slats goes either up or down depending on the altitude angle of the
    7482             :                 // element from which the light came. For a screen, the light passing
    7483             :                 // between the screen's cylinders goes either up or down depending on the altitude angle of the
    7484             :                 // element from which the light came.
    7485             : 
    7486      152960 :                 int IConstShaded = state.dataSurface->SurfWinActiveShadedConstruction(IWin);
    7487      152960 :                 if (state.dataSurface->SurfWinSolarDiffusing(IWin)) IConstShaded = state.dataSurface->Surface(IWin).Construction;
    7488             : 
    7489             :                 // Transmittance of window including shade, screen or blind
    7490      152960 :                 std::fill(transBmBmMult.begin(), transBmBmMult.end(), 0.0);
    7491      152960 :                 std::fill(transMult.begin(), transMult.end(), 0.0);
    7492             : 
    7493      152960 :                 if (ShadeOn) { // Shade
    7494      152960 :                     if (state.dataSurface->Surface(IWin).OriginalClass == SurfaceClass::TDD_Dome) {
    7495             :                         // Shaded visible transmittance of TDD for a single ray from sky/ground element
    7496           0 :                         transMult[1] = TransTDD(state, PipeNum, COSB, RadType::VisibleBeam) * surfWin.glazedFrac;
    7497             :                     } else { // Shade only, no TDD
    7498             :                         // Calculate transmittance of the combined window and shading device for this sky/ground element
    7499      305920 :                         transMult[1] = General::POLYF(COSB, state.dataConstruction->Construct(IConstShaded).TransVisBeamCoef) * surfWin.glazedFrac *
    7500      152960 :                                        surfWin.lightWellEff;
    7501             :                     }
    7502             : 
    7503           0 :                 } else if (ScreenOn) { // Screen: get beam-beam, beam-diffuse and diffuse-diffuse vis trans/ref of screen and glazing system
    7504           0 :                     auto const *screen = dynamic_cast<Material::MaterialScreen *>(state.dataMaterial->Material(surfWin.screenNum));
    7505           0 :                     assert(screen != nullptr);
    7506             : 
    7507           0 :                     Real64 phi = std::abs(PH - surfWin.phi);
    7508           0 :                     Real64 theta = std::abs(TH - surfWin.theta);
    7509             :                     int ip1, ip2, it1, it2; // lo/hi phi/theta interpolation map indices
    7510             :                     General::BilinearInterpCoeffs coeffs;
    7511             : 
    7512           0 :                     Material::NormalizePhiTheta(phi, theta);
    7513           0 :                     Material::GetPhiThetaIndices(phi, theta, screen->dPhi, screen->dTheta, ip1, ip2, it1, it2);
    7514           0 :                     General::GetBilinearInterpCoeffs(
    7515           0 :                         phi, theta, ip1 * screen->dPhi, ip2 * screen->dPhi, it1 * screen->dTheta, it2 * screen->dTheta, coeffs);
    7516             : 
    7517           0 :                     ReflGlDiffDiffFront = state.dataConstruction->Construct(IConst).ReflectVisDiffFront;
    7518           0 :                     ReflScDiffDiffBack = screen->DfRefVis;
    7519             : 
    7520           0 :                     auto const &b11 = screen->btars[ip1][it1];
    7521           0 :                     auto const &b12 = screen->btars[ip1][it2];
    7522           0 :                     auto const &b21 = screen->btars[ip2][it1];
    7523           0 :                     auto const &b22 = screen->btars[ip2][it2];
    7524             : 
    7525           0 :                     TransScBmDiffFront = General::BilinearInterp(b11.DfTransVis, b12.DfTransVis, b21.DfTransVis, b22.DfTransVis, coeffs);
    7526             : 
    7527           0 :                     transMult[1] = TransScBmDiffFront * surfWin.glazedFrac * state.dataConstruction->Construct(IConst).TransDiffVis /
    7528           0 :                                    (1 - ReflGlDiffDiffFront * ReflScDiffDiffBack) * surfWin.lightWellEff;
    7529             : 
    7530           0 :                     transBmBmMult[1] = General::BilinearInterp(b11.BmTransVis, b12.BmTransVis, b21.BmTransVis, b22.BmTransVis, coeffs);
    7531             : 
    7532           0 :                 } else if (BlindOn) { // Blind: get beam-diffuse and beam-beam vis trans of blind+glazing system
    7533             :                     // PETER:  As long as only interior blinds are allowed for TDDs, no need to change TransMult calculation
    7534             :                     //         for TDDs because it is based on TVISBR which is correctly calculated for TDDs above.
    7535           0 :                     auto const &blind = state.dataMaterial->Blind(BlNum);
    7536           0 :                     Real64 ProfAng = ProfileAngle(state, IWin, U, blind.SlatOrientation);
    7537             : 
    7538           0 :                     for (int JB = 1; JB <= Material::MaxSlatAngs; ++JB) {
    7539           0 :                         if (!state.dataSurface->SurfWinMovableSlats(IWin) && JB > 1) break;
    7540             : 
    7541           0 :                         TransBlBmDiffFront = Window::InterpProfAng(ProfAng, blind.VisFrontBeamDiffTrans(JB, {1, 37}));
    7542             : 
    7543           0 :                         if (ShType == WinShadingType::IntBlind) { // Interior blind
    7544           0 :                             ReflGlDiffDiffBack = construct.ReflectVisDiffBack;
    7545           0 :                             ReflBlBmDiffFront = Window::InterpProfAng(ProfAng, blind.VisFrontBeamDiffRefl(JB, {1, 37}));
    7546           0 :                             ReflBlDiffDiffFront = blind.VisFrontDiffDiffRefl(JB);
    7547           0 :                             TransBlDiffDiffFront = blind.VisFrontDiffDiffTrans(JB);
    7548           0 :                             transMult[JB] = TVISBR * (TransBlBmDiffFront + ReflBlBmDiffFront * ReflGlDiffDiffBack * TransBlDiffDiffFront /
    7549           0 :                                                                                (1.0 - ReflBlDiffDiffFront * ReflGlDiffDiffBack));
    7550             : 
    7551           0 :                         } else if (ShType == WinShadingType::ExtBlind) { // Exterior blind
    7552           0 :                             ReflGlDiffDiffFront = construct.ReflectVisDiffFront;
    7553           0 :                             ReflBlDiffDiffBack = blind.VisBackDiffDiffRefl(JB);
    7554           0 :                             transMult[JB] = TransBlBmDiffFront * surfWin.glazedFrac * construct.TransDiffVis /
    7555           0 :                                             (1.0 - ReflGlDiffDiffFront * ReflBlDiffDiffBack) * surfWin.lightWellEff;
    7556             : 
    7557             :                         } else { // Between-glass blind
    7558           0 :                             Real64 t1 = General::POLYF(COSB, construct.tBareVisCoef(1));
    7559           0 :                             td2 = construct.tBareVisDiff(2);
    7560           0 :                             rbd1 = construct.rbBareVisDiff(1);
    7561           0 :                             rfd2 = construct.rfBareVisDiff(2);
    7562           0 :                             Real64 tfshBd = Window::InterpProfAng(ProfAng, blind.VisFrontBeamDiffTrans(JB, {1, 37}));
    7563           0 :                             tfshd = blind.VisFrontDiffDiffTrans(JB);
    7564           0 :                             Real64 rfshB = Window::InterpProfAng(ProfAng, blind.VisFrontBeamDiffRefl(JB, {1, 37}));
    7565           0 :                             rbshd = blind.VisFrontDiffDiffRefl(JB);
    7566           0 :                             if (construct.TotGlassLayers == 2) { // 2 glass layers
    7567           0 :                                 transMult[JB] = t1 * (tfshBd * (1.0 + rfd2 * rbshd) + rfshB * rbd1 * tfshd) * td2 * surfWin.lightWellEff;
    7568             :                             } else { // 3 glass layers; blind between layers 2 and 3
    7569           0 :                                 Real64 t2 = General::POLYF(COSB, construct.tBareVisCoef(2));
    7570           0 :                                 td3 = construct.tBareVisDiff(3);
    7571           0 :                                 rfd3 = construct.rfBareVisDiff(3);
    7572           0 :                                 rbd2 = construct.rbBareVisDiff(2);
    7573           0 :                                 transMult[JB] = t1 * t2 * (tfshBd * (1.0 + rfd3 * rbshd) + rfshB * (rbd2 * tfshd + td2 * rbd1 * td2 * tfshd)) * td3 *
    7574           0 :                                                 surfWin.lightWellEff;
    7575             :                             }
    7576             :                         }
    7577             : 
    7578           0 :                         Real64 SlatAng = (state.dataSurface->SurfWinMovableSlats(IWin)) ? ((JB - 1) * Constant::Pi / (Material::MaxSlatAngs - 1))
    7579           0 :                                                                                         : (blind.SlatAngle * Constant::DegToRadians);
    7580             : 
    7581           0 :                         transBmBmMult[JB] =
    7582           0 :                             TVISBR * Window::BlindBeamBeamTrans(ProfAng, SlatAng, blind.SlatWidth, blind.SlatSeparation, blind.SlatThickness);
    7583             :                     } // End of loop over slat angles
    7584             : 
    7585             :                 } else { // Diffusing glass
    7586           0 :                     transMult[1] = General::POLYF(COSB, state.dataConstruction->Construct(IConstShaded).TransVisBeamCoef) * surfWin.glazedFrac *
    7587           0 :                                    surfWin.lightWellEff;
    7588             :                 } // End of check if shade, blind or diffusing glass
    7589             : 
    7590      152960 :                 if (state.dataSurface->Surface(IWin).OriginalClass == SurfaceClass::TDD_Dome) {
    7591             :                     // No beam is transmitted.  This takes care of all types of screens and blinds.
    7592           0 :                     std::fill(transBmBmMult.begin(), transBmBmMult.end(), 0.0);
    7593             :                 }
    7594             : 
    7595             :                 // Daylighting shelf simplification:  No beam makes it past end of shelf, all light is diffuse
    7596      152960 :                 if (InShelfSurf > 0) { // Inside daylighting shelf
    7597           0 :                     std::fill(transBmBmMult.begin(), transBmBmMult.end(), 0.0);
    7598             :                 }
    7599             : 
    7600             :                 // DayltgInterReflectedIllumTransBmBmMult is used in the following for windows with blinds or screens to get contribution from light
    7601             :                 // passing directly between slats or between screen material without reflection.
    7602             : 
    7603      305920 :                 for (int JB = 1; JB <= Material::MaxSlatAngs; ++JB) {
    7604             :                     // EXIT after first pass if not movable slats or exterior window screen
    7605      305920 :                     if (!state.dataSurface->SurfWinMovableSlats(IWin) && JB > 1) break;
    7606             : 
    7607      152960 :                     auto &wlumsk = dl->winLum(IHR, JB + 1);
    7608      152960 :                     auto &flfwsk = FLFW[JB + 1];
    7609      152960 :                     auto &flcwsk = FLCW[JB + 1];
    7610             : 
    7611      764800 :                     for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
    7612             : 
    7613      611840 :                         wlumsk.sky[iSky] += ZSK.sky[iSky] * transMult[JB] / Constant::Pi;
    7614      611840 :                         flfwsk.sky[iSky] += ZSK.sky[iSky] * transMult[JB] * (1.0 - surfWin.fractionUpgoing);
    7615      611840 :                         flcwsk.sky[iSky] += ZSK.sky[iSky] * transMult[JB] * surfWin.fractionUpgoing;
    7616             : 
    7617      611840 :                         if (BlindOn || ScreenOn) {
    7618           0 :                             if (PH > 0.0) {
    7619           0 :                                 flfwsk.sky[iSky] += ZSK.sky[iSky] * transBmBmMult[JB];
    7620             :                             } else {
    7621           0 :                                 flcwsk.sky[iSky] += ZSK.sky[iSky] * transBmBmMult[JB];
    7622             :                             }
    7623             :                         }
    7624             :                     }
    7625             : 
    7626      152960 :                     dl->winLum(IHR, JB + 1).sun += ZSU * transMult[JB] / Constant::Pi;
    7627      152960 :                     FLFW[JB + 1].sun += ZSU * transMult[JB] * (1.0 - surfWin.fractionUpgoing);
    7628      152960 :                     FLCW[JB + 1].sun += ZSU * transMult[JB] * surfWin.fractionUpgoing;
    7629      152960 :                     if (BlindOn || ScreenOn) {
    7630           0 :                         if (PH > 0.0) {
    7631           0 :                             FLFW[JB + 1].sun += ZSU * transBmBmMult[JB];
    7632             :                         } else {
    7633           0 :                             FLCW[JB + 1].sun += ZSU * transBmBmMult[JB];
    7634             :                         }
    7635             :                     }
    7636             :                 }
    7637             :             } // End of window with shade, screen, blind or diffusing glass
    7638             : 
    7639             :         } // End of azimuth integration loop, ITH
    7640             :     }     // End of altitude integration loop, IPH
    7641             : 
    7642      324804 :     if (OutShelfSurf > 0) { // Outside daylighting shelf
    7643             :         // Add exterior diffuse illuminance due to outside shelf
    7644             :         // Since all of the illuminance is added to the zone as upgoing diffuse, it can be added as a lump sum here
    7645             : 
    7646        2448 :         TVISBR = construct.TransDiffVis; // Assume diffuse transmittance for shelf illuminance
    7647             : 
    7648        2448 :         auto const &gilsk = dl->horIllum[IHR];
    7649        2448 :         auto &flcwsk = FLCW[1];
    7650       12240 :         for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
    7651             :             // This is only an estimate because the anisotropic sky view of the shelf is not yet taken into account.
    7652             :             // SurfAnisoSkyMult would be great to use but it is not available until the heat balance starts up.
    7653        9792 :             ZSK.sky[iSky] = gilsk.sky[iSky] * 1.0 * state.dataDaylightingDevicesData->Shelf(ShelfNum).OutReflectVis *
    7654        9792 :                             state.dataDaylightingDevicesData->Shelf(ShelfNum).ViewFactor;
    7655             : 
    7656             :             // SurfaceWindow(IWin)%FractionUpgoing is already set to 1.0 earlier
    7657        9792 :             flcwsk.sky[iSky] += ZSK.sky[iSky] * TVISBR * surfWin.fractionUpgoing;
    7658             :         } // ISKY
    7659             : 
    7660        2448 :         ZSU = dl->horIllum[IHR].sun * state.dataHeatBal->SurfSunlitFracHR(IHR, OutShelfSurf) *
    7661        2448 :               state.dataDaylightingDevicesData->Shelf(ShelfNum).OutReflectVis * state.dataDaylightingDevicesData->Shelf(ShelfNum).ViewFactor;
    7662        2448 :         FLCW[1].sun += ZSU * TVISBR * surfWin.fractionUpgoing;
    7663             :     }
    7664             : 
    7665             :     // Sky-related portion of internally reflected illuminance.
    7666             :     // The inside surface area, ZoneDaylight(ZoneNum)%totInsSurfArea, and ZoneDaylight(ZoneNum)%aveVisDiffReflect,
    7667             :     // were calculated in subr DayltgAveInteriorReflectance.
    7668             : 
    7669      974412 :     for (int JSH = 1; JSH <= Material::MaxSlatAngs + 1; ++JSH) {
    7670      974412 :         if (!state.dataSurface->SurfWinMovableSlats(IWin) && JSH > 2) break;
    7671             : 
    7672      649608 :         auto &eintsk = dl->reflIllum(IHR, JSH);
    7673      649608 :         auto const &flfwsk = FLFW[JSH];
    7674      649608 :         auto const &flcwsk = FLCW[JSH];
    7675             : 
    7676     3248040 :         for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
    7677             :             // Full area of window is used in following since effect of dividers on reducing
    7678             :             // effective window transmittance has already been accounted for in calc of FLFWSK and FLCWSK.
    7679     5196864 :             eintsk.sky[iSky] = (flfwsk.sky[iSky] * surfWin.rhoFloorWall + flcwsk.sky[iSky] * surfWin.rhoCeilingWall) *
    7680     2598432 :                                (surf.Area / surfWin.glazedFrac) / (EnclInsideSurfArea * (1.0 - dl->enclDaylight(enclNum).aveVisDiffReflect));
    7681             :         } // for (iSky)
    7682             :     }     // for (jSH)
    7683             : 
    7684             :     // BEAM SOLAR RADIATION ON WINDOW
    7685             : 
    7686             :     // Beam reaching window directly (without specular reflection from exterior obstructions)
    7687             : 
    7688      324804 :     if (state.dataHeatBal->SurfSunlitFracHR(IHR, IWin) > 0.0) {
    7689             :         // Cos of angle of incidence
    7690      243329 :         COSBSun = dl->sunAngles.sinPhi * std::sin(surfWin.phi) +
    7691      243329 :                   dl->sunAngles.cosPhi * std::cos(surfWin.phi) * std::cos(dl->sunAngles.theta - surfWin.theta);
    7692             : 
    7693      243329 :         if (COSBSun > 0.0) {
    7694             :             // Multiply direct normal illuminance (normalized to 1.0 lux)
    7695             :             // by incident angle factor and by fraction of window that is sunlit.
    7696             :             // Note that in the following SurfSunlitFracHR accounts for possibly non-zero transmittance of
    7697             :             // shading surfaces.
    7698             : 
    7699      243329 :             ZSU1 = COSBSun * state.dataHeatBal->SurfSunlitFracHR(IHR, IWin);
    7700             : 
    7701             :             // Contribution to window luminance and downgoing flux
    7702             : 
    7703             :             // -- Bare window
    7704             : 
    7705      243329 :             if (state.dataSurface->Surface(IWin).OriginalClass == SurfaceClass::TDD_Dome) {
    7706             :                 // Unshaded visible transmittance of TDD for collimated beam from the sun
    7707        4488 :                 TVISBSun = TransTDD(state, PipeNum, COSBSun, RadType::VisibleBeam) * surfWin.glazedFrac;
    7708        4488 :                 dl->TDDTransVisBeam(IHR, PipeNum) = TVISBSun;
    7709             : 
    7710        4488 :                 FLFW[1].sunDisk = 0.0; // Diffuse light only
    7711             : 
    7712        4488 :                 dl->winLum(IHR, 1).sun += ZSU1 * TVISBSun / Constant::Pi;
    7713        4488 :                 FLFW[1].sun += ZSU1 * TVISBSun * (1.0 - surfWin.fractionUpgoing);
    7714        4488 :                 FLCW[1].sun += ZSU1 * TVISBSun * surfWin.fractionUpgoing;
    7715             : 
    7716             :             } else { // Bare window
    7717      238841 :                 TVISBSun = General::POLYF(COSBSun, construct.TransVisBeamCoef) * surfWin.glazedFrac * surfWin.lightWellEff;
    7718             : 
    7719             :                 // Daylighting shelf simplification:  No beam makes it past end of shelf, all light is diffuse
    7720      238841 :                 if (InShelfSurf > 0) {     // Inside daylighting shelf
    7721        1836 :                     FLFW[1].sunDisk = 0.0; // Diffuse light only
    7722             : 
    7723             :                     // SurfaceWindow(IWin)%FractionUpgoing is already set to 1.0 earlier
    7724             :                     // WLUMSU(1,IHR) = WLUMSU(1,IHR) + ZSU1 * TVISBSun / PI
    7725             :                     // FLFWSU(1) = FLFWSU(1) + ZSU1 * TVISBSun * (1.0 - SurfaceWindow(IWin)%FractionUpgoing)
    7726        1836 :                     FLCW[1].sun += ZSU1 * TVISBSun * surfWin.fractionUpgoing;
    7727             :                 } else { // Normal window
    7728      237005 :                     FLFW[1].sunDisk = ZSU1 * TVISBSun;
    7729             :                 }
    7730             :             }
    7731             : 
    7732             :             // -- Window with shade, screen, blind or diffusing glass
    7733      243329 :             if (ShadeOn || BlindOn || ScreenOn || state.dataSurface->SurfWinSolarDiffusing(IWin)) {
    7734         472 :                 std::fill(transBmBmMult.begin(), transBmBmMult.end(), 0.0);
    7735         472 :                 std::fill(transMult.begin(), transMult.end(), 0.0);
    7736             : 
    7737             :                 // TH 7/7/2010 moved from inside the loop: DO JB = 1,MaxSlatAngs
    7738             :                 Real64 ProfAng;
    7739         472 :                 if (BlindOn) {
    7740           0 :                     auto const &blind = state.dataMaterial->Blind(BlNum);
    7741           0 :                     ProfAng = ProfileAngle(state, IWin, state.dataSurface->SurfSunCosHourly(IHR), blind.SlatOrientation);
    7742             :                 }
    7743             : 
    7744         944 :                 for (int JB = 1; JB <= Material::MaxSlatAngs; ++JB) {
    7745         944 :                     if (!state.dataSurface->SurfWinMovableSlats(IWin) && JB > 1) break;
    7746             : 
    7747         472 :                     if (ShadeOn || ScreenOn || state.dataSurface->SurfWinSolarDiffusing(IWin)) { // Shade or screen on or diffusing glass
    7748         472 :                         if (state.dataSurface->Surface(IWin).OriginalClass == SurfaceClass::TDD_Dome) {
    7749             :                             // Shaded visible transmittance of TDD for collimated beam from the sun
    7750           0 :                             transMult[1] = TransTDD(state, PipeNum, COSBSun, RadType::VisibleBeam) * surfWin.glazedFrac;
    7751             :                         } else {
    7752         472 :                             if (ScreenOn) {
    7753           0 :                                 auto const *screen = dynamic_cast<Material::MaterialScreen const *>(state.dataMaterial->Material(surfWin.screenNum));
    7754           0 :                                 assert(screen != nullptr);
    7755           0 :                                 Real64 phi = std::abs(dl->sunAngles.phi - surfWin.phi);
    7756           0 :                                 Real64 theta = std::abs(dl->sunAngles.theta - surfWin.theta);
    7757             :                                 int ip1, ip2, it1, it2;
    7758             :                                 General::BilinearInterpCoeffs coeffs;
    7759           0 :                                 Material::NormalizePhiTheta(phi, theta);
    7760           0 :                                 Material::GetPhiThetaIndices(phi, theta, screen->dPhi, screen->dTheta, ip1, ip2, it1, it2);
    7761           0 :                                 General::GetBilinearInterpCoeffs(
    7762           0 :                                     phi, theta, ip1 * screen->dPhi, ip2 * screen->dPhi, it1 * screen->dTheta, it2 * screen->dTheta, coeffs);
    7763           0 :                                 Real64 BmBmTransVis = General::BilinearInterp(screen->btars[ip1][it1].BmTransVis,
    7764           0 :                                                                               screen->btars[ip1][it2].BmTransVis,
    7765           0 :                                                                               screen->btars[ip2][it1].BmTransVis,
    7766           0 :                                                                               screen->btars[ip2][it2].BmTransVis,
    7767             :                                                                               coeffs);
    7768             : 
    7769           0 :                                 transMult[1] = BmBmTransVis * surfWin.glazedFrac * surfWin.lightWellEff;
    7770             :                             } else {
    7771         472 :                                 int IConstShaded = state.dataSurface->SurfWinActiveShadedConstruction(IWin);
    7772         472 :                                 if (state.dataSurface->SurfWinSolarDiffusing(IWin)) IConstShaded = surf.Construction;
    7773         944 :                                 transMult[1] = General::POLYF(COSBSun, state.dataConstruction->Construct(IConstShaded).TransVisBeamCoef) *
    7774         472 :                                                surfWin.glazedFrac * surfWin.lightWellEff;
    7775             :                             }
    7776             :                         }
    7777             : 
    7778             :                     } else { // Blind on
    7779             : 
    7780             :                         // As long as only interior blinds are allowed for TDDs, no need to change TransMult calculation
    7781             :                         // for TDDs because it is based on TVISBSun which is correctly calculated for TDDs above.
    7782           0 :                         auto const &blind = state.dataMaterial->Blind(BlNum);
    7783             : 
    7784           0 :                         Real64 TransBlBmDiffFront = Window::InterpProfAng(ProfAng, blind.VisFrontBeamDiffTrans(JB, {1, 37}));
    7785             : 
    7786           0 :                         if (ShType == WinShadingType::IntBlind) { // Interior blind
    7787             :                             // TH CR 8121, 7/7/2010
    7788             :                             // ReflBlBmDiffFront = WindowManager::InterpProfAng(ProfAng,Blind(BlNum)%VisFrontBeamDiffRefl)
    7789           0 :                             Real64 ReflBlBmDiffFront = Window::InterpProfAng(ProfAng, blind.VisFrontBeamDiffRefl(JB, {1, 37}));
    7790             : 
    7791             :                             // TH added 7/12/2010 for CR 8121
    7792           0 :                             Real64 ReflBlDiffDiffFront = blind.VisFrontDiffDiffRefl(JB);
    7793           0 :                             Real64 TransBlDiffDiffFront = blind.VisFrontDiffDiffTrans(JB);
    7794             : 
    7795           0 :                             transMult[JB] = TVISBSun * (TransBlBmDiffFront + ReflBlBmDiffFront * ReflGlDiffDiffBack * TransBlDiffDiffFront /
    7796           0 :                                                                                  (1.0 - ReflBlDiffDiffFront * ReflGlDiffDiffBack));
    7797             : 
    7798           0 :                         } else if (ShType == WinShadingType::ExtBlind) { // Exterior blind
    7799           0 :                             transMult[JB] = TransBlBmDiffFront *
    7800           0 :                                             (construct.TransDiffVis / (1.0 - ReflGlDiffDiffFront * blind.VisBackDiffDiffRefl(JB))) *
    7801           0 :                                             surfWin.glazedFrac * surfWin.lightWellEff;
    7802             : 
    7803             :                         } else { // Between-glass blind
    7804           0 :                             Real64 t1 = General::POLYF(COSBSun, construct.tBareVisCoef(1));
    7805           0 :                             Real64 tfshBd = Window::InterpProfAng(ProfAng, blind.VisFrontBeamDiffTrans(JB, {1, 37}));
    7806           0 :                             Real64 rfshB = Window::InterpProfAng(ProfAng, blind.VisFrontBeamDiffRefl(JB, {1, 37}));
    7807           0 :                             if (construct.TotGlassLayers == 2) { // 2 glass layers
    7808           0 :                                 transMult[JB] = t1 * (tfshBd * (1.0 + rfd2 * rbshd) + rfshB * rbd1 * tfshd) * td2 * surfWin.lightWellEff;
    7809             :                             } else { // 3 glass layers; blind between layers 2 and 3
    7810           0 :                                 Real64 t2 = General::POLYF(COSBSun, construct.tBareVisCoef(2));
    7811           0 :                                 transMult[JB] = t1 * t2 * (tfshBd * (1.0 + rfd3 * rbshd) + rfshB * (rbd2 * tfshd + td2 * rbd1 * td2 * tfshd)) * td3 *
    7812           0 :                                                 surfWin.lightWellEff;
    7813             :                             }
    7814             :                         }
    7815             : 
    7816           0 :                         Real64 SlatAng = (state.dataSurface->SurfWinMovableSlats(IWin)) ? ((JB - 1) * Constant::Pi / (Material::MaxSlatAngs - 1))
    7817           0 :                                                                                         : (blind.SlatAngle * Constant::DegToRadians);
    7818             : 
    7819           0 :                         transBmBmMult[JB] =
    7820           0 :                             TVISBSun * Window::BlindBeamBeamTrans(ProfAng, SlatAng, blind.SlatWidth, blind.SlatSeparation, blind.SlatThickness);
    7821             :                     } // ShadeOn/ScreenOn/BlindOn/Diffusing glass
    7822             : 
    7823         472 :                     if (state.dataSurface->Surface(IWin).OriginalClass == SurfaceClass::TDD_Dome) {
    7824           0 :                         std::fill(transBmBmMult.begin(), transBmBmMult.end(), 0.0); // No beam, diffuse only
    7825             :                     }
    7826             : 
    7827             :                     // Daylighting shelf simplification:  No beam makes it past end of shelf, all light is diffuse
    7828         472 :                     if (InShelfSurf > 0) {                                          // Inside daylighting shelf
    7829           0 :                         std::fill(transBmBmMult.begin(), transBmBmMult.end(), 0.0); // No beam, diffuse only
    7830             :                         // SurfaceWindow(IWin)%FractionUpgoing is already set to 1.0 earlier
    7831             :                     }
    7832             : 
    7833         472 :                     dl->winLum(IHR, JB + 1).sun += ZSU1 * transMult[JB] / Constant::Pi;
    7834         472 :                     dl->winLum(IHR, JB + 1).sunDisk = ZSU1 * transBmBmMult[JB] / Constant::Pi;
    7835         472 :                     FLFW[JB + 1].sun += ZSU1 * transMult[JB] * (1.0 - surfWin.fractionUpgoing);
    7836         472 :                     FLFW[JB + 1].sunDisk = ZSU1 * transBmBmMult[JB];
    7837         472 :                     FLCW[JB + 1].sun += ZSU1 * transMult[JB] * surfWin.fractionUpgoing;
    7838             :                 } // for (JB)
    7839             :             }     // if (BlindOn || ShadeOn)
    7840             :         }         // if (COSBSun > 0)
    7841             :     }             // if (SurfSunlitFracHR > 0)
    7842             : 
    7843             :     // Beam reaching window after specular reflection from exterior obstruction
    7844             : 
    7845             :     // In the following, Beam normal illuminance times ZSU1refl = illuminance on window due to
    7846             :     // specular reflection from exterior surfaces
    7847             : 
    7848      324804 :     if (state.dataSurface->CalcSolRefl && state.dataSurface->Surface(IWin).OriginalClass != SurfaceClass::TDD_Dome) {
    7849             : 
    7850         908 :         ZSU1refl = state.dataSurface->SurfReflFacBmToBmSolObs(IHR, IWin);
    7851             : 
    7852         908 :         if (ZSU1refl > 0.0) {
    7853             :             // Contribution to window luminance and downgoing flux
    7854             : 
    7855             :             // -- Bare window. We use diffuse-diffuse transmittance here rather than beam-beam to avoid
    7856             :             //    complications due to specular reflection from multiple exterior surfaces
    7857             : 
    7858           0 :             TVisSunRefl = construct.TransDiffVis * surfWin.glazedFrac * surfWin.lightWellEff;
    7859             :             // In the following it is assumed that all reflected beam is going downward, as it would be in the
    7860             :             // important case of reflection from a highly glazed facade of a neighboring building. However, in
    7861             :             // rare cases (such as upward specular reflection from a flat horizontal skylight) it may
    7862             :             // actually be going upward.
    7863           0 :             FLFW[1].sunDisk += ZSU1refl * TVisSunRefl;
    7864             : 
    7865             :             // -- Window with shade, blind or diffusing glass
    7866             : 
    7867           0 :             if (ShadeOn || BlindOn || ScreenOn || state.dataSurface->SurfWinSolarDiffusing(IWin)) {
    7868           0 :                 std::fill(transBmBmMult.begin(), transBmBmMult.end(), 0.0);
    7869           0 :                 std::fill(transMult.begin(), transMult.end(), 0.0);
    7870             : 
    7871           0 :                 for (int JB = 1; JB <= Material::MaxSlatAngs; ++JB) {
    7872           0 :                     if (!state.dataSurface->SurfWinMovableSlats(IWin) && JB > 1) break;
    7873             : 
    7874           0 :                     if (ShadeOn || state.dataSurface->SurfWinSolarDiffusing(IWin)) { // Shade on or diffusing glass
    7875           0 :                         int IConstShaded = state.dataSurface->SurfWinActiveShadedConstruction(IWin);
    7876           0 :                         if (state.dataSurface->SurfWinSolarDiffusing(IWin)) IConstShaded = state.dataSurface->Surface(IWin).Construction;
    7877           0 :                         transMult[1] = state.dataConstruction->Construct(IConstShaded).TransDiffVis * surfWin.glazedFrac * surfWin.lightWellEff;
    7878             : 
    7879           0 :                     } else if (ScreenOn) { // Exterior screen on
    7880           0 :                         auto const *screen = dynamic_cast<Material::MaterialScreen const *>(state.dataMaterial->Material(surfWin.screenNum));
    7881           0 :                         Real64 TransScDiffDiffFront = screen->DfTransVis;
    7882             : 
    7883           0 :                         transMult[1] = TransScDiffDiffFront *
    7884           0 :                                        (state.dataConstruction->Construct(IConst).TransDiffVis / (1.0 - ReflGlDiffDiffFront * ReflScDiffDiffBack)) *
    7885           0 :                                        surfWin.glazedFrac * surfWin.lightWellEff;
    7886             : 
    7887             :                     } else { // Blind on
    7888             : 
    7889           0 :                         auto const &blind = state.dataMaterial->Blind(BlNum);
    7890           0 :                         TransBlDiffDiffFront = state.dataMaterial->Blind(BlNum).VisFrontDiffDiffTrans(JB);
    7891           0 :                         if (ShType == WinShadingType::IntBlind) { // Interior blind
    7892           0 :                             ReflBlDiffDiffFront = blind.VisFrontDiffDiffRefl(JB);
    7893           0 :                             transMult[JB] = TVisSunRefl * (TransBlDiffDiffFront + ReflBlDiffDiffFront * ReflGlDiffDiffBack * TransBlDiffDiffFront /
    7894           0 :                                                                                       (1.0 - ReflBlDiffDiffFront * ReflGlDiffDiffBack));
    7895             : 
    7896           0 :                         } else if (ShType == WinShadingType::ExtBlind) { // Exterior blind
    7897           0 :                             transMult[JB] = TransBlDiffDiffFront *
    7898           0 :                                             (construct.TransDiffVis / (1.0 - ReflGlDiffDiffFront * blind.VisBackDiffDiffRefl(JB))) *
    7899           0 :                                             surfWin.glazedFrac * surfWin.lightWellEff;
    7900             : 
    7901             :                         } else { // Between-glass blind
    7902           0 :                             Real64 t1 = construct.tBareVisDiff(1);
    7903           0 :                             Real64 tfshBd = blind.VisFrontDiffDiffTrans(JB);
    7904           0 :                             Real64 rfshB = blind.VisFrontDiffDiffRefl(JB);
    7905           0 :                             if (construct.TotGlassLayers == 2) { // 2 glass layers
    7906           0 :                                 transMult[JB] = t1 * (tfshBd * (1.0 + rfd2 * rbshd) + rfshB * rbd1 * tfshd) * td2 * surfWin.lightWellEff;
    7907             :                             } else { // 3 glass layers; blind between layers 2 and 3
    7908           0 :                                 Real64 t2 = construct.tBareVisDiff(2);
    7909           0 :                                 transMult[JB] = t1 * t2 * (tfshBd * (1.0 + rfd3 * rbshd) + rfshB * (rbd2 * tfshd + td2 * rbd1 * td2 * tfshd)) * td3 *
    7910           0 :                                                 surfWin.lightWellEff;
    7911             :                             }
    7912             :                         } // End of check of interior/exterior/between-glass blind
    7913             :                     }     // if (Blind)
    7914             : 
    7915           0 :                     dl->winLum(IHR, JB + 1).sun += ZSU1refl * transMult[JB] / Constant::Pi;
    7916           0 :                     FLFW[JB + 1].sun += ZSU1refl * transMult[JB] * (1.0 - surfWin.fractionUpgoing);
    7917           0 :                     FLCW[JB + 1].sun += ZSU1refl * transMult[JB] * surfWin.fractionUpgoing;
    7918             :                 } // End of loop over slat angles
    7919             :             }     // End of check if window has shade, blind or diffusing glass
    7920             :         }         // End of check if ZSU1refl > 0.0
    7921             :     }             // End of check if solar reflections are in effect
    7922             : 
    7923             :     // Sun-related portion of internally reflected illuminance
    7924             : 
    7925      974412 :     for (int JSH = 1; JSH <= Material::MaxSlatAngs + 1; ++JSH) {
    7926      974412 :         if (!state.dataSurface->SurfWinMovableSlats(IWin) && JSH > 2) break;
    7927             : 
    7928             :         // Full area of window is used in following since effect of dividers on reducing
    7929             :         // effective window transmittance already accounted for in calc of FLFWSU and FLCWSU
    7930             :         // CR 7869 added effect of intervening interior windows on transmittance and
    7931             :         // added inside surface area of adjacent zone
    7932     1299216 :         dl->reflIllum(IHR, JSH).sun = (FLFW[JSH].sun * surfWin.rhoFloorWall + FLCW[JSH].sun * surfWin.rhoCeilingWall) *
    7933      649608 :                                       (surf.Area / surfWin.glazedFrac) / (EnclInsideSurfArea * (1.0 - thisEnclDaylight.aveVisDiffReflect));
    7934             : 
    7935     1299216 :         dl->reflIllum(IHR, JSH).sunDisk = FLFW[JSH].sunDisk * surfWin.rhoFloorWall * (surf.Area / surfWin.glazedFrac) /
    7936      649608 :                                           (EnclInsideSurfArea * (1.0 - thisEnclDaylight.aveVisDiffReflect));
    7937             :     }
    7938      324804 : } // DayltgInterReflectedIllum()
    7939             : 
    7940        1674 : void ComplexFenestrationLuminances(EnergyPlusData &state,
    7941             :                                    int const IWin,
    7942             :                                    int const WinEl,
    7943             :                                    int const NBasis,
    7944             :                                    int const IHR,
    7945             :                                    int const iRefPoint,
    7946             :                                    Array1D<Illums> &ElementLuminance, // luminance at window element (exterior side)
    7947             :                                    CalledFor const CalledFrom,
    7948             :                                    int const MapNum)
    7949             : {
    7950             : 
    7951             :     // SUBROUTINE INFORMATION:
    7952             :     //       AUTHOR         Simon Vidanovic
    7953             :     //       DATE WRITTEN   June 2013
    7954             : 
    7955        1674 :     Vector3<Real64> obsHitPt;    // Coordinates of hit point on an obstruction (m)
    7956        1674 :     Vector3<Real64> groundHitPt; // Coordinates of point that ray from window center hits the ground (m)
    7957             : 
    7958        1674 :     auto &dl = state.dataDayltg;
    7959             : 
    7960        1674 :     int CurCplxFenState = state.dataSurface->SurfaceWindow(IWin).ComplexFen.CurrentState;
    7961        1674 :     auto &complexWinGeom = state.dataBSDFWindow->ComplexWind(IWin).Geom(CurCplxFenState);
    7962             :     // Calculate luminance from sky and sun excluding exterior obstruction transmittances and obstruction multipliers
    7963        1674 :     int SolBmIndex = complexWinGeom.SolBmIndex(IHR, state.dataGlobal->TimeStep);
    7964      244404 :     for (int iIncElem = 1; iIncElem <= NBasis; ++iIncElem) {
    7965      242730 :         Real64 LambdaInc = complexWinGeom.Inc.Lamda(iIncElem);
    7966             :         // COSB = ComplexWind(IWin)%Geom(CurCplxFenState)%CosInc(iIncElem)
    7967             :         // DA = ComplexWind(IWin)%Geom(CurCplxFenState)%DAInc(iIncElem)
    7968      242730 :         Real64 Altitude = complexWinGeom.pInc(iIncElem).Altitude;
    7969      242730 :         Real64 Azimuth = complexWinGeom.pInc(iIncElem).Azimuth;
    7970      242730 :         auto &elemLum = ElementLuminance(iIncElem);
    7971      242730 :         auto const &gilsk = dl->horIllum[IHR];
    7972             : 
    7973      242730 :         if (Altitude > 0.0) {
    7974             :             // Ray from sky element
    7975      535680 :             for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
    7976      428544 :                 elemLum.sky[iSky] = DayltgSkyLuminance(state, static_cast<SkyType>(iSky), Azimuth, Altitude) * LambdaInc;
    7977             :             }
    7978      135594 :         } else if (Altitude < 0.0) {
    7979             :             // Ray from ground element
    7980             :             // BeamObstrMultiplier = ComplexWind(IWin)%DaylghtGeom(CurCplxFenState)%GndObstrMultiplier(WinEl, iIncElem)
    7981      535680 :             for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
    7982      428544 :                 elemLum.sky[iSky] = gilsk.sky[iSky] * state.dataEnvrn->GndReflectanceForDayltg / Constant::Pi * LambdaInc;
    7983             :             }
    7984      107136 :             elemLum.sun = dl->horIllum[IHR].sun * state.dataEnvrn->GndReflectanceForDayltg / Constant::Pi * LambdaInc;
    7985             :         } else {
    7986             :             // Ray from the element which is half sky and half ground
    7987      142290 :             for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
    7988             :                 // in this case half of the pach is coming from the sky and half from the ground
    7989      227664 :                 elemLum.sky[iSky] = 0.5 * DayltgSkyLuminance(state, static_cast<SkyType>(iSky), Azimuth, Altitude) * LambdaInc +
    7990      113832 :                                     0.5 * gilsk.sky[iSky] * state.dataEnvrn->GndReflectanceForDayltg / Constant::Pi * LambdaInc;
    7991             :             }
    7992       28458 :             elemLum.sun = 0.5 * dl->horIllum[IHR].sun * state.dataEnvrn->GndReflectanceForDayltg / Constant::Pi * LambdaInc;
    7993             :         }
    7994             :         // Sun beam calculations
    7995      242730 :         if ((SolBmIndex == iIncElem) && (state.dataHeatBal->SurfSunlitFracHR(IHR, IWin) > 0.0)) {
    7996        1008 :             elemLum.sunDisk = 1.0;
    7997             :         }
    7998             :     }
    7999             : 
    8000        1674 :     auto const &complexWinDaylightGeom = state.dataBSDFWindow->ComplexWind(IWin).DaylghtGeom(CurCplxFenState);
    8001             : 
    8002        1674 :     if (CalledFrom == CalledFor::RefPoint) {
    8003        1674 :         auto const &complexWinRefPoint = complexWinDaylightGeom.RefPoint(iRefPoint);
    8004             :         // add exterior obstructions transmittances to calculated luminances
    8005        1674 :         for (int iReflElem = 1; iReflElem <= complexWinRefPoint.NReflSurf(WinEl); ++iReflElem) {
    8006           0 :             Real64 ObstrTrans = complexWinRefPoint.TransOutSurf(iReflElem, WinEl);
    8007           0 :             int iReflElemIndex = complexWinRefPoint.RefSurfIndex(iReflElem, WinEl);
    8008             : 
    8009           0 :             auto &elemLum = ElementLuminance(iReflElemIndex);
    8010           0 :             for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
    8011           0 :                 elemLum.sky[iSky] *= ObstrTrans;
    8012             :             }
    8013           0 :             elemLum.sun *= ObstrTrans;
    8014           0 :             elemLum.sunDisk *= ObstrTrans;
    8015             :         }
    8016             : 
    8017             :         // add exterior ground element obstruction multipliers to calculated luminances. For sun reflection, calculate if
    8018             :         // sun reaches the ground for that point
    8019        1674 :         Vector3<Real64> const SUNCOS_IHR = state.dataSurface->SurfSunCosHourly(IHR);
    8020      108810 :         for (int iGndElem = 1; iGndElem <= complexWinRefPoint.NGnd(WinEl); ++iGndElem) {
    8021             :             // case for sky elements. Integration is done over upper ground hemisphere to determine how many obstructions
    8022             :             // were hit in the process
    8023             : 
    8024      107136 :             Real64 BeamObstrMultiplier = complexWinRefPoint.GndObstrMultiplier(iGndElem, WinEl);
    8025      107136 :             int iGndElemIndex = complexWinRefPoint.GndIndex(iGndElem, WinEl);
    8026             : 
    8027      107136 :             auto &elemLum = ElementLuminance(iGndElemIndex);
    8028      535680 :             for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
    8029      428544 :                 elemLum.sky[iSky] *= BeamObstrMultiplier;
    8030             :             }
    8031             : 
    8032             :             // direct sun disk reflect off the ground
    8033      107136 :             Real64 SunObstrMultiplier = 1.0;
    8034      107136 :             if (state.dataSurface->CalcSolRefl) {
    8035             :                 // Sun reaches ground point if vector from this point to the sun is unobstructed
    8036      204042 :                 for (int ObsSurfNum : state.dataSurface->AllShadowPossObstrSurfaceList) {
    8037      107136 :                     groundHitPt = complexWinRefPoint.GndPt(iGndElem, WinEl);
    8038      107136 :                     bool hitObs = PierceSurface(state, ObsSurfNum, groundHitPt, SUNCOS_IHR, obsHitPt);
    8039      107136 :                     if (hitObs) {
    8040       10230 :                         SunObstrMultiplier = 0.0;
    8041       10230 :                         break;
    8042             :                     }
    8043      107136 :                 }
    8044             :             }
    8045      107136 :             elemLum.sun *= SunObstrMultiplier;
    8046             :         }
    8047             : 
    8048        1674 :     } else { // if (CalledFrom != RefPoint)
    8049             : 
    8050           0 :         auto const &complexWinIllumMap = complexWinDaylightGeom.IlluminanceMap(iRefPoint, MapNum);
    8051             :         // add exterior obstructions transmittances to calculated luminances
    8052           0 :         for (int iReflElem = 1; iReflElem <= complexWinIllumMap.NReflSurf(WinEl); ++iReflElem) {
    8053           0 :             Real64 ObstrTrans = complexWinIllumMap.TransOutSurf(iReflElem, WinEl);
    8054           0 :             int iReflElemIndex = complexWinIllumMap.RefSurfIndex(iReflElem, WinEl);
    8055           0 :             auto &elemLum = ElementLuminance(iReflElemIndex);
    8056             : 
    8057           0 :             for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
    8058           0 :                 elemLum.sky[iSky] *= ObstrTrans;
    8059             :             }
    8060           0 :             elemLum.sun *= ObstrTrans;
    8061           0 :             elemLum.sunDisk *= ObstrTrans;
    8062             :         }
    8063             : 
    8064             :         // add exterior ground element obstruction multipliers to calculated luminances. For sun reflection, calculate if
    8065             :         // sun reaches the ground for that point
    8066           0 :         Vector3<Real64> const SUNCOS_IHR = state.dataSurface->SurfSunCosHourly(IHR);
    8067           0 :         for (int iGndElem = 1; iGndElem <= complexWinIllumMap.NGnd(WinEl); ++iGndElem) {
    8068             :             // case for sky elements. Integration is done over upper ground hemisphere to determine how many obstructions
    8069             :             // were hit in the process
    8070           0 :             Real64 BeamObstrMultiplier = complexWinIllumMap.GndObstrMultiplier(iGndElem, WinEl);
    8071           0 :             int iGndElemIndex = complexWinIllumMap.GndIndex(iGndElem, WinEl);
    8072             : 
    8073           0 :             auto &elemLum = ElementLuminance(iGndElemIndex);
    8074           0 :             for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
    8075           0 :                 elemLum.sky[iSky] *= BeamObstrMultiplier;
    8076             :             }
    8077             : 
    8078             :             // direct sun disk reflect off the ground
    8079           0 :             Real64 SunObstrMultiplier = 1.0;
    8080           0 :             if (state.dataSurface->CalcSolRefl) {
    8081             :                 // Sun reaches ground point if vector from this point to the sun is unobstructed
    8082           0 :                 for (int ObsSurfNum : state.dataSurface->AllShadowPossObstrSurfaceList) {
    8083           0 :                     groundHitPt = complexWinIllumMap.GndPt(iGndElem, WinEl);
    8084             : 
    8085           0 :                     bool hitObs = PierceSurface(state, ObsSurfNum, groundHitPt, SUNCOS_IHR, obsHitPt);
    8086           0 :                     if (hitObs) {
    8087           0 :                         SunObstrMultiplier = 0.0;
    8088           0 :                         break;
    8089             :                     }
    8090           0 :                 }
    8091             :             }
    8092           0 :             elemLum.sun *= SunObstrMultiplier;
    8093             :         }
    8094           0 :     } // if (CalledFrom == RefPoint)
    8095        1674 : } // ComplexFenestrationLuminances()
    8096             : 
    8097         186 : void DayltgInterReflectedIllumComplexFenestration(EnergyPlusData &state,
    8098             :                                                   int const IWin,            // Window index
    8099             :                                                   int const WinEl,           // Current window element counter
    8100             :                                                   int const IHR,             // Hour of day
    8101             :                                                   int const daylightCtrlNum, // Daylighting control number
    8102             :                                                   int const iRefPoint,       // reference point counter
    8103             :                                                   CalledFor const CalledFrom,
    8104             :                                                   int const MapNum)
    8105             : {
    8106             : 
    8107             :     // SUBROUTINE INFORMATION:
    8108             :     //       AUTHOR         Simon Vidanovic
    8109             :     //       DATE WRITTEN   April 2013
    8110             : 
    8111             :     // PURPOSE OF THIS SUBROUTINE:
    8112             :     // Called from CalcDayltgCoefficients for each complex (bsdf) fenestration and reference point in a daylit
    8113             :     // space, for each sun position. Calculates illuminance (EINTSK and EINTSU) at reference point due
    8114             :     // to internally reflected light by integrating to determine the amount of flux from
    8115             :     // sky and ground (and beam reflected from obstructions) transmitted through
    8116             :     // the center of the window and then reflecting this
    8117             :     // light from the inside surfaces of the space.
    8118             : 
    8119         186 :     auto &dl = state.dataDayltg;
    8120             : 
    8121         186 :     Array1D<Illums> FL; // Sky related luminous flux
    8122             :     // Array1D<Real64> FLSU;     // Sun related luminous flux, excluding entering beam
    8123             :     // Array1D<Real64> FLSUdisk; // Sun related luminous flux, due to entering beam
    8124             : 
    8125         186 :     Array1D<Illums> FirstFlux; // Sky related first reflected flux
    8126             :     // Array1D<Real64> FirstFluxSU;     // Sun related first reflected flux, excluding entering beam
    8127             :     // Array1D<Real64> FirstFluxSUdisk; // Sun related first reflected flux, due to entering beam
    8128             : 
    8129         186 :     Array1D<Illums> ElementLuminance; // sky related luminance at window element (exterior side)
    8130             :     // Array1D<Real64> ElementLuminanceSun;     // sun related luminance at window element (exterior side), exluding beam
    8131             :     // Array1D<Real64> ElementLuminanceSunDisk; // sun related luminance at window element (exterior side), due to sun beam
    8132         186 :     Illums FLTot;
    8133             :     // Real64 FLSUTot;
    8134             :     // Real64 FLSUdiskTot;
    8135             : 
    8136             :     // Total for first relflected fluxes
    8137         186 :     Illums FFTot = Illums();
    8138             :     // Real64 FFSUTot;
    8139             :     // Real64 FFSUdiskTot;
    8140             : 
    8141             :     int NIncBasis;
    8142             :     int SolBmIndex; // index of current sun position
    8143             : 
    8144             :     Real64 LambdaInc; // current lambda value for incoming direction
    8145             :     // REAL(r64) :: LambdaTrn  ! current lambda value for incoming direction
    8146             :     Real64 dirTrans; // directional bsdf transmittance
    8147             : 
    8148         186 :     auto const &surf = state.dataSurface->Surface(IWin);
    8149         186 :     auto const &surfWin = state.dataSurface->SurfaceWindow(IWin);
    8150             : 
    8151         186 :     int CurCplxFenState = surfWin.ComplexFen.CurrentState;
    8152         186 :     auto &complexWinGeom = state.dataBSDFWindow->ComplexWind(IWin).Geom(CurCplxFenState);
    8153         186 :     int iConst = surfWin.ComplexFen.State(CurCplxFenState).Konst;
    8154         186 :     int NTrnBasis = complexWinGeom.Trn.NBasis;
    8155             : 
    8156         186 :     if (!allocated(FL)) FL.allocate(NTrnBasis);
    8157         744 :     FL = Illums();
    8158             :     // if (!allocated(FLSU)) FLSU.dimension(NTrnBasis, 0.0);
    8159             :     // if (!allocated(FLSUdisk)) FLSUdisk.dimension(NTrnBasis, 0.0);
    8160             : 
    8161         186 :     if (!allocated(FirstFlux)) FirstFlux.allocate(NTrnBasis);
    8162         744 :     FirstFlux = Illums();
    8163             :     // if (!allocated(FirstFluxSU)) FirstFluxSU.dimension(NTrnBasis, 0.0);
    8164             :     // if (!allocated(FirstFluxSUdisk)) FirstFluxSUdisk.dimension(NTrnBasis, 0.0);
    8165             : 
    8166         186 :     NIncBasis = complexWinGeom.Inc.NBasis;
    8167         186 :     if (!allocated(ElementLuminance)) ElementLuminance.allocate(NIncBasis);
    8168         744 :     ElementLuminance = Illums();
    8169             :     // if (!allocated(ElementLuminanceSun)) ElementLuminanceSun.dimension(NIncBasis, 0.0);
    8170             :     // if (!allocated(ElementLuminanceSunDisk)) ElementLuminanceSunDisk.dimension(NIncBasis, 0.0);
    8171             : 
    8172             :     // Integration over sky/ground/sun elements is done over window incoming basis element and flux is calculated for each
    8173             :     // outgoing direction. This is used to calculate first reflected flux
    8174             : 
    8175         186 :     ComplexFenestrationLuminances(state, IWin, WinEl, NIncBasis, IHR, iRefPoint, ElementLuminance, CalledFrom, MapNum);
    8176             : 
    8177             :     // luminance from sun disk needs to include fraction of sunlit area
    8178         186 :     SolBmIndex = complexWinGeom.SolBmIndex(IHR, state.dataGlobal->TimeStep);
    8179         186 :     Real64 COSIncSun = (SolBmIndex > 0) ? complexWinGeom.CosInc(SolBmIndex) : 0.0;
    8180             : 
    8181       27156 :     for (int i = 1; i <= (int)ElementLuminance.size(); ++i)
    8182       26970 :         ElementLuminance(i).sunDisk *= state.dataHeatBal->SurfSunlitFracHR(IHR, IWin) * COSIncSun;
    8183             : 
    8184             :     //        FLSKTot = 0.0;
    8185         186 :     FLTot.sun = 0.0;
    8186         186 :     FLTot.sunDisk = 0.0;
    8187         186 :     FFTot.sun = 0.0;
    8188         186 :     FFTot.sunDisk = 0.0;
    8189             :     // now calculate flux into each outgoing direction by integrating over all incoming directions
    8190       27156 :     for (int iBackElem = 1; iBackElem <= NTrnBasis; ++iBackElem) {
    8191     3937620 :         for (int iIncElem = 1; iIncElem <= NIncBasis; ++iIncElem) {
    8192     3910650 :             LambdaInc = complexWinGeom.Inc.Lamda(iIncElem);
    8193     3910650 :             dirTrans = state.dataConstruction->Construct(iConst).BSDFInput.VisFrtTrans(iBackElem, iIncElem);
    8194             : 
    8195     3910650 :             auto &fl = FL(iBackElem);
    8196     3910650 :             auto const &elemLum = ElementLuminance(iIncElem);
    8197    19553250 :             for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
    8198    15642600 :                 fl.sky[iSky] += dirTrans * LambdaInc * elemLum.sky[iSky];
    8199             :             }
    8200             : 
    8201     3910650 :             fl.sun += dirTrans * LambdaInc * elemLum.sun;
    8202     3910650 :             fl.sunDisk += dirTrans * LambdaInc * elemLum.sunDisk;
    8203             :         }
    8204             : 
    8205       26970 :         auto &firstFlux = FirstFlux(iBackElem);
    8206       26970 :         auto const &fl = FL(iBackElem);
    8207      134850 :         for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
    8208      107880 :             firstFlux.sky[iSky] = fl.sky[iSky] * complexWinGeom.AveRhoVisOverlap(iBackElem);
    8209      107880 :             FFTot.sky[iSky] += firstFlux.sky[iSky];
    8210             :             //                FLSKTot( iSky ) += FLSK( iSky, iBackElem );
    8211             :         }
    8212       26970 :         firstFlux.sun = fl.sun * complexWinGeom.AveRhoVisOverlap(iBackElem);
    8213       26970 :         FFTot.sun += firstFlux.sun;
    8214       26970 :         FLTot.sun += fl.sun;
    8215             : 
    8216       26970 :         firstFlux.sunDisk = fl.sunDisk * complexWinGeom.AveRhoVisOverlap(iBackElem);
    8217       26970 :         FFTot.sunDisk += firstFlux.sunDisk;
    8218       26970 :         FLTot.sunDisk += fl.sunDisk;
    8219             :     }
    8220             : 
    8221         186 :     auto const &thisEnclDaylight = dl->enclDaylight(dl->daylightControl(daylightCtrlNum).enclIndex);
    8222         186 :     Real64 EnclInsideSurfArea = thisEnclDaylight.totInsSurfArea;
    8223             : 
    8224         186 :     auto &eintsk = dl->reflIllum(IHR, 1);
    8225         930 :     for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
    8226         744 :         eintsk.sky[iSky] = FFTot.sky[iSky] * (surf.Area / surfWin.glazedFrac) / (EnclInsideSurfArea * (1.0 - thisEnclDaylight.aveVisDiffReflect));
    8227             :     } // for (iSky)
    8228             : 
    8229         186 :     dl->reflIllum(IHR, 1).sun = FFTot.sun * (surf.Area / surfWin.glazedFrac) / (EnclInsideSurfArea * (1.0 - thisEnclDaylight.aveVisDiffReflect));
    8230         186 :     dl->reflIllum(IHR, 1).sunDisk =
    8231         186 :         FFTot.sunDisk * (surf.Area / surfWin.glazedFrac) / (EnclInsideSurfArea * (1.0 - thisEnclDaylight.aveVisDiffReflect));
    8232             : 
    8233         186 :     if (allocated(FL)) FL.deallocate();
    8234             :     // if (allocated(FLSU)) FLSU.deallocate();
    8235             :     // if (allocated(FLSUdisk)) FLSUdisk.deallocate();
    8236             : 
    8237         186 :     if (allocated(FirstFlux)) FirstFlux.deallocate();
    8238             :     // if (allocated(FirstFluxSU)) FirstFluxSU.deallocate();
    8239             :     // if (allocated(FirstFluxSUdisk)) FirstFluxSUdisk.deallocate();
    8240             : 
    8241         186 :     if (allocated(ElementLuminance)) ElementLuminance.deallocate();
    8242             :     // if (allocated(ElementLuminanceSun)) ElementLuminanceSun.deallocate();
    8243             :     // if (allocated(ElementLuminanceSunDisk)) ElementLuminanceSunDisk.deallocate();
    8244         186 : }
    8245             : 
    8246        1488 : void DayltgDirectIllumComplexFenestration(EnergyPlusData &state,
    8247             :                                           int const IWin,      // Window index
    8248             :                                           int const WinEl,     // Current window element counter
    8249             :                                           int const IHR,       // Hour of day
    8250             :                                           int const iRefPoint, // reference point index
    8251             :                                           CalledFor const CalledFrom,
    8252             :                                           int const MapNum)
    8253             : {
    8254             : 
    8255             :     // SUBROUTINE INFORMATION:
    8256             :     //       AUTHOR         Simon Vidanovic
    8257             :     //       DATE WRITTEN   June 2013
    8258             : 
    8259        1488 :     auto &dl = state.dataDayltg;
    8260             : 
    8261             :     // Luminances from different sources to the window
    8262        1488 :     Array1D<Illums> ElementLuminance; // sky related luminance at window element (exterior side)
    8263             :     // Array1D<Real64> ElementLuminanceSun; // sun related luminance at window element (exterior side),
    8264             :     // exluding beam
    8265             :     // Array1D<Real64> ElementLuminanceSunDisk; // sun related luminance at window element (exterior side),
    8266             :     // due to sun beam
    8267             : 
    8268             :     int RefPointIndex; // reference point patch number
    8269             : 
    8270             :     Real64 dirTrans;    // directional BSDF transmittance
    8271             :     Real64 dOmega;      // solid view angle of current element
    8272             :     Real64 zProjection; // z-axe projection of solid view angle (used to calculate amount of light at horizontal surface
    8273             :     // laying at reference point)
    8274             : 
    8275        1488 :     int CurCplxFenState = state.dataSurface->SurfaceWindow(IWin).ComplexFen.CurrentState;
    8276        1488 :     auto &complexWin = state.dataBSDFWindow->ComplexWind(IWin);
    8277        1488 :     int iConst = state.dataSurface->SurfaceWindow(IWin).ComplexFen.State(CurCplxFenState).Konst;
    8278        1488 :     int NIncBasis = complexWin.Geom(CurCplxFenState).Inc.NBasis;
    8279             : 
    8280        1488 :     if (!allocated(ElementLuminance)) ElementLuminance.allocate(NIncBasis);
    8281        5952 :     ElementLuminance = Illums();
    8282             :     // if (!allocated(ElementLuminanceSun)) ElementLuminanceSun.dimension(NIncBasis, 0.0);
    8283             :     // if (!allocated(ElementLuminanceSunDisk)) ElementLuminanceSunDisk.dimension(NIncBasis, 0.0);
    8284             : 
    8285        1488 :     ComplexFenestrationLuminances(state, IWin, WinEl, NIncBasis, IHR, iRefPoint, ElementLuminance, CalledFrom, MapNum);
    8286             : 
    8287             :     // find number of outgoing basis towards current reference point
    8288        1488 :     if (CalledFrom == CalledFor::RefPoint) {
    8289        1488 :         RefPointIndex = complexWin.DaylghtGeom(CurCplxFenState).RefPoint(iRefPoint).RefPointIndex(WinEl);
    8290        1488 :         dOmega = complexWin.RefPoint(iRefPoint).SolidAngle(WinEl);
    8291        1488 :         zProjection = complexWin.RefPoint(iRefPoint).SolidAngleVec(WinEl).z;
    8292           0 :     } else if (CalledFrom == CalledFor::MapPoint) {
    8293           0 :         assert(MapNum > 0);
    8294           0 :         RefPointIndex = complexWin.DaylghtGeom(CurCplxFenState).IlluminanceMap(iRefPoint, MapNum).RefPointIndex(WinEl);
    8295           0 :         dOmega = complexWin.IlluminanceMap(iRefPoint, MapNum).SolidAngle(WinEl);
    8296           0 :         zProjection = complexWin.IlluminanceMap(iRefPoint, MapNum).SolidAngleVec(WinEl).z;
    8297             :     }
    8298             : 
    8299        1488 :     Illums WinLum = Illums();
    8300        1488 :     Illums EDir = Illums();
    8301             : 
    8302      217248 :     for (int iIncElem = 1; iIncElem <= NIncBasis; ++iIncElem) {
    8303             :         // LambdaInc = ComplexWind(IWin)%Geom(CurCplxFenState)%Inc%Lamda(iIncElem)
    8304      215760 :         dirTrans = state.dataConstruction->Construct(iConst).BSDFInput.VisFrtTrans(RefPointIndex, iIncElem);
    8305             : 
    8306      215760 :         auto const &elemLum = ElementLuminance(iIncElem);
    8307     1078800 :         for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
    8308      863040 :             WinLum.sky[iSky] += dirTrans * elemLum.sky[iSky];
    8309             :         }
    8310             : 
    8311      215760 :         WinLum.sun += dirTrans * elemLum.sun;
    8312             : 
    8313             :         // For sun disk need to go throug outgoing directions and see which directions actually contain reference point
    8314             :     }
    8315             : 
    8316        1488 :     if (zProjection > 0.0) {
    8317        5580 :         for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
    8318        4464 :             EDir.sky[iSky] = WinLum.sky[iSky] * dOmega * zProjection;
    8319             :         }
    8320        1116 :         EDir.sun = WinLum.sun * dOmega * zProjection;
    8321             :     }
    8322             : 
    8323             :     // Store solution in global variables
    8324        1488 :     auto &avwlsk = dl->avgWinLum(IHR, 1);
    8325        1488 :     auto &edirsk = dl->dirIllum(IHR, 1);
    8326             : 
    8327        7440 :     for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
    8328        5952 :         avwlsk.sky[iSky] += WinLum.sky[iSky];
    8329        5952 :         edirsk.sky[iSky] += EDir.sky[iSky];
    8330             :     }
    8331             : 
    8332        1488 :     dl->avgWinLum(IHR, 1).sun += WinLum.sun;
    8333        1488 :     dl->dirIllum(IHR, 1).sun += EDir.sun;
    8334             :     // AVWLSUdisk(1,IHR) = AVWLSUdisk(1,IHR) + WinLumSUdisk
    8335        1488 : } // DayltgDirectIllumComplexFenestration()
    8336             : 
    8337         186 : void DayltgDirectSunDiskComplexFenestration(EnergyPlusData &state,
    8338             :                                             int const iWin,  // Window index
    8339             :                                             int const iHour, // Hour of day
    8340             :                                             int const iRefPoint,
    8341             :                                             int const NumEl,            // Total number of window elements
    8342             :                                             Real64 const AZVIEW,        // Azimuth of view vector in absolute coord system for
    8343             :                                             CalledFor const CalledFrom, // indicate  which type of routine called this routine
    8344             :                                             int const MapNum)
    8345             : {
    8346             : 
    8347             :     // SUBROUTINE INFORMATION:
    8348             :     //       AUTHOR         Simon Vidanovic
    8349             :     //       DATE WRITTEN   June 2013
    8350             : 
    8351             :     // PURPOSE OF THIS SUBROUTINE:
    8352             :     // Calculate illuminance from sun disk for complex fenestration systems
    8353             : 
    8354             :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    8355         186 :     auto &dl = state.dataDayltg;
    8356             : 
    8357         186 :     assert(CalledFrom != CalledFor::MapPoint || MapNum > 0);
    8358             : 
    8359         186 :     auto const &window = state.dataSurface->SurfaceWindow(iWin);
    8360         186 :     int CurCplxFenState = window.ComplexFen.CurrentState;
    8361         186 :     int iConst = window.ComplexFen.State(CurCplxFenState).Konst;
    8362             : 
    8363         186 :     auto const &complexWindow = state.dataBSDFWindow->ComplexWind(iWin);
    8364         186 :     auto const &complexWindowGeom = complexWindow.Geom(CurCplxFenState);
    8365         186 :     auto const &complexWindowDayltgGeom = complexWindow.DaylghtGeom(CurCplxFenState);
    8366         186 :     int SolBmIndex = complexWindowGeom.SolBmIndex(iHour, state.dataGlobal->TimeStep);
    8367             : 
    8368         186 :     Real64 WindowSolidAngleDaylightPoint = (CalledFrom == CalledFor::RefPoint) ? window.refPts(iRefPoint).solidAngWtd : 0.0;
    8369         186 :     if (WindowSolidAngleDaylightPoint < 1e-6) return;
    8370             : 
    8371           0 :     Illums WinLum;
    8372           0 :     Illums ElemLum;
    8373             : 
    8374           0 :     int NTrnBasis = complexWindowGeom.Trn.NBasis;
    8375           0 :     for (int iTrnElem = 1; iTrnElem <= NTrnBasis; ++iTrnElem) {
    8376             :         // if ray from any part of the window can reach reference point
    8377             :         int refPointIntersect = (CalledFrom == CalledFor::RefPoint)
    8378           0 :                                     ? complexWindowDayltgGeom.RefPoint(iRefPoint).RefPointIntersection(iTrnElem)
    8379           0 :                                     : complexWindowDayltgGeom.IlluminanceMap(iRefPoint, MapNum).RefPointIntersection(iTrnElem);
    8380             : 
    8381           0 :         if (refPointIntersect == 0) continue;
    8382             : 
    8383           0 :         Real64 PosFac = (CalledFrom == CalledFor::RefPoint) ? complexWindowDayltgGeom.RefPoint(iRefPoint).RefPtIntPosFac(iTrnElem)
    8384           0 :                                                             : complexWindowDayltgGeom.IlluminanceMap(iRefPoint, MapNum).RefPtIntPosFac(iTrnElem);
    8385             : 
    8386           0 :         Real64 RayZ = -complexWindowGeom.sTrn(iTrnElem).z;
    8387             : 
    8388             :         // Need to recalculate position factor for dominant direction in case of specular bsdf.  Otherwise this will produce
    8389             :         // very inaccurate results because of position factor of the sun and bsdf pach can vary by lot
    8390           0 :         if (iTrnElem == SolBmIndex) {
    8391           0 :             Real64 XR = std::tan(std::abs(Constant::PiOvr2 - AZVIEW - dl->sunAngles.theta) + 0.001);
    8392           0 :             Real64 YR = std::tan(dl->sunAngles.phi + 0.001);
    8393           0 :             PosFac = DayltgGlarePositionFactor(XR, YR);
    8394           0 :             RayZ = dl->sunAngles.sinPhi;
    8395             :         }
    8396             : 
    8397           0 :         if (PosFac == 0.0) continue;
    8398             : 
    8399           0 :         Real64 dirTrans = (SolBmIndex > 0) ? state.dataConstruction->Construct(iConst).BSDFInput.VisFrtTrans(iTrnElem, SolBmIndex) : 0.0;
    8400           0 :         Real64 LambdaTrn = complexWindowGeom.Trn.Lamda(iTrnElem);
    8401           0 :         Vector3<Real64> V = -complexWindowGeom.sTrn(iTrnElem);
    8402           0 :         Vector3<Real64> RWin = state.dataSurface->Surface(iWin).Centroid;
    8403           0 :         Real64 TransBeam = DayltgHitObstruction(state, iHour, iWin, RWin, V);
    8404             : 
    8405           0 :         WinLum.sunDisk += (14700.0 * std::sqrt(0.000068 * PosFac) * double(NumEl) / std::pow(WindowSolidAngleDaylightPoint, 0.8)) * dirTrans *
    8406           0 :                           LambdaTrn * TransBeam;
    8407             : 
    8408           0 :         ElemLum.sunDisk += RayZ * dirTrans * LambdaTrn * TransBeam;
    8409           0 :     } // for (iTrnElem)
    8410             : 
    8411           0 :     dl->avgWinLum(iHour, 1).sunDisk = WinLum.sunDisk;
    8412           0 :     dl->dirIllum(iHour, 1).sunDisk = ElemLum.sunDisk;
    8413             : }
    8414             : 
    8415   253406236 : Real64 DayltgSkyLuminance(EnergyPlusData const &state,
    8416             :                           SkyType sky,        // Sky type: 1=clear, 2=clear turbid, 3=intermediate, 4=overcast
    8417             :                           Real64 const THSKY, // Azimuth and altitude of sky element (radians)
    8418             :                           Real64 const PHSKY)
    8419             : {
    8420             : 
    8421             :     // SUBROUTINE INFORMATION:
    8422             :     //       AUTHOR         Fred Winkelmann
    8423             :     //       DATE WRITTEN   July 1997
    8424             : 
    8425             :     // PURPOSE OF THIS SUBROUTINE:
    8426             :     // Called by CalcDayltgCoefficients, DayltgExtHorizIllum AND DayltgInterReflectedIllum.  gives
    8427             :     // luminance in cd/m2 for four different sky types, as described in R.Perez, P.Ineichen,
    8428             :     // R.Seals, J.Michalsky and R.Stewart, "Modeling daylight availability and irradiance
    8429             :     // components from direct and global irradiance," Solar Energy 44, 1990, 271-289.
    8430             :     // The luminance distributions in this routine are normalized such that
    8431             :     // the zenith luminance is 1.0, i.e., DayltgSkyLuminance =
    8432             :     // (sky luminance at THSKY, PHSKY)/(zenith luminance), which is dimensionless.
    8433             :     // The sky types are:
    8434             :     // 1. Standard CIE clear sky
    8435             :     // 2. Standard CIE high-turbidity clear sky
    8436             :     // 3. CIE intermediate sky
    8437             :     // 4. CIE overcast sky
    8438             : 
    8439             :     // METHODOLOGY EMPLOYED:
    8440             : 
    8441             :     // REFERENCES:
    8442             :     // Based on DOE-2.1E subroutine DSKYLU, which did only clear and overcast skies.
    8443             : 
    8444             :     // OTHER NOTES:
    8445             :     // THSKY ranges from 0 to 2Pi starting with 0 directly East and rotating clockwise.
    8446             :     // PHSKY ranges from 0 to Pi starting with 0 at the horizon and Pi/2 at the zenith.
    8447             : 
    8448             :     // FUNCTION LOCAL VARIABLE DECLARATIONS:
    8449   253406236 :     auto &dl = state.dataDayltg;
    8450             : 
    8451   253406236 :     Real64 G = 0.0;    // Angle between sun and element of sky (radians)
    8452   253406236 :     Real64 COSG = 0.0; // Cosine of G
    8453             : 
    8454   253406236 :     Real64 SPHSKY = max(std::sin(PHSKY), 0.01); // Prevent floating point underflows
    8455   253406236 :     Real64 Z = Constant::PiOvr2 - dl->sunAngles.phi;
    8456   253406236 :     if (sky != SkyType::Overcast) { // Following not needed for overcast sky
    8457   190054677 :         COSG = SPHSKY * dl->sunAngles.sinPhi + std::cos(PHSKY) * dl->sunAngles.cosPhi * std::cos(THSKY - dl->sunAngles.theta);
    8458   190054677 :         COSG = max(DataPrecisionGlobals::constant_minusone, min(COSG, 1.0)); // Prevent out of range due to roundoff
    8459   190054677 :         G = std::acos(COSG);
    8460             :     }
    8461             : 
    8462   253406236 :     switch (sky) {
    8463    63351559 :     case SkyType::Clear: {
    8464    63351559 :         Real64 Z1 = 0.910 + 10.0 * std::exp(-3.0 * G) + 0.45 * COSG * COSG;
    8465    63351559 :         Real64 Z2 = 1.0 - std::exp(-0.32 / SPHSKY);
    8466    63351559 :         Real64 Z3 = 0.27385 * (0.91 + 10.0 * std::exp(-3.0 * Z) + 0.45 * dl->sunAngles.sinPhi * dl->sunAngles.sinPhi);
    8467    63351559 :         return Z1 * Z2 / Z3;
    8468             : 
    8469             :     } break;
    8470    63351559 :     case SkyType::ClearTurbid: {
    8471    63351559 :         Real64 Z1 = 0.856 + 16.0 * std::exp(-3.0 * G) + 0.3 * COSG * COSG;
    8472    63351559 :         Real64 Z2 = 1.0 - std::exp(-0.32 / SPHSKY);
    8473    63351559 :         Real64 Z3 = 0.27385 * (0.856 + 16.0 * std::exp(-3.0 * Z) + 0.3 * dl->sunAngles.sinPhi * dl->sunAngles.sinPhi);
    8474    63351559 :         return Z1 * Z2 / Z3;
    8475             : 
    8476             :     } break;
    8477             : 
    8478    63351559 :     case SkyType::Intermediate: {
    8479    63351559 :         Real64 Z1 = (1.35 * (std::sin(3.59 * PHSKY - 0.009) + 2.31) * std::sin(2.6 * dl->sunAngles.phi + 0.316) + PHSKY + 4.799) / 2.326;
    8480    63351559 :         Real64 Z2 = std::exp(-G * 0.563 * ((dl->sunAngles.phi - 0.008) * (PHSKY + 1.059) + 0.812));
    8481    63351559 :         Real64 Z3 = 0.99224 * std::sin(2.6 * dl->sunAngles.phi + 0.316) + 2.73852;
    8482    63351559 :         Real64 Z4 = std::exp(-Z * 0.563 * ((dl->sunAngles.phi - 0.008) * 2.6298 + 0.812));
    8483    63351559 :         return Z1 * Z2 / (Z3 * Z4);
    8484             :     } break;
    8485    63351559 :     case SkyType::Overcast: {
    8486    63351559 :         return (1.0 + 2.0 * SPHSKY) / 3.0;
    8487             :     } break;
    8488           0 :     default:
    8489           0 :         assert(false);
    8490             :         return 0.0;
    8491             :     }
    8492             : }
    8493             : 
    8494       76902 : Real64 ProfileAngle(EnergyPlusData &state,
    8495             :                     int const SurfNum,                                     // Surface number
    8496             :                     Vector3<Real64> const &CosDirSun,                      // Solar direction cosines
    8497             :                     DataWindowEquivalentLayer::Orientation const HorOrVert // If HORIZONTAL, calculates ProfileAngHor
    8498             : )
    8499             : {
    8500             : 
    8501             :     // SUBROUTINE INFORMATION:
    8502             :     //       AUTHOR         Fred Winkelmann
    8503             :     //       DATE WRITTEN   May 2001
    8504             : 
    8505             :     // PURPOSE OF THIS SUBROUTINE:
    8506             :     // Calculates profile angle for a surface.
    8507             : 
    8508             :     // Locals
    8509             :     // SUBROUTINE ARGUMENT DEFINITIONS:
    8510             :     // For HorOrVert = HORIZONTAL,
    8511             :     //  this is the incidence angle in a plane that is normal to the window
    8512             :     //  and parallel to the Y-axis of the window (the axis along
    8513             :     //  which the height of the window is measured).
    8514             :     //  For HorOrVert = VERTICAL,
    8515             :     //  this is the incidence angle in a plane that is normal to the window
    8516             :     //  and parallel to the X-axis of the window (the axis along
    8517             :     //  which the width of the window is measured).
    8518             :     // If VERTICAL, calculates ProfileAngVert
    8519             : 
    8520       76902 :     auto const &surf = state.dataSurface->Surface(SurfNum);
    8521       76902 :     if (HorOrVert == DataWindowEquivalentLayer::Orientation::Horizontal) { // Profile angle for horizontal structures
    8522       76902 :         Real64 ElevWin =
    8523       76902 :             Constant::PiOvr2 - surf.Tilt * Constant::DegToRadians;       // Window elevation: angle between outward normal and horizontal (radians)
    8524       76902 :         Real64 AzimWin = (90.0 - surf.Azimuth) * Constant::DegToRadians; // Window azimuth (radians)
    8525       76902 :         Real64 ElevSun = std::asin(CosDirSun.z);                         // Sun elevation; angle between sun and horizontal (radians)
    8526       76902 :         Real64 AzimSun = std::atan2(CosDirSun.y, CosDirSun.x);           // Sun azimuth (radians)
    8527       76902 :         return std::atan(std::sin(ElevSun) / std::abs(std::cos(ElevSun) * std::cos(AzimWin - AzimSun))) - ElevWin;
    8528             :     } else { // Profile angle for vertical structures
    8529           0 :         Real64 ElevWin = Constant::PiOvr2 - surf.Tilt * Constant::DegToRadians;
    8530           0 :         Real64 AzimWin = surf.Azimuth * Constant::DegToRadians; // 7952
    8531           0 :         Real64 AzimSun = std::atan2(CosDirSun.x, CosDirSun.y);  // 7952
    8532             : 
    8533             :         Real64 ProfileAng;
    8534           0 :         if (std::abs(ElevWin) < 0.1) {      // Near-vertical window
    8535           0 :             ProfileAng = AzimWin - AzimSun; // CR7952 allow sign changes.
    8536             :         } else {
    8537           0 :             Vector3<Real64> WinNorm = surf.OutNormVec; // Window outward normal unit vector
    8538           0 :             Real64 ThWin = AzimWin - Constant::PiOvr2;
    8539           0 :             Real64 const sin_ElevWin = std::sin(ElevWin);
    8540             :             // Cross product of WinNorm and vector along window baseline
    8541           0 :             Vector3<Real64> WinNormCrossBase = {-sin_ElevWin * std::cos(ThWin), sin_ElevWin * std::sin(ThWin), std::cos(ElevWin)};
    8542             :             // Projection of sun vector onto plane (perpendicular to window plane) determined
    8543             :             // by WinNorm and vector along baseline of window
    8544           0 :             Vector3<Real64> SunPrime = CosDirSun - WinNormCrossBase * dot(CosDirSun, WinNormCrossBase);
    8545           0 :             ProfileAng = std::abs(std::acos(dot(WinNorm, SunPrime) / SunPrime.magnitude()));
    8546             :             // CR7952 correct sign of result for vertical slats
    8547           0 :             if ((AzimWin - AzimSun) < 0.0) ProfileAng = -1.0 * ProfileAng;
    8548           0 :         }
    8549             :         // Constrain to 0 to pi
    8550           0 :         if (ProfileAng > Constant::Pi) ProfileAng = 2.0 * Constant::Pi - ProfileAng;
    8551           0 :         return ProfileAng;
    8552             :     }
    8553             : }
    8554             : 
    8555       36320 : void DayltgClosestObstruction(EnergyPlusData &state,
    8556             :                               Vector3<Real64> const &RecPt,  // Point on window from which ray emanates (m)
    8557             :                               Vector3<Real64> const &RayVec, // Unit vector along ray pointing away from window (m)
    8558             :                               int &NearestHitSurfNum,        // Surface number of nearest obstruction that is hit by ray;
    8559             :                               Vector3<Real64> &NearestHitPt  // Ray's hit point on nearest obstruction (m)
    8560             : )
    8561             : {
    8562             : 
    8563             :     // SUBROUTINE INFORMATION:
    8564             :     //       AUTHOR         Fred Winkelmann
    8565             :     //       DATE WRITTEN   November 2003
    8566             : 
    8567             :     // PURPOSE OF THIS SUBROUTINE:
    8568             :     // Determines surface number and hit point of closest exterior obstruction hit
    8569             :     // by a ray from a window. If no obstruction is hit, NearestHitSurfNum = 0.
    8570             : 
    8571             :     // Locals
    8572             :     // SUBROUTINE ARGUMENT DEFINITIONS:
    8573             :     //  = 0 if no obstruction is hit.
    8574             : 
    8575             :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    8576       36320 :     Vector3<Real64> HitPt; // Hit point on an obstruction (m)
    8577             :     bool hit;              // True iff obstruction is hit
    8578             : 
    8579       36320 :     NearestHitSurfNum = 0;
    8580       36320 :     Real64 NearestHitDistance_sq(std::numeric_limits<Real64>::max()); // Distance squared from receiving point to nearest hit point for a ray (m^2)
    8581       36320 :     NearestHitPt = 0.0;
    8582       36320 :     if (state.dataSurface->TotSurfaces < octreeCrossover) { // Linear search through surfaces
    8583             : 
    8584      181600 :         for (int ObsSurfNum : state.dataSurface->AllShadowPossObstrSurfaceList) {
    8585             :             // Determine if this ray hits the surface and, if so, get the distance from the receiving point to the hit
    8586      145280 :             hit = PierceSurface(state, ObsSurfNum, RecPt, RayVec, HitPt);
    8587      145280 :             if (!hit) // Ray pierces surface
    8588      108960 :                 continue;
    8589             : 
    8590             :             // If obstruction is a window and its base surface is the nearest obstruction hit so far set nearestHitSurface to this window
    8591             :             // Note that in this case NearestHitDistance_sq has already been calculated, so does not have to be recalculated
    8592       36320 :             if ((state.dataSurface->Surface(ObsSurfNum).Class == SurfaceClass::Window) &&
    8593           0 :                 (state.dataSurface->Surface(ObsSurfNum).BaseSurf == NearestHitSurfNum)) {
    8594           0 :                 NearestHitSurfNum = ObsSurfNum;
    8595             :             } else {
    8596             :                 // Distance squared from receiving point to hit point
    8597       36320 :                 Real64 const HitDistance_sq(distance_squared(HitPt, RecPt));
    8598             :                 // Reset NearestHitSurfNum and NearestHitDistance_sq if this hit point is closer than previous closest
    8599       36320 :                 if (HitDistance_sq < NearestHitDistance_sq) {
    8600       36320 :                     NearestHitDistance_sq = HitDistance_sq;
    8601       36320 :                     NearestHitSurfNum = ObsSurfNum;
    8602       36320 :                     NearestHitPt = HitPt;
    8603             :                 }
    8604             :             } // End of check if obstruction was hit
    8605       36320 :         }     // for (ObsSurfNum)
    8606             : 
    8607             :     } else { // Surface octree search
    8608             : 
    8609           0 :         SurfaceData const *nearestHitSurface(nullptr);
    8610             : 
    8611             :         // Lambda function for the octree to test for surface hit
    8612           0 :         auto surfaceHit = [=, &state, &RecPt, &RayVec, &hit, &NearestHitDistance_sq, &nearestHitSurface, &NearestHitPt](SurfaceData const &surface) {
    8613           0 :             if (surface.IsShadowPossibleObstruction) {
    8614           0 :                 Vector3<Real64> HitPt;
    8615             :                 // Determine if this ray hits the surface and, if so, get the distance from the receiving point to the hit
    8616           0 :                 hit = PierceSurface(surface, RecPt, RayVec, HitPt); // Check if ray pierces surface
    8617           0 :                 if (!hit) return;
    8618             : 
    8619             :                 // If obstruction is a window and its base surface is the nearest obstruction hit so far set nearestHitSurface to this window
    8620             :                 // Note that in this case NearestHitDistance_sq has already been calculated, so does not have to be recalculated
    8621           0 :                 if ((surface.Class == SurfaceClass::Window) && (surface.BaseSurf > 0) &&
    8622           0 :                     (&state.dataSurface->Surface(surface.BaseSurf) == nearestHitSurface)) {
    8623           0 :                     nearestHitSurface = &surface;
    8624             :                 } else {
    8625             :                     // Distance squared from receiving point to hit point
    8626           0 :                     Real64 const HitDistance_sq(distance_squared(HitPt, RecPt));
    8627             :                     // Reset nearestHitSurface and NearestHitDistance_sq if this hit point is closer than previous closest
    8628           0 :                     if (HitDistance_sq < NearestHitDistance_sq) {
    8629           0 :                         NearestHitDistance_sq = HitDistance_sq;
    8630           0 :                         nearestHitSurface = &surface;
    8631           0 :                         NearestHitPt = HitPt;
    8632             :                     }
    8633             :                 } // End of check if obstruction was hit
    8634           0 :             }
    8635           0 :         };
    8636             : 
    8637             :         // Process octree surface candidates
    8638           0 :         Vector3<Real64> const RayVec_inv(SurfaceOctreeCube::safe_inverse(RayVec));
    8639           0 :         state.dataHeatBalMgr->surfaceOctree.processSurfaceRayIntersectsCube(RecPt, RayVec, RayVec_inv, surfaceHit);
    8640           0 :         if (nearestHitSurface != nullptr) { // Find surface number: This is inefficient: Improve when surfaces know their own number
    8641           0 :             for (int i = 1; i <= state.dataSurface->TotSurfaces; ++i) {
    8642           0 :                 if (&state.dataSurface->Surface(i) == nearestHitSurface) {
    8643           0 :                     NearestHitSurfNum = i;
    8644           0 :                     break;
    8645             :                 }
    8646             :             }
    8647           0 :             assert(NearestHitSurfNum != 0);
    8648             :         }
    8649           0 :     }
    8650       36320 : } // DayltgClosestObstruction()
    8651             : 
    8652       36320 : Real64 DayltgSurfaceLumFromSun(EnergyPlusData &state,
    8653             :                                int const IHR,                   // Hour number
    8654             :                                Vector3<Real64> const &Ray,      // Ray from window to reflecting surface (m)
    8655             :                                int const ReflSurfNum,           // Number of surface for which luminance is being calculated
    8656             :                                Vector3<Real64> const &ReflHitPt // Point on ReflSurfNum for luminance calculation (m)
    8657             : )
    8658             : {
    8659             : 
    8660             :     // SUBROUTINE INFORMATION:
    8661             :     //       AUTHOR         Fred Winkelmann
    8662             :     //       DATE WRITTEN   November 2003
    8663             : 
    8664             :     // PURPOSE OF THIS SUBROUTINE:
    8665             :     // Calculates exterior surface luminance due to beam solar diffuse reflection.
    8666             : 
    8667             :     // Locals
    8668             :     // SUBROUTINE ARGUMENT DEFINITIONS:
    8669             :     //  beam normal illuminance (cd/m2)
    8670             : 
    8671             :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    8672       36320 :     Vector3<Real64> SurfaceLumFromSunReflNorm; // Unit normal to reflecting surface (m)
    8673       36320 :     Vector3<Real64> SurfaceLumFromSunObsHitPt; // Hit point on obstruction (m)
    8674             :     bool hitObs;                               // True iff obstruction is hit
    8675             :     Real64 DiffVisRefl;                        // Diffuse visible reflectance of ReflSurfNum
    8676             : 
    8677             :     // Skip daylighting shelves since reflection from these is separately calculated
    8678       36320 :     if (state.dataSurface->SurfDaylightingShelfInd(ReflSurfNum) > 0) return 0.0;
    8679             : 
    8680       36320 :     auto const &reflSurf = state.dataSurface->Surface(ReflSurfNum);
    8681             : 
    8682             :     // Normal to reflecting surface in hemisphere containing window element
    8683       36320 :     SurfaceLumFromSunReflNorm = reflSurf.OutNormVec;
    8684       36320 :     if (reflSurf.IsShadowing) {
    8685       36320 :         if (dot(SurfaceLumFromSunReflNorm, Ray) > 0.0) {
    8686       36320 :             SurfaceLumFromSunReflNorm *= -1.0;
    8687             :         }
    8688             :     }
    8689             :     // Cosine of angle of incidence of sun at HitPt if sun were to reach HitPt
    8690       36320 :     Vector3<Real64> const SUNCOS_IHR = state.dataSurface->SurfSunCosHourly(IHR);
    8691       36320 :     Real64 CosIncAngAtHitPt = dot(SurfaceLumFromSunReflNorm, SUNCOS_IHR);
    8692             :     // Require that the sun be in front of this surface relative to window element
    8693       36320 :     if (CosIncAngAtHitPt <= 0.0) return 0.0; // Sun is in back of reflecting surface
    8694             :     // Sun reaches ReflHitPt if vector from ReflHitPt to sun is unobstructed
    8695           0 :     hitObs = false;
    8696           0 :     for (int ObsSurfNum : state.dataSurface->AllShadowPossObstrSurfaceList) {
    8697             :         // Exclude as a possible obstructor ReflSurfNum and its base surface (if it has one)
    8698           0 :         if (ObsSurfNum == ReflSurfNum || ObsSurfNum == reflSurf.BaseSurf) continue;
    8699           0 :         hitObs = PierceSurface(state, ObsSurfNum, ReflHitPt, SUNCOS_IHR, SurfaceLumFromSunObsHitPt);
    8700           0 :         if (hitObs) break;
    8701           0 :     }
    8702             : 
    8703           0 :     if (hitObs) return 0.0; // Obstruction was hit, blocking s auto surfaceHit = [&state, &GroundHitPtun
    8704             :     // Obstruction was not hit; sun reaches ReflHitPt.
    8705             :     // Calculate luminance at ReflHitPt due to beam solar reflection (for unit beam normal illuminance)
    8706           0 :     if (reflSurf.IsShadowing) {
    8707           0 :         DiffVisRefl = state.dataSurface->SurfShadowDiffuseVisRefl(ReflSurfNum);
    8708             :         // Note that if the shadowing surface has a non-zero glazing fraction (e.g., neighboring bldg) that the above is
    8709             :         // (1 - glazing fraction) * (vis refl of opaque part of shadowing surface); specular reflection is
    8710             :         // excluded in this value of DiffVisRefl.
    8711             :     } else { // Exterior building surface
    8712           0 :         if (!state.dataConstruction->Construct(reflSurf.Construction).TypeIsWindow) {
    8713           0 :             DiffVisRefl = 1.0 - state.dataConstruction->Construct(reflSurf.Construction).OutsideAbsorpSolar;
    8714             :         } else {
    8715             :             // Window; assume bare so no beam-to-diffuse reflection
    8716           0 :             DiffVisRefl = 0.0;
    8717             :         }
    8718             :     }
    8719           0 :     return CosIncAngAtHitPt * DiffVisRefl / Constant::Pi;
    8720       36320 : }
    8721             : 
    8722      990056 : void DayltgInteriorMapIllum(EnergyPlusData &state)
    8723             : {
    8724             : 
    8725             :     // *****super modified version of DayltgInteriorIllum by Peter Graham Ellis
    8726             :     // *****removes all control code, just calculates illum with previously determined control settings
    8727             :     // *****this should be packaged into a subroutine called from 2 places
    8728             : 
    8729             :     // SUBROUTINE INFORMATION:
    8730             :     //       AUTHOR         Fred Winkelmann
    8731             :     //       DATE WRITTEN   July 1997
    8732             :     //       MODIFIED       March 2000, FW: interpolate clear-sky daylight factors using
    8733             :     //                      HourOfDay/WeightNow and NextHour/WeightNextHour. Previously
    8734             :     //                      only HourOfDay was used
    8735             :     //                      Jan 2001, FW: interpolate in slat angle for windows with blinds
    8736             :     //                      that have movable slats
    8737             :     //                      Dec 2003, FW: fix bug--even though between-glass shade/blind is on
    8738             :     //                        daylight illum at ref pt was calculated as though it was off
    8739             :     //                      June 2009, TH: modified for thermochromic windows
    8740             :     //                      March 2010, TH: fix bug (CR 8057) for electrochromic windows
    8741             :     //       RE-ENGINEERED  na
    8742             : 
    8743             :     // PURPOSE OF THIS SUBROUTINE:
    8744             :     // Using daylighting factors and exterior illuminance, determine
    8745             :     // the current-hour interior daylight illuminance and glare index
    8746             :     // at each reference point in a space.
    8747             : 
    8748             :     // Called by InitSurfaceHeatBalance.
    8749             : 
    8750             :     // REFERENCES:
    8751             :     // Based on DOE-2.1E subroutine DINTIL.
    8752      990056 :     auto &dl = state.dataDayltg;
    8753             : 
    8754             :     // Locals
    8755      990056 :     Array1D<Real64> daylight_illum;
    8756             : 
    8757             :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    8758             :     int iSky1; // Sky type index values for averaging two sky types
    8759             :     int iSky2;
    8760             :     Real64 SkyWeight;    // Weighting factor used to average two different sky types
    8761             :     Real64 HorIllSkyFac; // Ratio between horizontal illuminance from sky horizontal irradiance and
    8762             :     //   luminous efficacy and horizontal illuminance from averaged sky
    8763             : 
    8764      990056 :     if (state.dataGlobal->WarmupFlag) return;
    8765             : 
    8766      242675 :     daylight_illum.allocate(MaxMapRefPoints);
    8767             : 
    8768             :     //              Initialize reference point illuminance and window background luminance
    8769             : 
    8770      244606 :     for (auto &thisMap : dl->illumMaps) {
    8771        1931 :         int enclNum = thisMap.enclIndex;
    8772        1931 :         auto &thisEnclDaylight = dl->enclDaylight(enclNum);
    8773             : 
    8774        1931 :         int NREFPT = thisMap.TotalMapRefPoints; // Number of daylighting map reference points
    8775             : 
    8776        1931 :         daylight_illum = 0.0;
    8777             : 
    8778        1931 :         if (state.dataEnvrn->SkyClearness > 3.0) { // Sky is average of clear and clear turbid
    8779        1419 :             SkyWeight = min(1.0, (state.dataEnvrn->SkyClearness - 3.0) / 3.0);
    8780        1419 :             iSky1 = (int)SkyType::Clear;
    8781        1419 :             iSky2 = (int)SkyType::ClearTurbid;
    8782         512 :         } else if (state.dataEnvrn->SkyClearness > 1.2) { // Sky is average of clear turbid and intermediate
    8783         198 :             SkyWeight = (state.dataEnvrn->SkyClearness - 1.2) / 1.8;
    8784         198 :             iSky1 = (int)SkyType::ClearTurbid;
    8785         198 :             iSky2 = (int)SkyType::Intermediate;
    8786             :         } else { // Sky is average of intermediate and overcast
    8787         314 :             SkyWeight = min(1.0, max(0.0, (state.dataEnvrn->SkyClearness - 1.0) / 0.2, (state.dataEnvrn->SkyBrightness - 0.05) / 0.4));
    8788         314 :             iSky1 = (int)SkyType::Intermediate;
    8789         314 :             iSky2 = (int)SkyType::Overcast;
    8790             :         }
    8791             : 
    8792             :         //              First loop over windows in this space.
    8793             :         //              Find contribution of each window to the daylight illum
    8794             :         //              and to the glare numerator at each reference point.
    8795             :         //              Use shading flags set in WindowShadingManager.
    8796             : 
    8797        1931 :         auto &daylFacHrCurr = thisMap.daylFac[state.dataGlobal->HourOfDay];
    8798        1931 :         auto &daylFacHrPrev = thisMap.daylFac[state.dataGlobal->PreviousHour];
    8799             : 
    8800       12439 :         for (int loop = 1; loop <= thisEnclDaylight.NumOfDayltgExtWins; ++loop) {
    8801       10508 :             int IWin = thisEnclDaylight.DayltgExtWinSurfNums(loop);
    8802             : 
    8803             :             // Added TH 6/29/2009 for thermochromic windows
    8804       10508 :             Real64 VTRatio = 1.0;
    8805       10508 :             if (NREFPT > 0) {
    8806       10508 :                 int IConst = state.dataSurface->Surface(IWin).Construction;
    8807       10508 :                 auto const &construction = state.dataConstruction->Construct(IConst);
    8808       10508 :                 if (construction.TCFlag == 1) {
    8809             :                     // For thermochromic windows, daylight and glare factors are always calculated
    8810             :                     //  based on the master construction. They need to be adjusted by the VTRatio, including:
    8811             :                     //  ZoneDaylight()%DaylIllFacSky, DaylIllFacSun, DaylIllFacSunDisk; DaylBackFacSky,
    8812             :                     //  DaylBackFacSun, DaylBackFacSunDisk, DaylSourceFacSky, DaylSourceFacSun, DaylSourceFacSunDisk
    8813           0 :                     Real64 VTNow = General::POLYF(1.0, construction.TransVisBeamCoef);
    8814           0 :                     Real64 VTMaster = General::POLYF(1.0, state.dataConstruction->Construct(construction.TCMasterConst).TransVisBeamCoef);
    8815           0 :                     VTRatio = VTNow / VTMaster;
    8816             :                 }
    8817             :             }
    8818             : 
    8819       10508 :             Real64 wgtThisHr = state.dataGlobal->WeightNow;
    8820       10508 :             Real64 wgtPrevHr = state.dataGlobal->WeightPreviousHour;
    8821             : 
    8822       10508 :             std::array<Dayltg::Illums, (int)DataSurfaces::WinCover::Num> DFHR; // Sky daylight factor for sky type, bare/shaded window
    8823             : 
    8824       10508 :             auto &dfhr = DFHR[iWinCover_Bare];
    8825       10508 :             auto &dfhr2 = DFHR[iWinCover_Shaded];
    8826             : 
    8827       10508 :             int SurfWinSlatsAngIndex = state.dataSurface->SurfWinSlatsAngIndex(IWin);
    8828       10508 :             int slatAngLo = SurfWinSlatsAngIndex + 1;
    8829       10508 :             int slatAngHi = min(slatAngLo + 1, Material::MaxSlatAngs + 1);
    8830       10508 :             Real64 interpFac = state.dataSurface->SurfWinSlatsAngInterpFac(IWin);
    8831             : 
    8832             :             //              Loop over reference points
    8833      993558 :             for (int ILB = 1; ILB <= NREFPT; ++ILB) {
    8834             : 
    8835      983050 :                 auto const &illSkyCurr = daylFacHrCurr(loop, ILB, 1);
    8836      983050 :                 auto const &illSkyPrev = daylFacHrPrev(loop, ILB, 1);
    8837      983050 :                 auto const &ill2SkyCurr = daylFacHrCurr(loop, ILB, 2);
    8838      983050 :                 auto const &ill2SkyPrev = daylFacHrPrev(loop, ILB, 2);
    8839             : 
    8840      983050 :                 auto const &illLoSkyCurr = daylFacHrCurr(loop, ILB, slatAngLo);
    8841      983050 :                 auto const &illLoSkyPrev = daylFacHrPrev(loop, ILB, slatAngLo);
    8842      983050 :                 auto const &illHiSkyCurr = daylFacHrCurr(loop, ILB, slatAngHi);
    8843      983050 :                 auto const &illHiSkyPrev = daylFacHrPrev(loop, ILB, slatAngHi);
    8844             : 
    8845             :                 //          Daylight factors for current sun position
    8846     4915250 :                 for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
    8847             :                     //                                ===Bare window===
    8848     3932200 :                     dfhr.sky[iSky] = VTRatio * (wgtThisHr * illSkyCurr.sky[iSky] + wgtPrevHr * illSkyPrev.sky[iSky]);
    8849             : 
    8850     8526200 :                     if ((state.dataSurface->SurfWinWindowModelType(IWin) != WindowModel::BSDF) &&
    8851     4594000 :                         (IS_SHADED(state.dataSurface->SurfWinShadingFlag(IWin)) || state.dataSurface->SurfWinSolarDiffusing(IWin))) {
    8852             : 
    8853             :                         //                                 ===Shaded window===
    8854     3270400 :                         if (!state.dataSurface->SurfWinMovableSlats(IWin)) {
    8855             :                             // Shade, screen, blind with fixed slats, or diffusing glass
    8856     3270400 :                             dfhr2.sky[iSky] = VTRatio * (wgtThisHr * ill2SkyCurr.sky[iSky] + wgtPrevHr * ill2SkyPrev.sky[iSky]);
    8857             : 
    8858             :                         } else { // Blind with movable slats
    8859           0 :                             Real64 illSkyCurr = General::Interp(illLoSkyCurr.sky[iSky], illHiSkyCurr.sky[iSky], interpFac);
    8860           0 :                             Real64 illSkyPrev = General::Interp(illLoSkyPrev.sky[iSky], illHiSkyPrev.sky[iSky], interpFac);
    8861             : 
    8862           0 :                             dfhr2.sky[iSky] = VTRatio * (wgtThisHr * illSkyCurr + wgtPrevHr * illSkyPrev);
    8863             :                         } // End of check if window has blind with movable slats
    8864             :                     }     // End of check if window is shaded or has diffusing glass
    8865             :                 }         // for (iSky)
    8866             : 
    8867             :                 // Sun daylight factor for bare/shaded window
    8868      983050 :                 std::array<Illums, (int)DataSurfaces::WinCover::Num> tmpDFHR;
    8869      983050 :                 tmpDFHR[iWinCover_Bare].sun = VTRatio * (wgtThisHr * (daylFacHrCurr(loop, ILB, 1).sun + daylFacHrCurr(loop, ILB, 1).sunDisk) +
    8870      983050 :                                                          wgtPrevHr * (daylFacHrPrev(loop, ILB, 1).sun + daylFacHrPrev(loop, ILB, 1).sunDisk));
    8871             : 
    8872     2131550 :                 if ((state.dataSurface->SurfWinWindowModelType(IWin) != WindowModel::BSDF) &&
    8873     1148500 :                     (IS_SHADED(state.dataSurface->SurfWinShadingFlag(IWin)) || state.dataSurface->SurfWinSolarDiffusing(IWin))) {
    8874             : 
    8875             :                     //                                 ===Shaded window===
    8876      817600 :                     if (!state.dataSurface->SurfWinMovableSlats(IWin)) {
    8877             :                         // Shade, screen, blind with fixed slats, or diffusing glass
    8878     1635200 :                         tmpDFHR[iWinCover_Shaded].sun =
    8879      817600 :                             VTRatio * (wgtThisHr * daylFacHrCurr(loop, ILB, 2).sun + wgtPrevHr * daylFacHrPrev(loop, ILB, 2).sun);
    8880             : 
    8881      817600 :                         if (!state.dataSurface->SurfWinSlatsBlockBeam(IWin)) {
    8882      817600 :                             tmpDFHR[iWinCover_Shaded].sun +=
    8883      817600 :                                 VTRatio * (wgtThisHr * daylFacHrCurr(loop, ILB, 2).sunDisk + wgtPrevHr * daylFacHrPrev(loop, ILB, 2).sunDisk);
    8884             :                         }
    8885             :                     } else { // Blind with movable slats
    8886           0 :                         int SurfWinSlatsAngIndex = state.dataSurface->SurfWinSlatsAngIndex(IWin);
    8887           0 :                         int slatAngLo = SurfWinSlatsAngIndex + 1;
    8888           0 :                         int slatAngHi = min(slatAngLo + 1, Material::MaxSlatAngs + 1);
    8889           0 :                         Real64 interpFac = state.dataSurface->SurfWinSlatsAngInterpFac(IWin);
    8890             : 
    8891             :                         Real64 DaylIllFacSunNow =
    8892           0 :                             General::Interp(daylFacHrCurr(loop, ILB, slatAngLo).sun, daylFacHrCurr(loop, ILB, slatAngHi).sun, interpFac);
    8893             :                         Real64 DaylIllFacSunPrev =
    8894           0 :                             General::Interp(daylFacHrPrev(loop, ILB, slatAngLo).sun, daylFacHrPrev(loop, ILB, slatAngHi).sun, interpFac);
    8895           0 :                         DFHR[iWinCover_Shaded].sun = VTRatio * (wgtThisHr * DaylIllFacSunNow + wgtPrevHr * DaylIllFacSunPrev);
    8896             : 
    8897             :                         // We add the contribution from the solar disk if slats do not block beam solar
    8898             :                         // TH CR 8010, DaylIllFacSunDisk needs to be interpolated
    8899           0 :                         if (!state.dataSurface->SurfWinSlatsBlockBeam(IWin)) {
    8900             :                             Real64 DaylIllFacSunDiskNow =
    8901           0 :                                 General::Interp(daylFacHrCurr(loop, ILB, slatAngLo).sunDisk, daylFacHrCurr(loop, ILB, slatAngHi).sunDisk, interpFac);
    8902             :                             Real64 DaylIllFacSunDiskPrev =
    8903           0 :                                 General::Interp(daylFacHrPrev(loop, ILB, slatAngLo).sunDisk, daylFacHrPrev(loop, ILB, slatAngHi).sunDisk, interpFac);
    8904           0 :                             DFHR[iWinCover_Shaded].sun += VTRatio * (wgtThisHr * DaylIllFacSunDiskNow + wgtPrevHr * DaylIllFacSunDiskPrev);
    8905             :                         }
    8906             :                     } // End of check if window has blind with movable slats
    8907             :                 }     // End of check if window is shaded or has diffusing glass
    8908             : 
    8909             :                 //              Get illuminance at ref point from bare and shaded window by
    8910             :                 //              multiplying daylight factors by exterior horizontal illuminance
    8911             : 
    8912             :                 // Adding 0.001 in the following prevents zero DayltgInteriorMapIllumHorIllSky in early morning or late evening when sun
    8913             :                 // is up in the present time step but GILSK(ISky,HourOfDay) and GILSK(ISky,NextHour) are both zero.
    8914      983050 :                 Illums tmpHorIll; // Horizontal illuminance for different sky types
    8915      983050 :                 auto const &gilCurr = dl->horIllum[state.dataGlobal->HourOfDay];
    8916      983050 :                 auto const &gilPrev = dl->horIllum[state.dataGlobal->PreviousHour];
    8917     4915250 :                 for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
    8918     3932200 :                     tmpHorIll.sky[iSky] = wgtThisHr * gilCurr.sky[iSky] + wgtPrevHr * gilPrev.sky[iSky] + 0.001;
    8919             :                 }
    8920             : 
    8921             :                 // HISKF is current time step horizontal illuminance from sky, calculated in DayltgLuminousEfficacy,
    8922             :                 // which is called in WeatherManager. HISUNF is current time step horizontal illuminance from sun,
    8923             :                 // also calculated in DayltgLuminousEfficacy.
    8924      983050 :                 HorIllSkyFac = state.dataEnvrn->HISKF / ((1.0 - SkyWeight) * tmpHorIll.sky[iSky2] + SkyWeight * tmpHorIll.sky[iSky1]);
    8925             : 
    8926     2783700 :                 for (int iWinCover = 0; iWinCover < (int)WinCover::Num; ++iWinCover) {
    8927     1966100 :                     if (iWinCover == iWinCover_Shaded) {
    8928      983050 :                         if (state.dataSurface->SurfWinWindowModelType(IWin) == WindowModel::BSDF) break;
    8929      983050 :                         if (NOT_SHADED(state.dataSurface->SurfWinShadingFlag(IWin)) && !state.dataSurface->SurfWinSolarDiffusing(IWin)) break;
    8930             :                     }
    8931     1800650 :                     auto const &dfhr = DFHR[iWinCover];
    8932             : 
    8933     3601300 :                     thisMap.refPts(ILB).winLums(loop)[iWinCover] = tmpDFHR[iWinCover].sun * state.dataEnvrn->HISUNF +
    8934     1800650 :                                                                    HorIllSkyFac * (dfhr.sky[iSky1] * SkyWeight * tmpHorIll.sky[iSky1] +
    8935     1800650 :                                                                                    dfhr.sky[iSky2] * (1.0 - SkyWeight) * tmpHorIll.sky[iSky2]);
    8936             :                 }
    8937             : 
    8938             :             } // End of reference point loop
    8939             :         }     // End of first loop over windows
    8940             : 
    8941             :         //              Second loop over windows. Find total daylight illuminance
    8942             :         //              and background luminance for each ref pt from all windows in
    8943             :         //              the space.  Use shading flags.
    8944             : 
    8945       12439 :         for (int loop = 1; loop <= thisEnclDaylight.NumOfDayltgExtWins; ++loop) {
    8946       10508 :             int IWin = thisEnclDaylight.DayltgExtWinSurfNums(loop);
    8947       10508 :             auto const &surfWin = state.dataSurface->SurfaceWindow(IWin);
    8948             : 
    8949       10508 :             int IS = findWinShadingStatus(state, IWin);
    8950             : 
    8951             :             // CR 8057. 3/17/2010.
    8952             :             // Switchable windows may be in partially switched state rather than fully dark state
    8953       10508 :             Real64 VTMULT = 1.0;
    8954             : 
    8955       10508 :             int ICtrl = state.dataSurface->Surface(IWin).activeWindowShadingControl;
    8956       10508 :             if (state.dataSurface->Surface(IWin).HasShadeControl) {
    8957        8815 :                 if (state.dataSurface->WindowShadingControl(ICtrl).shadingControlType == WindowShadingControlType::MeetDaylIlumSetp &&
    8958           0 :                     state.dataSurface->SurfWinShadingFlag(IWin) == WinShadingType::SwitchableGlazing) {
    8959             :                     // switchable windows in partial or fully switched state,
    8960             :                     //  get its intermediate VT calculated in DayltgInteriorIllum
    8961           0 :                     int IConstShaded = state.dataSurface->Surface(IWin).activeShadedConstruction;
    8962           0 :                     if (IConstShaded > 0) {
    8963             :                         // Visible transmittance (VT) of electrochromic (EC) windows in fully dark state
    8964           0 :                         Real64 VTDark = General::POLYF(1.0, state.dataConstruction->Construct(IConstShaded).TransVisBeamCoef) * surfWin.glazedFrac;
    8965           0 :                         if (VTDark > 0) VTMULT = state.dataSurface->SurfWinVisTransSelected(IWin) / VTDark;
    8966             :                     }
    8967             :                 }
    8968             :             }
    8969             : 
    8970      993558 :             for (int IL = 1; IL <= NREFPT; ++IL) {
    8971             :                 //              Determine if illuminance contribution is from bare or shaded window
    8972      983050 :                 daylight_illum(IL) += VTMULT * thisMap.refPts(IL).winLums(loop)[IS - 1];
    8973             :             }
    8974             :         } // End of second window loop
    8975             : 
    8976             :         //              Variables for reporting
    8977      161156 :         for (int IL = 1; IL <= NREFPT; ++IL) {
    8978      159225 :             thisMap.refPts(IL).lums[iLum_Illum] = max(daylight_illum(IL), 0.0);
    8979             :         }
    8980             :     } // End loop over maps
    8981      990056 : } // DayltgInteriorMapIllum()
    8982             : 
    8983        1649 : void ReportIllumMap(EnergyPlusData &state, int const MapNum)
    8984             : {
    8985             : 
    8986             :     // SUBROUTINE INFORMATION:
    8987             :     //       AUTHOR         Peter Ellis
    8988             :     //       DATE WRITTEN   May 2003
    8989             : 
    8990             :     // PURPOSE OF THIS SUBROUTINE:
    8991             :     // This subroutine produces the Daylighting Illuminance Map output.  Each separate map (by zone)
    8992             :     // is placed on a temporary file and later (see CloseReportIllumMaps) coallesced into a single
    8993             :     // output file.
    8994             : 
    8995             :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    8996             : 
    8997        1649 :     std::string MapNoString;
    8998        1649 :     auto &dl = state.dataDayltg;
    8999             : 
    9000        1649 :     if (dl->ReportIllumMap_firstTime) {
    9001           6 :         dl->ReportIllumMap_firstTime = false;
    9002           6 :         dl->FirstTimeMaps.dimension((int)dl->illumMaps.size(), true);
    9003           6 :         dl->EnvrnPrint.dimension((int)dl->illumMaps.size(), true);
    9004           6 :         dl->SavedMnDy.allocate((int)dl->illumMaps.size());
    9005             :     }
    9006             : 
    9007        1649 :     auto &illumMap = dl->illumMaps(MapNum);
    9008             : 
    9009        1649 :     if (dl->FirstTimeMaps(MapNum)) {
    9010             : 
    9011           9 :         dl->FirstTimeMaps(MapNum) = false;
    9012             : 
    9013           9 :         auto openMapFile = [&](const fs::path &filePath) -> InputOutputFile & {
    9014           9 :             auto &outputFile = *illumMap.mapFile;
    9015           9 :             outputFile.filePath = FileSystem::appendSuffixToPath(filePath, fmt::to_string(MapNum));
    9016           9 :             outputFile.ensure_open(state, "ReportIllumMap");
    9017           9 :             return outputFile;
    9018           9 :         };
    9019           9 :         if (dl->MapColSep == DataStringGlobals::CharTab) {
    9020           0 :             if (!openMapFile(state.files.outputMapTabFilePath).good()) return;
    9021             :             //                CommaDelimited = false; //Unused Set but never used
    9022           9 :         } else if (dl->MapColSep == DataStringGlobals::CharComma) {
    9023           9 :             if (!openMapFile(state.files.outputMapCsvFilePath).good()) return;
    9024             :             //                CommaDelimited = true; //Unused Set but never used
    9025             :         } else {
    9026           0 :             if (!openMapFile(state.files.outputMapTxtFilePath).good()) return;
    9027             :             //                CommaDelimited = false; //Unused Set but never used
    9028             :         }
    9029             : 
    9030           9 :         dl->SavedMnDy(MapNum) = state.dataEnvrn->CurMnDyHr.substr(0, 5);
    9031             : 
    9032           9 :         illumMap.Name = format("{} at {:.2R}m", illumMap.Name, illumMap.Z);
    9033             :     }
    9034        1649 :     if (dl->SavedMnDy(MapNum) != state.dataEnvrn->CurMnDyHr.substr(0, 5)) {
    9035          13 :         dl->EnvrnPrint(MapNum) = true;
    9036          13 :         dl->SavedMnDy(MapNum) = state.dataEnvrn->CurMnDyHr.substr(0, 5);
    9037             :     }
    9038             : 
    9039        1649 :     illumMap.pointsHeader = "";
    9040        1649 :     int rCount = 0;
    9041        3685 :     for (auto &thisDayltgCtrl : dl->daylightControl) {
    9042        2036 :         if (thisDayltgCtrl.zoneIndex != illumMap.zoneIndex) continue;
    9043             : 
    9044        3654 :         for (int R = 1; R <= thisDayltgCtrl.TotalDaylRefPoints; ++R) {
    9045        2005 :             ++rCount;
    9046        2005 :             auto const &refPt = thisDayltgCtrl.refPts(R);
    9047        2005 :             illumMap.pointsHeader += format(" RefPt{}=({:.2R}:{:.2R}:{:.2R}),", rCount, refPt.absCoords.x, refPt.absCoords.y, refPt.absCoords.z);
    9048             :         }
    9049             :     }
    9050             : 
    9051        1649 :     if (rCount > 0) {
    9052             :         // Remove trailing comma
    9053        1649 :         illumMap.pointsHeader.pop_back();
    9054             :     }
    9055        1649 :     if (dl->EnvrnPrint(MapNum)) {
    9056          22 :         WriteDaylightMapTitle(
    9057          22 :             state, MapNum, *illumMap.mapFile, illumMap.Name, state.dataEnvrn->EnvironmentName, illumMap.zoneIndex, illumMap.pointsHeader, illumMap.Z);
    9058          22 :         dl->EnvrnPrint(MapNum) = false;
    9059             :     }
    9060             : 
    9061        1649 :     if (!state.dataGlobal->WarmupFlag) {
    9062        1649 :         if (state.dataGlobal->TimeStep == state.dataGlobal->NumOfTimeStepInHour) { // Report only hourly
    9063             : 
    9064         304 :             int linelen = 0;
    9065             :             // Write X scale column header
    9066         304 :             std::string mapLine = format(" {} {:02}:00", dl->SavedMnDy(MapNum), state.dataGlobal->HourOfDay);
    9067         304 :             if (illumMap.HeaderXLineLengthNeeded) linelen = int(len(mapLine));
    9068         304 :             int RefPt = 1;
    9069        2944 :             for (int X = 1; X <= illumMap.Xnum; ++X) {
    9070             :                 const std::string AddXorYString =
    9071        2640 :                     format("{}({:.2R};{:.2R})=", dl->MapColSep, illumMap.refPts(RefPt).absCoords.x, illumMap.refPts(RefPt).absCoords.y);
    9072        2640 :                 if (illumMap.HeaderXLineLengthNeeded) linelen += int(len(AddXorYString));
    9073        2640 :                 mapLine += AddXorYString;
    9074        2640 :                 ++RefPt;
    9075        2640 :             } // X
    9076             : 
    9077         304 :             if (illumMap.HeaderXLineLengthNeeded) {
    9078           9 :                 illumMap.HeaderXLineLength = linelen;
    9079           9 :                 if (static_cast<std::string::size_type>(illumMap.HeaderXLineLength) > len(mapLine)) {
    9080           0 :                     ShowWarningError(state,
    9081           0 :                                      format("ReportIllumMap: Map=\"{}\" -- the X Header overflows buffer -- will be truncated at {} characters.",
    9082           0 :                                             illumMap.Name,
    9083           0 :                                             int(len(mapLine))));
    9084           0 :                     ShowContinueError(state, format("...needed {} characters. Please contact EnergyPlus support.", illumMap.HeaderXLineLength));
    9085             :                 }
    9086           9 :                 illumMap.HeaderXLineLengthNeeded = false;
    9087             :             }
    9088             : 
    9089         304 :             print(*illumMap.mapFile, "{}\n", mapLine);
    9090             : 
    9091             :             // Write Y scale prefix and illuminance values
    9092         304 :             RefPt = 1;
    9093        3264 :             for (int Y = 1; Y <= illumMap.Ynum; ++Y) {
    9094        2960 :                 mapLine = format("({:.2R};{:.2R})=", illumMap.refPts(RefPt).absCoords.x, illumMap.refPts(RefPt).absCoords.y);
    9095       28960 :                 for (int R = RefPt; R <= RefPt + illumMap.Xnum - 1; ++R) {
    9096       26000 :                     int IllumOut = nint(illumMap.refPts(R).lumsHr[iLum_Illum]);
    9097       26000 :                     std::string String = fmt::to_string(IllumOut);
    9098             :                     ;
    9099       26000 :                     if (!illumMap.refPts(R).inBounds) {
    9100           0 :                         String = "*" + String;
    9101             :                     }
    9102       26000 :                     mapLine += dl->MapColSep + String;
    9103       26000 :                 }
    9104             : 
    9105        2960 :                 print(*illumMap.mapFile, "{}\n", mapLine);
    9106             : 
    9107        2960 :                 RefPt += illumMap.Xnum;
    9108             :             } // X
    9109             : 
    9110         304 :             if (state.dataSQLiteProcedures->sqlite) {
    9111          16 :                 if (dl->SQFirstTime) {
    9112           1 :                     int const nX(maxval(dl->illumMaps, &IllumMap::Xnum));
    9113           1 :                     int const nY(maxval(dl->illumMaps, &IllumMap::Ynum));
    9114           1 :                     dl->XValue.allocate(nX);
    9115           1 :                     dl->YValue.allocate(nY);
    9116           1 :                     dl->IllumValue.allocate(nX, nY);
    9117           1 :                     dl->SQFirstTime = false;
    9118             :                 }
    9119             : 
    9120         176 :                 for (int Y = 1; Y <= illumMap.Ynum; ++Y) {
    9121         160 :                     dl->YValue(Y) = illumMap.Ymin + (Y - 1) * illumMap.Yinc;
    9122        1760 :                     for (int X = 1; X <= illumMap.Xnum; ++X) {
    9123        1600 :                         dl->XValue(X) = illumMap.Xmin + (X - 1) * illumMap.Xinc;
    9124        1600 :                         int IllumIndex = X + (Y - 1) * illumMap.Xnum;
    9125        1600 :                         dl->IllumValue(X, Y) = nint(illumMap.refPts(IllumIndex).lumsHr[iLum_Illum]);
    9126        1600 :                         if (!illumMap.refPts(IllumIndex).inBounds) {
    9127           0 :                             dl->IllumValue(X, Y) = -dl->IllumValue(X, Y);
    9128             :                         }
    9129             :                     } // X Loop
    9130             :                 }     // Y Loop
    9131             : 
    9132             :                 // We need DataGlobals::CalendarYear, and not DataEnvironment::Year because
    9133             :                 // otherwise if you run a TMY file, you'll get for eg 1977, 1981, etc
    9134          32 :                 state.dataSQLiteProcedures->sqlite->createSQLiteDaylightMap(MapNum,
    9135          16 :                                                                             state.dataGlobal->CalendarYear,
    9136          16 :                                                                             state.dataEnvrn->Month,
    9137          16 :                                                                             state.dataEnvrn->DayOfMonth,
    9138          16 :                                                                             state.dataGlobal->HourOfDay,
    9139             :                                                                             illumMap.Xnum,
    9140          16 :                                                                             dl->XValue,
    9141             :                                                                             illumMap.Ynum,
    9142          16 :                                                                             dl->YValue,
    9143          16 :                                                                             dl->IllumValue);
    9144             : 
    9145             :             } // WriteOutputToSQLite
    9146         304 :         }     // end time step
    9147             :     }         // not Warmup
    9148        1649 : }
    9149             : 
    9150         796 : void CloseReportIllumMaps(EnergyPlusData &state)
    9151             : {
    9152             : 
    9153             :     // SUBROUTINE INFORMATION:
    9154             :     //       AUTHOR         Linda K. Lawrie
    9155             :     //       DATE WRITTEN   June 2003
    9156             : 
    9157             :     // PURPOSE OF THIS SUBROUTINE:
    9158             :     // This subroutine "closes" out the created daylight illuminance maps by merging them
    9159             :     // into the "eplusout.map" file.
    9160             : 
    9161             :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    9162         796 :     auto &dl = state.dataDayltg;
    9163             : 
    9164         796 :     if ((int)dl->illumMaps.size() > 0) {
    9165             :         // Write map header
    9166           6 :         if (dl->MapColSep == DataStringGlobals::CharTab) {
    9167           0 :             state.files.map.filePath = state.files.outputMapTabFilePath;
    9168           6 :         } else if (dl->MapColSep == DataStringGlobals::CharComma) {
    9169           6 :             state.files.map.filePath = state.files.outputMapCsvFilePath;
    9170             :         } else {
    9171           0 :             state.files.map.filePath = state.files.outputMapTxtFilePath;
    9172             :         }
    9173             : 
    9174           6 :         state.files.map.ensure_open(state, "CloseReportIllumMaps");
    9175             : 
    9176          15 :         for (int MapNum = 1; MapNum <= (int)dl->illumMaps.size(); ++MapNum) {
    9177           9 :             auto &illumMap = dl->illumMaps(MapNum);
    9178           9 :             if (!illumMap.mapFile->good()) continue; // fatal error processing
    9179             : 
    9180           9 :             const std::vector<std::string> mapLines = illumMap.mapFile->getLines();
    9181           9 :             if (mapLines.empty()) {
    9182           0 :                 ShowSevereError(state, format("CloseReportIllumMaps: IllumMap=\"{}\" is empty.", illumMap.Name));
    9183           0 :                 break;
    9184             :             }
    9185        3295 :             for (const std::string &mapLine : mapLines) {
    9186        3286 :                 print(state.files.map, "{}\n", mapLine);
    9187           9 :             }
    9188           9 :             illumMap.mapFile->del();
    9189           9 :         }
    9190             : 
    9191           6 :         if (!dl->mapResultsReported && !state.dataErrTracking->AbortProcessing) {
    9192           0 :             const std::string message = "CloseReportIllumMaps: Illuminance maps requested but no data ever reported. Likely cause is no solar.";
    9193           0 :             ShowSevereError(state, message);
    9194           0 :             print(state.files.map, "{}\n", message);
    9195           0 :         }
    9196             :     }
    9197         796 : }
    9198             : 
    9199         796 : void CloseDFSFile(EnergyPlusData &state)
    9200             : {
    9201             : 
    9202             :     // SUBROUTINE INFORMATION:
    9203             :     //       AUTHOR         Linda Lawrie
    9204             :     //       DATE WRITTEN   August 2010
    9205             : 
    9206             :     // PURPOSE OF THIS SUBROUTINE:
    9207             :     // Make sure DFSFile is closed at exit time.  Do not rely on operating system to
    9208             :     // take care of it.
    9209             : 
    9210         796 :     state.files.dfs.close();
    9211         796 : }
    9212             : 
    9213          64 : void DayltgSetupAdjZoneListsAndPointers(EnergyPlusData &state)
    9214             : {
    9215             : 
    9216             :     // SUBROUTINE INFORMATION:
    9217             :     //       AUTHOR         Fred Winkelmann
    9218             :     //       DATE WRITTEN   Feb. 2004
    9219             :     //       MODIFIED:      June 2010;LKL - Merged two routines.
    9220             : 
    9221             :     // PURPOSE OF THIS SUBROUTINE:
    9222             :     // For each Daylighting:Detailed enclosure, creates a list of adjacent enclosures,
    9223             :     // that have one or more exterior windows and that share one or more interior
    9224             :     // windows with Z. Used in calculation of daylighting through interior windows.
    9225             : 
    9226             :     // Sets the daylighting factor pointers for each Daylighting:Detailed control. The pointer
    9227             :     // may be associated with an exterior window in a daylit target zone's enclosure or an exterior window in
    9228             :     // an adjacent enclosure, daylit or not, that shares interior windows with the target zone's enclosure.
    9229             : 
    9230             :     // Count number of exterior Windows (use to allocate arrays)
    9231          64 :     auto &dl = state.dataDayltg;
    9232             : 
    9233         785 :     for (int enclNum = 1; enclNum <= state.dataViewFactor->NumOfSolarEnclosures; ++enclNum) {
    9234         721 :         auto &thisEnclDaylight = dl->enclDaylight(enclNum);
    9235         721 :         thisEnclDaylight.TotalExtWindows = 0;
    9236             : 
    9237             :         // Count exterior windows in this solar enclosure
    9238        8165 :         for (int const surfNum : state.dataViewFactor->EnclSolInfo(enclNum).SurfacePtr) {
    9239        7444 :             auto const &surf = state.dataSurface->Surface(surfNum);
    9240        7444 :             if ((surf.Class == SurfaceClass::Window && surf.ExtBoundCond == ExternalEnvironment) ||
    9241        6266 :                 surf.OriginalClass == SurfaceClass::TDD_Diffuser) {
    9242        1178 :                 ++thisEnclDaylight.TotalExtWindows;
    9243             :             }
    9244             :         }
    9245             :     } // End of primary enclosure loop
    9246             : 
    9247         785 :     for (int enclNum = 1; enclNum <= state.dataViewFactor->NumOfSolarEnclosures; ++enclNum) {
    9248         721 :         int NumList = 0;
    9249         721 :         if (state.dataViewFactor->EnclSolInfo(enclNum).TotalEnclosureDaylRefPoints == 0) continue;
    9250         289 :         auto &thisEnclDaylight = dl->enclDaylight(enclNum);
    9251         289 :         if (!thisEnclDaylight.hasSplitFluxDaylighting) continue;
    9252             :         // This is a Daylighting:Detailed enclosure
    9253             :         // Find adjacent zones/enclosures
    9254        7241 :         for (int adjEnclNum = 1; adjEnclNum <= state.dataViewFactor->NumOfSolarEnclosures; ++adjEnclNum) {
    9255        6955 :             if (adjEnclNum == enclNum) continue;
    9256             :             // Require that adjEnclNum have a least one exterior window
    9257        6669 :             bool AdjEnclHasExtWins = false;
    9258       64772 :             for (int SurfNumAdj : state.dataViewFactor->EnclSolInfo(adjEnclNum).SurfacePtr) {
    9259       68254 :                 if ((state.dataSurface->Surface(SurfNumAdj).Class == SurfaceClass::Window) &&
    9260        5076 :                     (state.dataSurface->Surface(SurfNumAdj).ExtBoundCond == ExternalEnvironment)) {
    9261        5075 :                     AdjEnclHasExtWins = true;
    9262        5075 :                     break;
    9263             :                 }
    9264             :             }
    9265        6669 :             if (!AdjEnclHasExtWins) continue;
    9266             :             // Loop again through surfaces in ZoneNumAdj and see if any are interior windows adjacent to ZoneNum
    9267       64960 :             for (int SurfNumAdj : state.dataViewFactor->EnclSolInfo(adjEnclNum).SurfacePtr) {
    9268       59886 :                 auto const &surfAdj = state.dataSurface->Surface(SurfNumAdj);
    9269       59886 :                 if ((surfAdj.Class == SurfaceClass::Window) && (surfAdj.ExtBoundCond >= 1)) {
    9270             :                     // This is an interior window in ZoneNumAdj
    9271           1 :                     if (state.dataSurface->Surface(surfAdj.ExtBoundCond).SolarEnclIndex == enclNum) {
    9272             :                         // This interior window is adjacent to ZoneNum
    9273           1 :                         ++NumList;
    9274           1 :                         break;
    9275             :                     }
    9276             :                 }
    9277             :             }
    9278             :         }
    9279         286 :         thisEnclDaylight.AdjIntWinEnclNums.allocate(NumList);
    9280         286 :         thisEnclDaylight.AdjIntWinEnclNums = 0;
    9281             :     } // End of primary enclosure loop
    9282             : 
    9283         785 :     for (int enclNum = 1; enclNum <= state.dataViewFactor->NumOfSolarEnclosures; ++enclNum) {
    9284         721 :         int NumList = 0;
    9285         721 :         if (state.dataViewFactor->EnclSolInfo(enclNum).TotalEnclosureDaylRefPoints == 0) continue;
    9286         289 :         auto &thisEnclDaylight = dl->enclDaylight(enclNum);
    9287         289 :         if (!thisEnclDaylight.hasSplitFluxDaylighting) continue;
    9288             :         // This is a Daylighting:Detailed enclosure
    9289             :         // Find adjacent zones/enclosures
    9290        7241 :         for (int adjEnclNum = 1; adjEnclNum <= state.dataViewFactor->NumOfSolarEnclosures; ++adjEnclNum) {
    9291        6955 :             if (adjEnclNum == enclNum) continue;
    9292             :             // Require that adjEnclNum have a least one exterior window
    9293        6669 :             bool AdjEnclHasExtWins = false;
    9294       64772 :             for (int SurfNumAdj : state.dataViewFactor->EnclSolInfo(adjEnclNum).SurfacePtr) {
    9295       63178 :                 auto const &surfAdj = state.dataSurface->Surface(SurfNumAdj);
    9296       63178 :                 if (surfAdj.Class == SurfaceClass::Window && surfAdj.ExtBoundCond == ExternalEnvironment) {
    9297        5075 :                     AdjEnclHasExtWins = true;
    9298        5075 :                     break;
    9299             :                 }
    9300             :             }
    9301        6669 :             if (!AdjEnclHasExtWins) continue;
    9302             :             // Loop again through surfaces in ZoneNumAdj and see if any are interior windows adjacent to enclNum
    9303       64960 :             for (int SurfNumAdj : state.dataViewFactor->EnclSolInfo(adjEnclNum).SurfacePtr) {
    9304       59886 :                 auto const &surfAdj = state.dataSurface->Surface(SurfNumAdj);
    9305       59886 :                 if (surfAdj.Class != SurfaceClass::Window || surfAdj.ExtBoundCond < 1) continue;
    9306             : 
    9307             :                 // This is an interior window in adjEnclNum
    9308           1 :                 if (state.dataSurface->Surface(surfAdj.ExtBoundCond).SolarEnclIndex != enclNum) continue;
    9309             : 
    9310             :                 // This interior window is adjacent to ZoneNum
    9311           1 :                 ++NumList;
    9312           1 :                 int enclNumAdj = surfAdj.SolarEnclIndex;
    9313           1 :                 thisEnclDaylight.AdjIntWinEnclNums(NumList) = enclNumAdj;
    9314           1 :                 dl->enclDaylight(enclNumAdj).adjEnclHasDayltgCtrl = true;
    9315           1 :                 break;
    9316             :             }
    9317             :         }
    9318         286 :         thisEnclDaylight.NumOfIntWinAdjEncls = NumList;
    9319             :     } // End of primary enclosure loop
    9320             : 
    9321             :     // now fill out information on relationship between adjacent exterior windows and associated interior windows
    9322         785 :     for (int enclNum = 1; enclNum <= state.dataViewFactor->NumOfSolarEnclosures; ++enclNum) {
    9323         721 :         auto &enclDayl = dl->enclDaylight(enclNum);
    9324             :         // first find count of exterior windows
    9325         721 :         if (enclDayl.NumOfIntWinAdjEncls <= 0) {
    9326         720 :             enclDayl.NumOfIntWinAdjEnclExtWins = 0;
    9327         720 :             continue;
    9328             :         }
    9329           2 :         for (int adjEnclNum : enclDayl.AdjIntWinEnclNums) {
    9330           9 :             for (int SurfNumAdj : state.dataViewFactor->EnclSolInfo(adjEnclNum).SurfacePtr) {
    9331          10 :                 if ((state.dataSurface->Surface(SurfNumAdj).Class == SurfaceClass::Window) &&
    9332           2 :                     (state.dataSurface->Surface(SurfNumAdj).ExtBoundCond == ExternalEnvironment)) {
    9333           1 :                     ++enclDayl.NumOfIntWinAdjEnclExtWins;
    9334             :                 }
    9335             :             }
    9336             :         }
    9337             :         // now allocate nested struct based on exterior window count
    9338           1 :         enclDayl.IntWinAdjEnclExtWin.allocate(enclDayl.NumOfIntWinAdjEnclExtWins);
    9339             : 
    9340             :         // now fill nested structure
    9341           1 :         int ExtWinIndex = 0;
    9342           2 :         for (int adjEnclNum : enclDayl.AdjIntWinEnclNums) {
    9343           9 :             for (int SurfNumAdj : state.dataViewFactor->EnclSolInfo(adjEnclNum).SurfacePtr) {
    9344           8 :                 auto const &surfAdj = state.dataSurface->Surface(SurfNumAdj);
    9345           8 :                 if (surfAdj.Class != SurfaceClass::Window || surfAdj.ExtBoundCond != ExternalEnvironment) continue;
    9346             : 
    9347           1 :                 ++ExtWinIndex;
    9348           1 :                 auto &intWinAdjEnclExtWin = enclDayl.IntWinAdjEnclExtWin(ExtWinIndex);
    9349           1 :                 intWinAdjEnclExtWin.SurfNum = SurfNumAdj;
    9350             : 
    9351             :                 // now count interior windows shared by both zones
    9352           1 :                 int NumOfIntWindowsCount = 0;
    9353           9 :                 for (int SurfNumAdj2 : state.dataViewFactor->EnclSolInfo(adjEnclNum).SurfacePtr) {
    9354           8 :                     auto const &surfAdj2 = state.dataSurface->Surface(SurfNumAdj2);
    9355           8 :                     if ((surfAdj2.Class == SurfaceClass::Window) && (surfAdj2.ExtBoundCond >= 1)) {
    9356             :                         // This is an interior window in ZoneNumAdj
    9357           1 :                         if (state.dataSurface->Surface(surfAdj2.ExtBoundCond).SolarEnclIndex == enclNum) {
    9358             :                             // This interior window is adjacent to ZoneNum and associated with this
    9359           1 :                             ++NumOfIntWindowsCount;
    9360             :                         }
    9361             :                     }
    9362             :                 } // for (SurfNumAdj2)
    9363             : 
    9364             :                 // allocate nested array
    9365           1 :                 intWinAdjEnclExtWin.IntWinNum.allocate(NumOfIntWindowsCount);
    9366           1 :                 intWinAdjEnclExtWin.IntWinNum = 0;
    9367           1 :                 int IntWinIndex = 0;
    9368           9 :                 for (int SurfNumAdj2 : state.dataViewFactor->EnclSolInfo(adjEnclNum).SurfacePtr) {
    9369           8 :                     auto const &surfAdj2 = state.dataSurface->Surface(SurfNumAdj2);
    9370           8 :                     if (surfAdj2.Class != SurfaceClass::Window || surfAdj2.ExtBoundCond < 1) continue;
    9371             : 
    9372             :                     // This is an interior window in ZoneNumAdj
    9373           1 :                     if (state.dataSurface->Surface(surfAdj2.ExtBoundCond).SolarEnclIndex == enclNum) {
    9374             :                         // This interior window is adjacent to ZoneNum and associated with this
    9375           1 :                         intWinAdjEnclExtWin.IntWinNum(++IntWinIndex) = SurfNumAdj2;
    9376             :                     }
    9377             :                 } // for (SurfNumAdj2)
    9378             :             }     // for (SurfNumAdj)
    9379             :         }         // for (adjEnclNum)
    9380             :     }             // End of primary enclosure loop
    9381             : 
    9382          64 :     Array1D_int enclExtWin;
    9383          64 :     enclExtWin.dimension(state.dataViewFactor->NumOfSolarEnclosures, 0);
    9384             : 
    9385         785 :     for (int enclNum = 1; enclNum <= state.dataViewFactor->NumOfSolarEnclosures; ++enclNum) {
    9386         721 :         enclExtWin(enclNum) = 0;
    9387         721 :         if (state.dataViewFactor->EnclSolInfo(enclNum).TotalEnclosureDaylRefPoints == 0) continue;
    9388         289 :         auto const &thisEnclDaylight = dl->enclDaylight(enclNum);
    9389         289 :         if (!thisEnclDaylight.hasSplitFluxDaylighting) continue;
    9390             :         // This is a Daylighting:Detailed zone
    9391             : 
    9392             :         // Get exterior windows in this solar enclosure
    9393        3441 :         for (int const surfNum : state.dataViewFactor->EnclSolInfo(enclNum).SurfacePtr) {
    9394        3155 :             auto const &surf = state.dataSurface->Surface(surfNum);
    9395        3155 :             if ((surf.Class == SurfaceClass::Window && surf.ExtBoundCond == ExternalEnvironment) ||
    9396        2311 :                 surf.OriginalClass == SurfaceClass::TDD_Diffuser) {
    9397         844 :                 ++enclExtWin(enclNum);
    9398             :             }
    9399             :         }
    9400             : 
    9401             :         // Get exterior windows in adjacent enclosures that share interior windows with enclNum
    9402         286 :         if (thisEnclDaylight.NumOfIntWinAdjEncls == 0) continue;
    9403             : 
    9404           2 :         for (int adjEnclNum : thisEnclDaylight.AdjIntWinEnclNums) {
    9405             :             // Get exterior windows in EnclNumAdj -- there must be at least one, otherwise
    9406             :             // it would not be an "AdjIntWinEncl"
    9407           9 :             for (int SurfNumAdj : state.dataViewFactor->EnclSolInfo(adjEnclNum).SurfacePtr) {
    9408           8 :                 auto const &surfAdj = state.dataSurface->Surface(SurfNumAdj);
    9409           8 :                 if ((surfAdj.Class == SurfaceClass::Window && surfAdj.ExtBoundCond == ExternalEnvironment) ||
    9410           7 :                     surfAdj.OriginalClass == SurfaceClass::TDD_Diffuser) {
    9411           1 :                     ++enclExtWin(enclNum);
    9412             :                 }
    9413             :             }
    9414             :         } // for (adjEnclNum)
    9415             :     }     // for (enclNum)
    9416             : 
    9417          64 :     dl->maxShadeDeployOrderExtWins = 0;
    9418         785 :     for (int enclNum = 1; enclNum <= state.dataViewFactor->NumOfSolarEnclosures; ++enclNum) {
    9419         721 :         auto &thisEnclDaylight = dl->enclDaylight(enclNum);
    9420         721 :         if (!thisEnclDaylight.hasSplitFluxDaylighting) continue;
    9421         286 :         thisEnclDaylight.NumOfDayltgExtWins = 0;
    9422         286 :         int thisEnclNumRefPoints = state.dataViewFactor->EnclSolInfo(enclNum).TotalEnclosureDaylRefPoints;
    9423         286 :         if (thisEnclNumRefPoints > 0) {
    9424             :             // This is a Daylighting:Detailed enclosure
    9425             : 
    9426             :             // Get exterior windows in this enclosure
    9427         286 :             if (enclExtWin(enclNum) == 0) continue;
    9428         286 :             thisEnclDaylight.DayltgExtWinSurfNums.allocate(enclExtWin(enclNum));
    9429         286 :             thisEnclDaylight.DayltgExtWinSurfNums = 0;
    9430         573 :             for (int controlNum : thisEnclDaylight.daylightControlIndexes) {
    9431         287 :                 auto &thisDayltgCtrl = dl->daylightControl(controlNum);
    9432         287 :                 thisDayltgCtrl.MapShdOrdToLoopNum.allocate(enclExtWin(enclNum));
    9433         287 :                 thisDayltgCtrl.MapShdOrdToLoopNum = 0;
    9434             : 
    9435         287 :                 assert((int)thisDayltgCtrl.refPts.size() == thisDayltgCtrl.TotalDaylRefPoints);
    9436         750 :                 for (auto &refPt : thisDayltgCtrl.refPts) {
    9437         463 :                     refPt.extWins.allocate(enclExtWin(enclNum));
    9438        1952 :                     for (auto &extWin : refPt.extWins) {
    9439        1489 :                         new (&extWin) DaylRefPtExtWin();
    9440             :                     }
    9441             :                 }
    9442         286 :             }
    9443             : 
    9444         286 :             int enclExtWinCtr = 0;
    9445             : 
    9446        3441 :             for (int const surfNum : state.dataViewFactor->EnclSolInfo(enclNum).SurfacePtr) {
    9447        3155 :                 auto const &surf = state.dataSurface->Surface(surfNum);
    9448        3155 :                 if ((surf.Class == SurfaceClass::Window && surf.ExtBoundCond == ExternalEnvironment) ||
    9449        2311 :                     surf.OriginalClass == SurfaceClass::TDD_Diffuser) {
    9450         844 :                     ++enclExtWinCtr;
    9451         844 :                     thisEnclDaylight.DayltgExtWinSurfNums(enclExtWinCtr) = surfNum;
    9452             :                 }
    9453             :             }
    9454             : 
    9455             :             // Get exterior windows in adjacent enclosures that share interior windows with enclNum
    9456         286 :             if (thisEnclDaylight.NumOfIntWinAdjEncls > 0) {
    9457           2 :                 for (int adjEnclNum : thisEnclDaylight.AdjIntWinEnclNums) {
    9458             :                     // Get exterior windows in EnclNumAdj -- there must be at least one, otherwise
    9459             :                     // it would not be an "AdjIntWinEncl"
    9460           9 :                     for (int SurfNumAdj : state.dataViewFactor->EnclSolInfo(adjEnclNum).SurfacePtr) {
    9461           8 :                         auto &surfAdj = state.dataSurface->Surface(SurfNumAdj);
    9462           8 :                         if ((surfAdj.Class == SurfaceClass::Window && surfAdj.ExtBoundCond == ExternalEnvironment) ||
    9463           7 :                             surfAdj.OriginalClass == SurfaceClass::TDD_Diffuser) {
    9464           1 :                             ++enclExtWinCtr;
    9465           1 :                             thisEnclDaylight.DayltgExtWinSurfNums(enclExtWinCtr) = SurfNumAdj;
    9466             : 
    9467           1 :                             auto &surfWinAdj = state.dataSurface->SurfaceWindow(SurfNumAdj);
    9468             :                             // If no daylighting in the adjacent enclosure, set up variables anyway:
    9469           2 :                             if (state.dataViewFactor->EnclSolInfo(adjEnclNum).TotalEnclosureDaylRefPoints == 0 &&
    9470           1 :                                 !state.dataSurface->SurfWinSurfDayLightInit(SurfNumAdj)) {
    9471           1 :                                 surfWinAdj.refPts.allocate(thisEnclNumRefPoints);
    9472           2 :                                 for (auto &refPt : surfWinAdj.refPts) {
    9473           1 :                                     new (&refPt) SurfaceWindowRefPt();
    9474             :                                 }
    9475           1 :                                 state.dataSurface->SurfWinSurfDayLightInit(SurfNumAdj) = true;
    9476             :                             }
    9477             :                         }
    9478             :                     } // for (SurfNumAdj)
    9479             :                 }     // for (adjEnclNum)
    9480             :             }         // if (thisEnclDaylight.NumOfIntWinAdjEncls > 0)
    9481             : 
    9482         286 :             thisEnclDaylight.NumOfDayltgExtWins = enclExtWin(enclNum);
    9483         286 :             int winSize = enclExtWin(enclNum);
    9484         286 :             int numSlatAngs = state.dataSurface->actualMaxSlatAngs + 1;
    9485         573 :             for (int controlNum : thisEnclDaylight.daylightControlIndexes) {
    9486         287 :                 auto &thisDayltgCtrl = dl->daylightControl(controlNum);
    9487         287 :                 int refSize = thisDayltgCtrl.TotalDaylRefPoints;
    9488        7175 :                 for (int iHr = 1; iHr <= (int)Constant::HoursInDay; ++iHr) {
    9489        6888 :                     thisDayltgCtrl.daylFac[iHr].allocate(winSize, refSize, numSlatAngs);
    9490             :                 }
    9491         286 :             }
    9492             :         } // if (thisEncl.NumOfRefPoints > 0)
    9493             : 
    9494         286 :         if (state.dataSurface->TotWinShadingControl > 0) {
    9495          22 :             CreateShadeDeploymentOrder(state, enclNum);
    9496             :         }
    9497             :     } // for (enclNum)
    9498             : 
    9499          64 :     int numSlatAngs = state.dataSurface->actualMaxSlatAngs + 1;
    9500         785 :     for (int enclNum = 1; enclNum <= state.dataViewFactor->NumOfSolarEnclosures; ++enclNum) {
    9501         721 :         auto const &thisEnclDaylight = dl->enclDaylight(enclNum);
    9502         721 :         if (!thisEnclDaylight.hasSplitFluxDaylighting) continue;
    9503         286 :         int thisEnclNumRefPoints = state.dataViewFactor->EnclSolInfo(enclNum).TotalEnclosureDaylRefPoints;
    9504         286 :         if (thisEnclNumRefPoints > 0) {
    9505         286 :             if (state.dataSurface->TotWinShadingControl > 0) {
    9506          22 :                 MapShadeDeploymentOrderToLoopNumber(state, enclNum);
    9507             :             }
    9508             :         }
    9509             :     }
    9510             : 
    9511          73 :     for (auto &illumMap : dl->illumMaps) {
    9512           9 :         assert((int)illumMap.refPts.size() == illumMap.TotalMapRefPoints);
    9513           9 :         if (illumMap.TotalMapRefPoints == 0) continue;
    9514             : 
    9515           9 :         int numExtWin = enclExtWin(illumMap.enclIndex);
    9516           9 :         if (numExtWin == 0) continue;
    9517             : 
    9518         634 :         for (auto &refPt : illumMap.refPts) {
    9519         625 :             refPt.winLums.allocate(numExtWin);
    9520        2475 :             for (auto &winLums : refPt.winLums) {
    9521        1850 :                 winLums = {0.0, 0.0};
    9522             :             }
    9523             :         }
    9524             : 
    9525         225 :         for (int iHr = 1; iHr <= (int)Constant::HoursInDay; ++iHr) {
    9526         216 :             illumMap.daylFac[iHr].allocate(numExtWin, illumMap.TotalMapRefPoints, numSlatAngs);
    9527             :         }
    9528             : 
    9529             :     } // End of map loop
    9530             : 
    9531         256 :     dl->dirIllum.dimension(Constant::HoursInDay, numSlatAngs, Illums());
    9532         256 :     dl->reflIllum.dimension(Constant::HoursInDay, numSlatAngs, Illums());
    9533         256 :     dl->winLum.dimension(Constant::HoursInDay, numSlatAngs, Illums());
    9534         256 :     dl->avgWinLum.dimension(Constant::HoursInDay, numSlatAngs, Illums());
    9535             : 
    9536             :     static constexpr std::string_view Format_700("! <Enclosure/Window Adjacency Daylighting Counts>, Enclosure Name, Number of Exterior Windows, "
    9537             :                                                  "Number of Exterior Windows in Adjacent Enclosures\n");
    9538          64 :     print(state.files.eio, Format_700);
    9539         785 :     for (int enclNum = 1; enclNum <= state.dataViewFactor->NumOfSolarEnclosures; ++enclNum) {
    9540         721 :         auto &thisEnclDaylight = dl->enclDaylight(enclNum);
    9541         721 :         if (!thisEnclDaylight.hasSplitFluxDaylighting) continue;
    9542         286 :         if (state.dataViewFactor->EnclSolInfo(enclNum).TotalEnclosureDaylRefPoints == 0) continue;
    9543             :         static constexpr std::string_view Format_701("Enclosure/Window Adjacency Daylighting Counts, {},{},{}\n");
    9544         286 :         print(state.files.eio,
    9545             :               Format_701,
    9546         286 :               state.dataViewFactor->EnclSolInfo(enclNum).Name,
    9547         286 :               thisEnclDaylight.TotalExtWindows,
    9548         572 :               (thisEnclDaylight.NumOfDayltgExtWins - thisEnclDaylight.TotalExtWindows));
    9549             :     }
    9550             :     static constexpr std::string_view Format_702(
    9551             :         "! <Enclosure/Window Adjacency Daylighting Matrix>, Enclosure Name, Number of Adjacent Enclosures with Windows,Adjacent "
    9552             :         "Enclosure Names - 1st 100 (max)\n");
    9553          64 :     print(state.files.eio, Format_702);
    9554         785 :     for (int enclNum = 1; enclNum <= state.dataViewFactor->NumOfSolarEnclosures; ++enclNum) {
    9555         721 :         auto &thisEnclDaylight = dl->enclDaylight(enclNum);
    9556         721 :         if (!thisEnclDaylight.hasSplitFluxDaylighting) continue;
    9557         286 :         if (state.dataViewFactor->EnclSolInfo(enclNum).TotalEnclosureDaylRefPoints == 0) continue;
    9558             :         static constexpr std::string_view Format_703("Enclosure/Window Adjacency Daylighting Matrix, {},{}");
    9559         286 :         print(state.files.eio, Format_703, state.dataViewFactor->EnclSolInfo(enclNum).Name, thisEnclDaylight.NumOfIntWinAdjEncls);
    9560         287 :         for (int loop = 1, loop_end = min(thisEnclDaylight.NumOfIntWinAdjEncls, 100); loop <= loop_end; ++loop) {
    9561           1 :             print(state.files.eio, ",{}", state.dataViewFactor->EnclSolInfo(thisEnclDaylight.AdjIntWinEnclNums(loop)).Name);
    9562             :         }
    9563         286 :         print(state.files.eio, "\n");
    9564             :     }
    9565             : 
    9566          64 :     enclExtWin.deallocate();
    9567          64 : }
    9568             : 
    9569          22 : void CreateShadeDeploymentOrder(EnergyPlusData &state, int const enclNum)
    9570             : {
    9571             :     // J. Glazer - 2018
    9572             :     // create sorted list for shade deployment order
    9573             :     // first step is to create a sortable list of WindowShadingControl objects by sequence
    9574          22 :     auto &dl = state.dataDayltg;
    9575             : 
    9576          22 :     std::vector<std::pair<int, int>> shadeControlSequence; // sequence, WindowShadingControl
    9577          55 :     for (int iShadeCtrl = 1; iShadeCtrl <= state.dataSurface->TotWinShadingControl; ++iShadeCtrl) {
    9578          33 :         auto &winShadeControl = state.dataSurface->WindowShadingControl(iShadeCtrl);
    9579          41 :         for (int spaceNum : state.dataHeatBal->Zone(winShadeControl.ZoneIndex).spaceIndexes) {
    9580          33 :             int shadeCtrlEnclNum = state.dataHeatBal->space(spaceNum).solarEnclosureNum;
    9581          33 :             if (shadeCtrlEnclNum == enclNum) {
    9582          25 :                 shadeControlSequence.push_back(std::make_pair(winShadeControl.SequenceNumber, iShadeCtrl));
    9583          25 :                 break;
    9584             :             }
    9585          33 :         }
    9586             :     }
    9587             :     // sort the WindowShadingControl objects based on sequence number
    9588          22 :     sort(shadeControlSequence.begin(), shadeControlSequence.end());
    9589             :     // now make the deployment list of lists.
    9590             :     // each sublist is a group of surfaces that should be deployed together
    9591             :     // often the sublist is just a single item.
    9592          22 :     dl->maxShadeDeployOrderExtWins = 0;
    9593          44 :     for (int controlNum : dl->enclDaylight(enclNum).daylightControlIndexes) {
    9594          22 :         auto &thisDaylightCtrl = dl->daylightControl(controlNum);
    9595          47 :         for (auto sequence : shadeControlSequence) { // This is an iterator (THIS_AUTO_OK)
    9596          25 :             int curShadeControlNum = sequence.second;
    9597          25 :             auto const &winShadeControl = state.dataSurface->WindowShadingControl(curShadeControlNum);
    9598          25 :             if (winShadeControl.multiSurfaceControl == MultiSurfaceControl::Group) {
    9599             :                 // add a group of surfaces since they should be deployed as a group
    9600           4 :                 std::vector<int> group;
    9601          13 :                 for (int i = 1; i <= winShadeControl.FenestrationCount; i++) {
    9602           9 :                     group.push_back(winShadeControl.FenestrationIndex(i));
    9603             :                 }
    9604           4 :                 thisDaylightCtrl.ShadeDeployOrderExtWins.push_back(group);
    9605           4 :             } else {
    9606             :                 // add each individual surface as a separate list so they are deployed individually
    9607          46 :                 for (int i = 1; i <= winShadeControl.FenestrationCount; i++) {
    9608          25 :                     std::vector<int> singleMemberVector;
    9609          25 :                     singleMemberVector.push_back(winShadeControl.FenestrationIndex(i));
    9610          25 :                     thisDaylightCtrl.ShadeDeployOrderExtWins.push_back(singleMemberVector);
    9611          25 :                 }
    9612             :             }
    9613          22 :         }
    9614          22 :         dl->maxShadeDeployOrderExtWins = max(dl->maxShadeDeployOrderExtWins, (int)thisDaylightCtrl.ShadeDeployOrderExtWins.size());
    9615          22 :     }
    9616             : 
    9617          22 :     dl->maxDayltgExtWins = 0;
    9618         117 :     for (auto const &enclDayl : dl->enclDaylight) {
    9619          95 :         dl->maxDayltgExtWins = max(dl->maxDayltgExtWins, enclDayl.NumOfDayltgExtWins);
    9620             :     }
    9621             : 
    9622          22 : } // CreateShadeDeploymentOrder()
    9623             : 
    9624          22 : void MapShadeDeploymentOrderToLoopNumber(EnergyPlusData &state, int const enclNum)
    9625             : {
    9626             :     // J. Glazer - 2018
    9627             :     // Allow a way to map back to the original "loop" index that is used in many other places in the
    9628             :     // ZoneDayLight data structure when traversing the list in the order of the window shaded deployment
    9629          22 :     auto &dl = state.dataDayltg;
    9630             : 
    9631          22 :     auto const &thisEnclDaylight = dl->enclDaylight(enclNum);
    9632          22 :     auto const &thisEnclSol = state.dataViewFactor->EnclSolInfo(enclNum);
    9633             : 
    9634          22 :     if (thisEnclSol.TotalEnclosureDaylRefPoints == 0 || thisEnclDaylight.NumOfDayltgExtWins == 0) return;
    9635             : 
    9636          44 :     for (int controlNum : thisEnclDaylight.daylightControlIndexes) {
    9637          22 :         auto &thisDaylightCtrl = dl->daylightControl(controlNum);
    9638          22 :         if (thisDaylightCtrl.ShadeDeployOrderExtWins.size() == 0) continue;
    9639             : 
    9640          21 :         int count = 0;
    9641          21 :         bool showOnce = true;
    9642          50 :         for (auto const &listOfExtWin : thisDaylightCtrl.ShadeDeployOrderExtWins) {
    9643          63 :             for (int IWinShdOrd : listOfExtWin) {
    9644          34 :                 ++count;
    9645          34 :                 if (count > thisEnclDaylight.NumOfDayltgExtWins) {
    9646           0 :                     if (showOnce) {
    9647           0 :                         ShowWarningError(
    9648             :                             state,
    9649           0 :                             format("MapShadeDeploymentOrderToLoopNumber: too many controlled shaded windows in enclosure {}", thisEnclSol.Name));
    9650           0 :                         ShowContinueError(state,
    9651             :                                           "Check the Zone Name in the WindowShadingControl that references the following fenestration surfaces:");
    9652           0 :                         showOnce = false;
    9653             :                     }
    9654           0 :                     ShowContinueError(state, format("  -  {}", state.dataSurface->Surface(IWinShdOrd).Name));
    9655             :                 }
    9656          86 :                 for (int loop = 1; loop <= thisEnclDaylight.NumOfDayltgExtWins; ++loop) {
    9657          86 :                     int IWinLoop = thisEnclDaylight.DayltgExtWinSurfNums(loop);
    9658          86 :                     if (IWinShdOrd == IWinLoop) {
    9659          34 :                         thisDaylightCtrl.MapShdOrdToLoopNum(count) = loop;
    9660          34 :                         break;
    9661             :                     }
    9662             :                 }
    9663          29 :             }
    9664          21 :         } // for (listOfExtWin)
    9665          22 :     }     // for (controlNum)
    9666             : } // MapShadeDeploymentOrderToLoopNumber()
    9667             : 
    9668        1015 : void DayltgInterReflIllFrIntWins(EnergyPlusData &state, int const enclNum)
    9669             : {
    9670             : 
    9671             :     // SUBROUTINE INFORMATION:
    9672             :     //       AUTHOR         Fred Winkelmann
    9673             :     //       DATE WRITTEN   Mar. 2004
    9674             : 
    9675             :     // PURPOSE OF THIS SUBROUTINE:
    9676             :     // Calculates the inter-reflected illuminance in a daylit zone from beam
    9677             :     // and diffuse daylight entering the zone through interior windows. This illuminance
    9678             :     // is determined by the split-flux method and is assumed to be uniform, i.e., the same
    9679             :     // at all reference points.
    9680             : 
    9681        1015 :     auto &dl = state.dataDayltg;
    9682             : 
    9683        1015 :     auto &enclDayl = dl->enclDaylight(enclNum);
    9684        1015 :     auto &enclSol = state.dataViewFactor->EnclSolInfo(enclNum);
    9685             : 
    9686        1015 :     enclDayl.InterReflIllFrIntWins = 0.0;
    9687             : 
    9688        8120 :     for (int const IWin : enclSol.SurfacePtr) {
    9689        7105 :         auto &surf = state.dataSurface->Surface(IWin);
    9690        7105 :         if (surf.Class != SurfaceClass::Window || surf.ExtBoundCond < 1) continue;
    9691        1015 :         auto const &surfWin = state.dataSurface->SurfaceWindow(IWin);
    9692             :         // This is an interior window in ZoneNum
    9693        1015 :         int const ConstrNum = surf.Construction;
    9694        1015 :         int const adjEnclNum = state.dataSurface->Surface(surf.ExtBoundCond).SolarEnclIndex;
    9695             :         // Luminous flux transmitted through an int win from adjacent zone's enclosure (lumens)
    9696        1015 :         Real64 QDifTrans = state.dataHeatBal->EnclSolQSDifSol(adjEnclNum) * state.dataConstruction->Construct(ConstrNum).TransDiffVis * surf.Area *
    9697        1015 :                            state.dataEnvrn->PDIFLW;
    9698        1015 :         Real64 QDifTransUp = QDifTrans * surfWin.fractionUpgoing;         // Upgoing part of QDifTrans (lumens)
    9699        1015 :         Real64 QDifTransDn = QDifTrans * (1.0 - surfWin.fractionUpgoing); // Downgoing part of QDifTrans (lumens)
    9700        1015 :         if (enclDayl.totInsSurfArea * (1.0 - enclDayl.aveVisDiffReflect) != 0.0) {
    9701        1015 :             enclDayl.InterReflIllFrIntWins += (QDifTransDn * surfWin.rhoFloorWall + QDifTransUp * surfWin.rhoCeilingWall) /
    9702        1015 :                                               (enclDayl.totInsSurfArea * (1.0 - enclDayl.aveVisDiffReflect));
    9703             :         }
    9704             :     } // for (iWin)
    9705             : 
    9706             :     // Add inter-reflected illuminance from beam solar entering enclosure through interior windows
    9707             :     // TH, CR 7873, 9/17/2009
    9708        1015 :     if (dl->enclDaylight(enclNum).totInsSurfArea > 0) {
    9709        1015 :         enclDayl.InterReflIllFrIntWins +=
    9710        1015 :             (state.dataHeatBal->EnclSolDBIntWin(enclNum) * state.dataEnvrn->BeamSolarRad * state.dataEnvrn->PDIRLW * enclDayl.floorVisRefl) /
    9711        1015 :             (enclDayl.totInsSurfArea * (1.0 - enclDayl.aveVisDiffReflect));
    9712             :     }
    9713        1015 : } // DayltgInterReflIllFrIntWins()
    9714             : 
    9715          64 : void CalcMinIntWinSolidAngs(EnergyPlusData &state)
    9716             : {
    9717             : 
    9718             :     // SUBROUTINE INFORMATION:
    9719             :     //       AUTHOR         Fred Winkelmann
    9720             :     //       DATE WRITTEN   Feb. 2004
    9721             : 
    9722             :     // PURPOSE OF THIS SUBROUTINE:
    9723             :     // For each Daylighting:Detailed zone finds the minimum solid angle subtended
    9724             :     // by interior windows through which daylight can pass from adjacent zones with
    9725             :     // exterior windows.
    9726             : 
    9727          64 :     auto &dl = state.dataDayltg;
    9728             : 
    9729         785 :     for (int enclNum = 1; enclNum <= state.dataViewFactor->NumOfSolarEnclosures; ++enclNum) {
    9730         721 :         auto &thisEnclDaylight = dl->enclDaylight(enclNum);
    9731         721 :         thisEnclDaylight.MinIntWinSolidAng = 2.0 * Constant::Pi;
    9732         721 :         if (state.dataViewFactor->EnclSolInfo(enclNum).TotalEnclosureDaylRefPoints == 0) continue;
    9733         289 :         if (thisEnclDaylight.NumOfIntWinAdjEncls == 0) continue;
    9734             : 
    9735           8 :         for (int IWin : state.dataViewFactor->EnclSolInfo(enclNum).SurfacePtr) {
    9736           7 :             auto const &surf = state.dataSurface->Surface(IWin);
    9737             : 
    9738           7 :             if ((surf.Class != SurfaceClass::Window) || (surf.ExtBoundCond < 1)) continue;
    9739             : 
    9740             :             // This is an interior window in enclNum
    9741           1 :             int const winAdjEnclNum = state.dataSurface->Surface(surf.ExtBoundCond).SolarEnclIndex;
    9742           1 :             bool IntWinNextToIntWinAdjZone = false; // True if an interior window is next to a zone with one or more exterior windows
    9743           1 :             for (int adjEnclNum : thisEnclDaylight.AdjIntWinEnclNums) {
    9744           1 :                 if (winAdjEnclNum == adjEnclNum) {
    9745           1 :                     IntWinNextToIntWinAdjZone = true;
    9746           1 :                     break;
    9747             :                 }
    9748             :             }
    9749             : 
    9750           1 :             if (!IntWinNextToIntWinAdjZone) continue;
    9751             : 
    9752           2 :             for (int controlNum : thisEnclDaylight.daylightControlIndexes) {
    9753           1 :                 auto &thisDayltgCtrl = dl->daylightControl(controlNum);
    9754           2 :                 for (int IL = 1; IL <= thisDayltgCtrl.TotalDaylRefPoints; ++IL) {
    9755             :                     // Reference point in absolute coordinate system
    9756           1 :                     Vector3<Real64> RREF = thisDayltgCtrl.refPts(IL).absCoords;
    9757           1 :                     bool is_Triangle = (surf.Sides == 3);
    9758           1 :                     bool is_Rectangle = (surf.Sides == 4);
    9759             : 
    9760           1 :                     Vector3<Real64> W1, W2, W3;
    9761           1 :                     if (is_Rectangle) {
    9762             :                         // Vertices of window numbered counter-clockwise starting at upper left as viewed
    9763             :                         // from inside of room. Assumes original vertices are numbered counter-clockwise from
    9764             :                         // upper left as viewed from outside.
    9765           1 :                         W3 = surf.Vertex(2);
    9766           1 :                         W2 = surf.Vertex(3);
    9767           1 :                         W1 = surf.Vertex(4);
    9768           0 :                     } else if (is_Triangle) {
    9769           0 :                         W3 = surf.Vertex(2);
    9770           0 :                         W2 = surf.Vertex(3);
    9771           0 :                         W1 = surf.Vertex(1);
    9772             :                     }
    9773             :                     // Unit vectors from window vertex 2 to 1 and 2 to 3, center point of window,
    9774             :                     // and vector from ref pt to center of window
    9775           1 :                     Vector3<Real64> W21 = W1 - W2;
    9776           1 :                     Vector3<Real64> W23 = W3 - W2;
    9777           1 :                     Real64 HW = W21.magnitude();
    9778           1 :                     Real64 WW = W23.magnitude();
    9779             :                     Vector3<Real64> WC =
    9780           3 :                         (is_Rectangle) ? (W2 + (W23 + W21) / 2.0) : (is_Triangle ? (W2 + (W23 + W21) / 3.0) : (W2 + (W23 + W21) / 3.0));
    9781             : 
    9782             :                     // Vector from ref point to center of window
    9783           1 :                     Vector3<Real64> REFWC = WC - RREF;
    9784           1 :                     W21 /= HW;
    9785           1 :                     W23 /= WW;
    9786             :                     // Unit vector normal to window (pointing away from room)
    9787           1 :                     Vector3<Real64> WNORM = surf.OutNormVec;
    9788             :                     // Distance from ref point to center of window
    9789           1 :                     Real64 DIS = REFWC.magnitude();
    9790             :                     // Unit vector from ref point to center of window
    9791           1 :                     Vector3<Real64> Ray = REFWC / DIS;
    9792             :                     // Cosine of angle between ray from ref pt to center of window and window outward normal
    9793           1 :                     Real64 COSB = dot(WNORM, Ray);
    9794           1 :                     if (COSB > 0.01765) { // 0 <= B < 89 deg
    9795             :                         // Above test avoids case where ref point cannot receive daylight directly from the
    9796             :                         // interior window
    9797           1 :                         Real64 IntWinSolidAng = COSB * surf.Area / (pow_2(DIS) + 0.001);
    9798           1 :                         thisEnclDaylight.MinIntWinSolidAng = min(thisEnclDaylight.MinIntWinSolidAng, IntWinSolidAng);
    9799             :                     }
    9800           1 :                 } // for (IL)
    9801           1 :             }     // for (controlNum)
    9802             :         }         // for (IWin)
    9803             :     }             // for (enclNum)
    9804          64 : }
    9805             : 
    9806         128 : void CheckForGeometricTransform(EnergyPlusData &state, bool &doTransform, Real64 &OldAspectRatio, Real64 &NewAspectRatio)
    9807             : {
    9808             : 
    9809             :     // SUBROUTINE INFORMATION:
    9810             :     //       AUTHOR         Linda Lawrie
    9811             :     //       DATE WRITTEN   February 2009
    9812             : 
    9813             :     // PURPOSE OF THIS SUBROUTINE:
    9814             :     // check for geometrytransform in the daylighting access for reference and map points
    9815             : 
    9816             :     // METHODOLOGY EMPLOYED:
    9817             :     // once reference points  have been converted to WCS,
    9818             :     //  change them to reflect a different aspect
    9819             :     // ratio for the entire building based on user input.
    9820             : 
    9821             :     // SUBROUTINE PARAMETER DEFINITIONS:
    9822             :     static constexpr std::string_view CurrentModuleObject = "GeometryTransform";
    9823             : 
    9824             :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    9825         128 :     Array1D_string cAlphas(1);
    9826         128 :     Array1D<Real64> rNumerics;
    9827             : 
    9828             :     // begin execution
    9829             :     // get user input...
    9830         128 :     doTransform = false;
    9831         128 :     OldAspectRatio = 1.0;
    9832         128 :     NewAspectRatio = 1.0;
    9833             : 
    9834         128 :     auto &ip = state.dataInputProcessing->inputProcessor;
    9835         128 :     auto const &ipsc = state.dataIPShortCut;
    9836         128 :     if (ip->getNumObjectsFound(state, CurrentModuleObject) == 1) {
    9837             :         int NAlphas;
    9838             :         int NNum;
    9839             :         int IOStat;
    9840           0 :         ip->getObjectItem(state,
    9841             :                           CurrentModuleObject,
    9842             :                           1,
    9843             :                           cAlphas,
    9844             :                           NAlphas,
    9845             :                           rNumerics,
    9846             :                           NNum,
    9847             :                           IOStat,
    9848           0 :                           ipsc->lNumericFieldBlanks,
    9849           0 :                           ipsc->lAlphaFieldBlanks,
    9850           0 :                           ipsc->cAlphaFieldNames,
    9851           0 :                           ipsc->cNumericFieldNames);
    9852           0 :         OldAspectRatio = rNumerics(1);
    9853           0 :         NewAspectRatio = rNumerics(2);
    9854           0 :         std::string transformPlane = cAlphas(1);
    9855           0 :         if (transformPlane != "XY") {
    9856           0 :             ShowWarningError(state, format("{}: invalid {}=\"{}...ignored.", CurrentModuleObject, ipsc->cAlphaFieldNames(1), cAlphas(1)));
    9857             :         }
    9858           0 :         doTransform = true;
    9859           0 :         state.dataSurface->AspectTransform = true;
    9860           0 :     }
    9861         128 :     if (state.dataSurface->WorldCoordSystem) {
    9862          56 :         doTransform = false;
    9863          56 :         state.dataSurface->AspectTransform = false;
    9864             :     }
    9865         128 : }
    9866             : 
    9867          22 : void WriteDaylightMapTitle(EnergyPlusData &state,
    9868             :                            int const mapNum,
    9869             :                            InputOutputFile &mapFile,
    9870             :                            std::string const &mapName,
    9871             :                            std::string const &environmentName,
    9872             :                            int const ZoneNum,
    9873             :                            std::string const &refPts,
    9874             :                            Real64 const zcoord)
    9875             : {
    9876             :     // SUBROUTINE INFORMATION:
    9877             :     //       AUTHOR         Greg Stark
    9878             :     //       DATE WRITTEN   Sept 2008
    9879             : 
    9880             :     // PURPOSE OF THIS SUBROUTINE:
    9881             :     // The purpose of the routine is to allow the daylighting map data to be written in various formats
    9882             : 
    9883             :     // must add correct number of commas at end
    9884          22 :     auto &dl = state.dataDayltg;
    9885             : 
    9886          22 :     std::string fullmapName = fmt::format("{}:{}:{} Illuminance [lux] (Hourly)", state.dataHeatBal->Zone(ZoneNum).Name, environmentName, mapName);
    9887          22 :     print(mapFile, "Date/Time{}{}{}{}{}{}\n", dl->MapColSep, fullmapName, dl->MapColSep, refPts, dl->MapColSep, dl->MapColSep);
    9888             : 
    9889          22 :     if (state.dataSQLiteProcedures->sqlite) {
    9890           1 :         state.dataSQLiteProcedures->sqlite->createSQLiteDaylightMapTitle(mapNum, fullmapName, environmentName, ZoneNum, refPts, zcoord);
    9891             :     }
    9892          22 : } // WritDaylightMapTitle()
    9893             : 
    9894             : } // namespace EnergyPlus::Dayltg

Generated by: LCOV version 1.14